nav: fixing dimming and inter nav clicks

This commit is contained in:
Hunter Miller 2021-09-14 17:52:12 -05:00
parent f8bfbf1bbb
commit 65b9f229c5
7 changed files with 224 additions and 231 deletions

View File

@ -22,7 +22,7 @@ const variants: Record<ButtonVariant, string> = {
secondary: 'text-black bg-gray-100',
caution: 'text-white bg-orange-400',
destructive: 'text-white bg-red-500',
'alt-primary': 'text-white bg-blue-400',
'alt-primary': 'text-white bg-blue-400 ring-blue-300',
'alt-secondary': 'text-blue-400 bg-blue-50'
};

View File

@ -35,9 +35,11 @@ type LeapProps = {
menu: MenuState;
dropdown: string;
navOpen: boolean;
shouldDim: boolean;
} & HTMLAttributes<HTMLDivElement>;
export const Leap = React.forwardRef(({ menu, dropdown, navOpen, className }: LeapProps, ref) => {
export const Leap = React.forwardRef(
({ menu, dropdown, navOpen, shouldDim, className }: LeapProps, ref) => {
const { push } = useHistory();
const match = useRouteMatch<{ menu?: MenuState; query?: string; desk?: string }>(
`/leap/${menu}/:query?/(apps)?/:desk?`
@ -200,7 +202,7 @@ export const Leap = React.forwardRef(({ menu, dropdown, navOpen, className }: Le
<form
className={classNames(
'flex items-center h-full w-full px-2 rounded-full bg-white default-ring focus-within:ring-2',
navOpen && menu !== 'search' && 'opacity-60',
shouldDim && 'opacity-60',
!navOpen ? 'bg-gray-50' : '',
className
)}
@ -243,4 +245,5 @@ export const Leap = React.forwardRef(({ menu, dropdown, navOpen, className }: Le
)}
</div>
);
});
}
);

View File

@ -52,33 +52,6 @@ export type MenuState =
| 'help-and-support'
| 'system-preferences';
export function createNextPath(current: string, nextPart?: string): string {
let end = nextPart;
const parts = current.split('/').reverse();
if (parts[1] === 'search') {
end = 'apps';
}
if (parts[0] === 'leap') {
end = `search/${nextPart}`;
}
return `${current}/${end}`;
}
export function createPreviousPath(current: string): string {
const parts = current.split('/');
parts.pop();
if (parts[parts.length - 1] === 'leap') {
parts.push('search');
}
if (parts[parts.length - 2] === 'apps') {
parts.pop();
}
return parts.join('/');
}
interface NavProps {
menu?: MenuState;
}
@ -123,6 +96,15 @@ export const Nav: FunctionComponent<NavProps> = ({ menu }) => {
}
}, []);
const preventClose = useCallback((e) => {
const target = e.target as HTMLElement;
const hasNavAncestor = target.closest('#dialog-nav');
if (hasNavAncestor) {
e.preventDefault();
}
}, []);
return (
<>
{/* Using portal so that we can retain the same nav items both in the dialog and in the base header */}
@ -132,12 +114,20 @@ export const Nav: FunctionComponent<NavProps> = ({ menu }) => {
>
<SystemMenu
open={!!systemMenuOpen}
menu={menuState}
navOpen={isOpen}
shouldDim={isOpen && menu !== 'system-preferences' && menu !== 'help-and-support'}
className={classNames('relative z-50 flex-none', eitherOpen ? 'bg-white' : 'bg-gray-50')}
/>
<NotificationsLink menu={menuState} navOpen={isOpen} />
<Leap ref={inputRef} menu={menuState} dropdown="leap-items" navOpen={isOpen} />
<NotificationsLink
navOpen={isOpen}
shouldDim={(isOpen && menu !== 'notifications') || !!systemMenuOpen}
/>
<Leap
ref={inputRef}
menu={menuState}
dropdown="leap-items"
navOpen={isOpen}
shouldDim={(isOpen && menu !== 'search') || !!systemMenuOpen}
/>
</Portal.Root>
<div
ref={navRef}
@ -152,6 +142,7 @@ export const Nav: FunctionComponent<NavProps> = ({ menu }) => {
/>
<Dialog open={isOpen} onOpenChange={onDialogClose}>
<DialogContent
onInteractOutside={preventClose}
onOpenAutoFocus={onOpen}
className="fixed bottom-0 sm:top-0 sm:bottom-auto scroll-left-50 flex flex-col scroll-full-width max-w-[882px] px-4 sm:pb-4 text-gray-400 -translate-x-1/2 outline-none"
role="combobox"
@ -160,6 +151,7 @@ export const Nav: FunctionComponent<NavProps> = ({ menu }) => {
aria-expanded={isOpen}
>
<header
id="dialog-nav"
ref={dialogNavRef}
className="max-w-[712px] w-full mx-auto my-6 sm:mb-3 order-last sm:order-none"
/>

View File

@ -4,7 +4,6 @@ import { Link, LinkProps } from 'react-router-dom';
import { Bullet } from '../components/icons/Bullet';
import { Notification } from '../state/hark-types';
import { useNotifications } from '../state/notifications';
import { MenuState } from './Nav';
type NotificationsState = 'empty' | 'unread' | 'attention-needed';
@ -25,11 +24,11 @@ function getNotificationsState(
}
type NotificationsLinkProps = Omit<LinkProps<HTMLAnchorElement>, 'to'> & {
menu: MenuState;
navOpen: boolean;
shouldDim: boolean;
};
export const NotificationsLink = ({ navOpen, menu }: NotificationsLinkProps) => {
export const NotificationsLink = ({ navOpen, shouldDim }: NotificationsLinkProps) => {
const { notifications, systemNotifications } = useNotifications();
const state = getNotificationsState(notifications, systemNotifications);
@ -39,7 +38,7 @@ export const NotificationsLink = ({ navOpen, menu }: NotificationsLinkProps) =>
className={classNames(
'relative z-50 flex-none circle-button h4 default-ring',
navOpen && 'text-opacity-60',
navOpen && menu !== 'notifications' && 'opacity-60',
shouldDim && 'opacity-60',
state === 'empty' && !navOpen && 'text-gray-400 bg-gray-50',
state === 'empty' && navOpen && 'text-gray-400 bg-white',
state === 'unread' && 'bg-blue-400 text-white',

View File

@ -7,13 +7,11 @@ import { Vat } from '@urbit/api/hood';
import { Adjust } from '../components/icons/Adjust';
import { useVat } from '../state/kiln';
import { disableDefault, handleDropdownLink } from '../state/util';
import { MenuState } from './Nav';
import { useMedia } from '../logic/useMedia';
type SystemMenuProps = HTMLAttributes<HTMLButtonElement> & {
menu: MenuState;
open: boolean;
navOpen: boolean;
shouldDim: boolean;
};
function getHash(vat: Vat): string {
@ -21,7 +19,7 @@ function getHash(vat: Vat): string {
return parts[parts.length - 1];
}
export const SystemMenu = ({ className, menu, open, navOpen }: SystemMenuProps) => {
export const SystemMenu = ({ className, open, shouldDim }: SystemMenuProps) => {
const { push } = useHistory();
const [copied, setCopied] = useState(false);
const garden = useVat('garden');
@ -61,10 +59,7 @@ export const SystemMenu = ({ className, menu, open, navOpen }: SystemMenuProps)
className={classNames(
'appearance-none circle-button default-ring',
open && 'text-gray-300',
navOpen &&
menu !== 'system-preferences' &&
menu !== 'help-and-support' &&
'opacity-60',
shouldDim && 'opacity-60',
className
)}
>

View File

@ -53,8 +53,8 @@ export const SystemPreferences = (props: RouteComponentProps<{ submenu: string }
);
return (
<div className="flex h-[600px] max-h-full">
<aside className="flex-none min-w-60 border-r-2 border-gray-50">
<div className="flex h-full overflow-y-auto">
<aside className="flex-none min-w-60">
<div className="p-8">
<input className="input h4 default-ring bg-gray-50" placeholder="Search Preferences" />
</div>
@ -84,7 +84,7 @@ export const SystemPreferences = (props: RouteComponentProps<{ submenu: string }
</ul>
</nav>
</aside>
<section className="flex-1 p-8 text-black">
<section className="flex-1 min-h-[600px] p-8 text-black border-l-2 border-gray-50">
<Switch>
<Route path={`${match.url}/system-updates`} component={SystemUpdatePrefs} />
<Route path={`${match.url}/interface`} component={InterfacePrefs} />

View File

@ -11,6 +11,10 @@ export const TileInfo = () => {
const charge = useCharge(desk);
const vat = useVat(desk);
if (!charge) {
return null;
}
return (
<Dialog open onOpenChange={(open) => !open && push('/')}>
<DialogContent>