grid: tweaking focus styles first pass

This commit is contained in:
Hunter Miller 2021-08-30 15:28:43 -05:00
parent 5de3919e8c
commit 2ff5a39bf7
23 changed files with 74387 additions and 27701 deletions

2
.gitignore vendored
View File

@ -78,3 +78,5 @@ pkg/interface/link-webext/web-ext-artifacts
# Logs # Logs
*.log *.log
.vercel

File diff suppressed because it is too large Load Diff

View File

@ -35,8 +35,11 @@ export const AppLink = <T extends DocketWithDesk>({
}: AppLinkProps<T>) => { }: AppLinkProps<T>) => {
const linkTo = to?.(app); const linkTo = to?.(app);
const linkClassnames = classNames( const linkClassnames = classNames(
'flex items-center default-ring ring-offset-2 rounded-lg', 'flex items-center default-ring rounded-lg',
selected && 'ring-4', size === 'default' && 'ring-offset-2',
size !== 'xs' && 'p-2',
size === 'xs' && 'p-1',
selected && 'bg-blue-200',
className className
); );
const link = (children: ReactNode) => const link = (children: ReactNode) =>

View File

@ -11,7 +11,7 @@ type AppListProps<T extends DocketWithDesk> = {
matchAgainst?: MatchItem; matchAgainst?: MatchItem;
onClick?: (e: MouseEvent<HTMLAnchorElement>, app: T) => void; onClick?: (e: MouseEvent<HTMLAnchorElement>, app: T) => void;
listClass?: string; listClass?: string;
} & Omit<AppLinkProps<T>, 'app' | 'onClick'>; } & Omit<AppLinkProps, 'app' | 'onClick'>;
export function appMatches(target: DocketWithDesk, match?: MatchItem): boolean { export function appMatches(target: DocketWithDesk, match?: MatchItem): boolean {
if (!match) { if (!match) {
@ -27,7 +27,7 @@ export const AppList = <T extends DocketWithDesk>({
labelledBy, labelledBy,
matchAgainst, matchAgainst,
onClick, onClick,
listClass = 'space-y-8', listClass,
size = 'default', size = 'default',
...props ...props
}: AppListProps<T>) => { }: AppListProps<T>) => {
@ -37,9 +37,9 @@ export const AppList = <T extends DocketWithDesk>({
return ( return (
<ul <ul
className={classNames( className={classNames(
size === 'default' && 'space-y-8', size === 'default' && 'space-y-4',
size === 'small' && 'space-y-4', size !== 'xs' && '-mx-2',
size === 'xs' && 'space-y-2', size === 'xs' && '-mx-1',
listClass listClass
)} )}
aria-labelledby={labelledBy} aria-labelledby={labelledBy}

View File

@ -23,8 +23,9 @@ export const ProviderLink = ({
<Link <Link
to={(to && to(provider)) || `/leap/search/${provider.shipName}/apps`} to={(to && to(provider)) || `/leap/search/${provider.shipName}/apps`}
className={classNames( className={classNames(
'flex items-center space-x-3 default-ring ring-offset-2 rounded-lg', 'flex items-center p-2 space-x-3 default-ring rounded-lg',
selected && 'ring-4', !small && 'ring-offset-2',
selected && 'bg-blue-200',
className className
)} )}
{...props} {...props}

View File

@ -39,7 +39,7 @@ export const ProviderList = ({
return ( return (
<ul <ul
className={classNames(small ? 'space-y-4' : 'space-y-8', listClass)} className={classNames('-mx-2', !small && 'space-y-4', listClass)}
aria-labelledby={labelledBy} aria-labelledby={labelledBy}
> >
{providers.map((p) => ( {providers.map((p) => (

View File

@ -1,14 +1,7 @@
import React, { useEffect } from 'react'; import React from 'react';
import { useLeapStore } from './Nav';
import helpAndSupport from '../assets/help-and-support.svg'; import helpAndSupport from '../assets/help-and-support.svg';
export const Help = () => { export const Help = () => {
const select = useLeapStore((state) => state.select);
useEffect(() => {
select('Help and Support');
}, []);
return ( return (
<div className="flex flex-col items-center px-4 py-8 md:px-8 md:py-16 space-y-8 md:space-y-16"> <div className="flex flex-col items-center px-4 py-8 md:px-8 md:py-16 space-y-8 md:space-y-16">
<img className="w-52 h-auto" src={helpAndSupport} alt="" /> <img className="w-52 h-auto" src={helpAndSupport} alt="" />

View File

@ -34,10 +34,10 @@ export function createPreviousPath(current: string): string {
type LeapProps = { type LeapProps = {
menu: MenuState; menu: MenuState;
dropdown: string; dropdown: string;
showClose: boolean; navOpen: boolean;
} & HTMLAttributes<HTMLDivElement>; } & HTMLAttributes<HTMLDivElement>;
export const Leap = React.forwardRef(({ menu, dropdown, showClose, className }: LeapProps, ref) => { export const Leap = React.forwardRef(({ menu, dropdown, navOpen, className }: LeapProps, ref) => {
const { push } = useHistory(); const { push } = useHistory();
const match = useRouteMatch<{ menu?: MenuState; query?: string; desk?: string }>( const match = useRouteMatch<{ menu?: MenuState; query?: string; desk?: string }>(
`/leap/${menu}/:query?/(apps)?/:desk?` `/leap/${menu}/:query?/(apps)?/:desk?`
@ -196,9 +196,12 @@ export const Leap = React.forwardRef(({ menu, dropdown, showClose, className }:
); );
return ( return (
<div className="relative z-50 w-full">
<form <form
className={classNames( className={classNames(
'relative z-50 flex items-center w-full px-2 rounded-full bg-white default-ring focus-within:ring-4', 'flex items-center h-full w-full px-2 rounded-full bg-white default-ring focus-within:ring-2',
navOpen && menu !== 'search' && 'opacity-80',
!navOpen ? 'bg-gray-100' : '',
className className
)} )}
onSubmit={onSubmit} onSubmit={onSubmit}
@ -217,7 +220,7 @@ export const Leap = React.forwardRef(({ menu, dropdown, showClose, className }:
type="text" type="text"
ref={inputRef} ref={inputRef}
placeholder={selection ? '' : 'Search Landscape'} placeholder={selection ? '' : 'Search Landscape'}
className="flex-1 w-full h-full px-2 h4 rounded-full bg-transparent outline-none" className="flex-1 w-full px-2 h4 rounded-full bg-transparent outline-none"
value={rawInput} value={rawInput}
onClick={toggleSearch} onClick={toggleSearch}
onFocus={onFocus} onFocus={onFocus}
@ -227,16 +230,17 @@ export const Leap = React.forwardRef(({ menu, dropdown, showClose, className }:
aria-controls={dropdown} aria-controls={dropdown}
aria-activedescendant={selectedMatch?.display || selectedMatch?.value} aria-activedescendant={selectedMatch?.display || selectedMatch?.value}
/> />
{showClose && ( </form>
{navOpen && (
<Link <Link
to="/" to="/"
className="circle-button w-8 h-8 text-gray-400 bg-gray-100 default-ring" className="absolute top-1/2 right-2 flex-none circle-button w-8 h-8 text-gray-400 bg-gray-100 default-ring -translate-y-1/2"
onClick={() => select(null)} onClick={() => select(null)}
> >
<Cross className="w-3 h-3 fill-current" /> <Cross className="w-3 h-3 fill-current" />
<span className="sr-only">Close</span> <span className="sr-only">Close</span>
</Link> </Link>
)} )}
</form> </div>
); );
}); });

View File

@ -142,17 +142,12 @@ export const Nav: FunctionComponent<NavProps> = ({ menu }) => {
<SystemMenu <SystemMenu
open={systemMenuOpen} open={systemMenuOpen}
setOpen={setSystemMenuOpen} setOpen={setSystemMenuOpen}
showOverlay={!isOpen} menu={menuState}
navOpen={isOpen}
className={classNames('relative z-50 flex-none', eitherOpen ? 'bg-white' : 'bg-gray-100')} className={classNames('relative z-50 flex-none', eitherOpen ? 'bg-white' : 'bg-gray-100')}
/> />
<NotificationsLink isOpen={isOpen} /> <NotificationsLink menu={menuState} navOpen={isOpen} />
<Leap <Leap ref={inputRef} menu={menuState} dropdown="leap-items" navOpen={isOpen} />
ref={inputRef}
menu={menuState}
dropdown="leap-items"
showClose={isOpen}
className={classNames('flex-1 max-w-[600px]', !isOpen ? 'bg-gray-100' : '')}
/>
</Portal.Root> </Portal.Root>
<div <div
ref={navRef} ref={navRef}
@ -169,7 +164,7 @@ export const Nav: FunctionComponent<NavProps> = ({ menu }) => {
<DialogContent <DialogContent
onOpenAutoFocus={onOpen} onOpenAutoFocus={onOpen}
onInteractOutside={disableCloseWhenDropdownOpen} onInteractOutside={disableCloseWhenDropdownOpen}
className="fixed bottom-0 sm:top-0 scroll-left-50 flex flex-col scroll-full-width max-w-3xl px-4 pb-4 text-gray-400 -translate-x-1/2 outline-none" className="fixed bottom-0 sm:top-0 scroll-left-50 flex flex-col scroll-full-width max-w-3xl px-4 sm:pb-4 text-gray-400 -translate-x-1/2 outline-none"
role="combobox" role="combobox"
aria-controls="leap-items" aria-controls="leap-items"
aria-owns="leap-items" aria-owns="leap-items"
@ -178,7 +173,7 @@ export const Nav: FunctionComponent<NavProps> = ({ menu }) => {
<header ref={dialogNavRef} className="my-6 order-last sm:order-none" /> <header ref={dialogNavRef} className="my-6 order-last sm:order-none" />
<div <div
id="leap-items" id="leap-items"
className="grid grid-rows-[fit-content(100vh)] bg-white rounded-3xl overflow-hidden default-ring" className="grid grid-rows-[fit-content(100vh)] bg-white rounded-3xl overflow-hidden default-ring focus-visible:ring-2"
tabIndex={0} tabIndex={0}
role="listbox" role="listbox"
> >

View File

@ -1,6 +1,5 @@
import React, { useEffect } from 'react'; import React from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { useLeapStore } from './Nav';
import { Button } from '../components/Button'; import { Button } from '../components/Button';
import { Notification } from '../state/hark-types'; import { Notification } from '../state/hark-types';
import { BasicNotification } from './notifications/BasicNotification'; import { BasicNotification } from './notifications/BasicNotification';
@ -27,12 +26,12 @@ const Empty = () => (
); );
export const Notifications = () => { export const Notifications = () => {
const select = useLeapStore((s) => s.select); // const select = useLeapStore((s) => s.select);
const { notifications, systemNotifications, hasAnyNotifications } = useNotifications(); const { notifications, systemNotifications, hasAnyNotifications } = useNotifications();
useEffect(() => { // useEffect(() => {
select('Notifications'); // select('Notifications');
}, []); // }, []);
return ( return (
<div className="grid grid-rows-[auto,1fr] h-full p-4 md:p-8 overflow-hidden"> <div className="grid grid-rows-[auto,1fr] h-full p-4 md:p-8 overflow-hidden">

View File

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

View File

@ -4,15 +4,17 @@ import clipboardCopy from 'clipboard-copy';
import React, { HTMLAttributes, useCallback, useState } from 'react'; import React, { HTMLAttributes, useCallback, useState } from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { Adjust } from '../components/icons/Adjust'; import { Adjust } from '../components/icons/Adjust';
import { disableDefault } from '../state/util'; import { disableDefault, handleDropdownLink } from '../state/util';
import { MenuState } from './Nav';
type SystemMenuProps = HTMLAttributes<HTMLButtonElement> & { type SystemMenuProps = HTMLAttributes<HTMLButtonElement> & {
open: boolean; open: boolean;
setOpen: (open: boolean) => void; setOpen: (open: boolean) => void;
showOverlay?: boolean; menu: MenuState;
navOpen: boolean;
}; };
export const SystemMenu = ({ open, setOpen, className, showOverlay = false }: SystemMenuProps) => { export const SystemMenu = ({ open, setOpen, className, menu, navOpen }: SystemMenuProps) => {
const [copied, setCopied] = useState(false); const [copied, setCopied] = useState(false);
const copyHash = useCallback((event: Event) => { const copyHash = useCallback((event: Event) => {
@ -30,7 +32,12 @@ export const SystemMenu = ({ open, setOpen, className, showOverlay = false }: Sy
<> <>
<DropdownMenu.Root open={open} onOpenChange={(isOpen) => setOpen(isOpen)}> <DropdownMenu.Root open={open} onOpenChange={(isOpen) => setOpen(isOpen)}>
<DropdownMenu.Trigger <DropdownMenu.Trigger
className={classNames('circle-button default-ring', open && 'text-gray-300', className)} className={classNames(
'circle-button default-ring',
open && 'text-gray-300',
navOpen && menu !== 'system-preferences' && menu !== 'help-and-support' && 'opacity-80',
className
)}
> >
<Adjust className="w-6 h-6 fill-current" /> <Adjust className="w-6 h-6 fill-current" />
<span className="sr-only">System Menu</span> <span className="sr-only">System Menu</span>
@ -39,17 +46,14 @@ export const SystemMenu = ({ open, setOpen, className, showOverlay = false }: Sy
<DropdownMenu.Content <DropdownMenu.Content
onCloseAutoFocus={disableDefault} onCloseAutoFocus={disableDefault}
sideOffset={12} sideOffset={12}
className="dropdown min-w-64 p-6 font-semibold text-gray-500 bg-white" className="dropdown min-w-64 p-4 font-semibold text-gray-500 bg-white"
> >
<DropdownMenu.Group className="space-y-6"> <DropdownMenu.Group>
<DropdownMenu.Item <DropdownMenu.Item
as={Link} as={Link}
to="/leap/system-preferences" to="/leap/system-preferences"
className="flex items-center space-x-2 default-ring ring-offset-2 rounded" className="flex items-center p-2 mb-2 space-x-2 focus:bg-blue-200 focus:outline-none rounded"
onSelect={(e) => { onSelect={handleDropdownLink(setOpen)}
e.preventDefault();
setTimeout(() => setOpen(false), 0);
}}
> >
<span className="w-5 h-5 bg-gray-100 rounded-full" /> <span className="w-5 h-5 bg-gray-100 rounded-full" />
<span className="h4">System Preferences</span> <span className="h4">System Preferences</span>
@ -57,19 +61,15 @@ export const SystemMenu = ({ open, setOpen, className, showOverlay = false }: Sy
<DropdownMenu.Item <DropdownMenu.Item
as={Link} as={Link}
to="/leap/help-and-support" to="/leap/help-and-support"
className="flex items-center space-x-2 default-ring ring-offset-2 rounded" className="flex items-center p-2 mb-2 space-x-2 focus:bg-blue-200 focus:outline-none rounded"
onSelect={(e) => { onSelect={handleDropdownLink(setOpen)}
e.stopPropagation();
e.preventDefault();
setTimeout(() => setOpen(false), 0);
}}
> >
<span className="w-5 h-5 bg-gray-100 rounded-full" /> <span className="w-5 h-5 bg-gray-100 rounded-full" />
<span className="h4">Help and Support</span> <span className="h4">Help and Support</span>
</DropdownMenu.Item> </DropdownMenu.Item>
<DropdownMenu.Item <DropdownMenu.Item
as="button" as="button"
className="inline-flex items-center py-2 px-3 h4 text-black bg-gray-100 rounded default-ring" className="inline-flex items-center py-2 px-3 m-2 h4 text-black bg-gray-100 rounded focus:bg-blue-200 focus:outline-none"
onSelect={copyHash} onSelect={copyHash}
> >
<span className="sr-only">Base Hash</span> <span className="sr-only">Base Hash</span>
@ -82,7 +82,7 @@ export const SystemMenu = ({ open, setOpen, className, showOverlay = false }: Sy
</DropdownMenu.Content> </DropdownMenu.Content>
</DropdownMenu.Root> </DropdownMenu.Root>
{showOverlay && open && ( {!navOpen && open && (
<div className="fixed z-30 right-0 bottom-0 w-screen h-screen bg-black opacity-30" /> <div className="fixed z-30 right-0 bottom-0 w-screen h-screen bg-black opacity-30" />
)} )}
</> </>

View File

@ -1,20 +1,14 @@
import React, { useCallback, useEffect } from 'react'; import React, { useCallback } from 'react';
import { Link, Route, RouteComponentProps, Switch, useRouteMatch } from 'react-router-dom'; import { Link, Route, RouteComponentProps, Switch, useRouteMatch } from 'react-router-dom';
import classNames from 'classnames'; import classNames from 'classnames';
import { useLeapStore } from './Nav';
import { NotificationPrefs } from './preferences/NotificationPrefs'; import { NotificationPrefs } from './preferences/NotificationPrefs';
import { SystemUpdatePrefs } from './preferences/SystemUpdatePrefs'; import { SystemUpdatePrefs } from './preferences/SystemUpdatePrefs';
import notificationsSVG from '../assets/notifications.svg'; import notificationsSVG from '../assets/notifications.svg';
import systemUpdatesSVG from '../assets/system-updates.svg'; import systemUpdatesSVG from '../assets/system-updates.svg';
export const SystemPreferences = ({ match }: RouteComponentProps<{ submenu: string }>) => { export const SystemPreferences = ({ match }: RouteComponentProps<{ submenu: string }>) => {
const select = useLeapStore((state) => state.select);
const subMatch = useRouteMatch<{ submenu: string }>(`${match.url}/:submenu`); const subMatch = useRouteMatch<{ submenu: string }>(`${match.url}/:submenu`);
useEffect(() => {
select('System Preferences');
}, []);
const matchSub = useCallback( const matchSub = useCallback(
(target: string) => { (target: string) => {
if (!subMatch && target === 'notifications') { if (!subMatch && target === 'notifications') {

View File

@ -1,16 +1,10 @@
import React, { useEffect } from 'react'; import React from 'react';
import { Setting } from '../../components/Setting'; import { Setting } from '../../components/Setting';
import { useLeapStore } from '../Nav';
import { usePreferencesStore } from './usePreferencesStore'; import { usePreferencesStore } from './usePreferencesStore';
export const NotificationPrefs = () => { export const NotificationPrefs = () => {
const select = useLeapStore((s) => s.select);
const { doNotDisturb, mentions, toggleDoNotDisturb, toggleMentions } = usePreferencesStore(); const { doNotDisturb, mentions, toggleDoNotDisturb, toggleMentions } = usePreferencesStore();
useEffect(() => {
select('System Preferences: Notifications');
}, []);
return ( return (
<> <>
<h2 className="h3 mb-7">Notifications</h2> <h2 className="h3 mb-7">Notifications</h2>

View File

@ -4,20 +4,14 @@ import { Setting } from '../../components/Setting';
import { ShipName } from '../../components/ShipName'; import { ShipName } from '../../components/ShipName';
import { Spinner } from '../../components/Spinner'; import { Spinner } from '../../components/Spinner';
import { useAsyncCall } from '../../logic/useAsyncCall'; import { useAsyncCall } from '../../logic/useAsyncCall';
import { useLeapStore } from '../Nav';
import { usePreferencesStore } from './usePreferencesStore'; import { usePreferencesStore } from './usePreferencesStore';
export const SystemUpdatePrefs = () => { export const SystemUpdatePrefs = () => {
const select = useLeapStore((s) => s.select);
const { otasEnabled, otaSource, toggleOTAs, setOTASource } = usePreferencesStore(); const { otasEnabled, otaSource, toggleOTAs, setOTASource } = usePreferencesStore();
const [source, setSource] = useState(otaSource); const [source, setSource] = useState(otaSource);
const sourceDirty = source !== otaSource; const sourceDirty = source !== otaSource;
const { status: sourceStatus, call: setOTA } = useAsyncCall(setOTASource); const { status: sourceStatus, call: setOTA } = useAsyncCall(setOTASource);
useEffect(() => {
select('System Preferences: Updates');
}, []);
useEffect(() => { useEffect(() => {
setSource(otaSource); setSource(otaSource);
}, [otaSource]); }, [otaSource]);

View File

@ -91,7 +91,7 @@ export const Home = () => {
return ( return (
<div className="h-full p-4 md:p-8 font-semibold leading-tight text-black overflow-y-auto"> <div className="h-full p-4 md:p-8 font-semibold leading-tight text-black overflow-y-auto">
<h2 id="recent-apps" className="mb-6 h4 text-gray-500"> <h2 id="recent-apps" className="mb-4 h4 text-gray-500">
Recent Apps Recent Apps
</h2> </h2>
{recentApps.length === 0 && ( {recentApps.length === 0 && (
@ -116,7 +116,7 @@ export const Home = () => {
/> />
)} )}
<hr className="-mx-4 my-6 md:-mx-8 md:my-9" /> <hr className="-mx-4 my-6 md:-mx-8 md:my-9" />
<h2 id="recent-devs" className="mb-6 h4 text-gray-500"> <h2 id="recent-devs" className="mb-4 h4 text-gray-500">
Recent Developers Recent Developers
</h2> </h2>
{recentDevs.length === 0 && ( {recentDevs.length === 0 && (

View File

@ -1,11 +1,5 @@
import { DocketHref } from '@urbit/api/docket'; import { DocketHref } from '@urbit/api/docket';
export function makeKeyFn(key: string) {
return (childKeys: string[] = []) => {
return [key].concat(childKeys);
};
}
export const useMockData = import.meta.env.MODE === 'mock'; export const useMockData = import.meta.env.MODE === 'mock';
export async function fakeRequest<T>(data: T, time = 300): Promise<T> { export async function fakeRequest<T>(data: T, time = 300): Promise<T> {
@ -24,6 +18,11 @@ export function disableDefault<T extends Event>(e: T): void {
e.preventDefault(); e.preventDefault();
} }
export function disableDefault<T extends Event>(e: T): void { // hack until radix-ui fixes this behavior
export function handleDropdownLink(setOpen: (open: boolean) => void): (e: Event) => void {
return (e: Event) => {
e.stopPropagation();
e.preventDefault(); e.preventDefault();
setTimeout(() => setOpen(false), 15);
};
} }

View File

@ -15,5 +15,5 @@
} }
.default-ring { .default-ring {
@apply focus:ring-4 ring-blue-400 ring-opacity-80 focus:outline-none; @apply focus-visible:ring-2 ring-blue-400 ring-opacity-80 focus-visible:outline-none;
} }

View File

@ -41,7 +41,7 @@ export const Tile: FunctionComponent<TileProps> = ({ charge, desk }) => {
href={active ? link : undefined} href={active ? link : undefined}
target={desk} target={desk}
className={classNames( className={classNames(
'group relative font-semibold aspect-w-1 aspect-h-1 rounded-3xl default-ring overflow-hidden', 'group relative font-semibold aspect-w-1 aspect-h-1 rounded-3xl default-ring focus-visible:ring-4 overflow-hidden',
!active && 'cursor-default' !active && 'cursor-default'
)} )}
style={{ backgroundColor: active ? color || 'purple' : suspendColor }} style={{ backgroundColor: active ? color || 'purple' : suspendColor }}

View File

@ -4,7 +4,7 @@ import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
import classNames from 'classnames'; import classNames from 'classnames';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import useDocketState from '../state/docket'; import useDocketState from '../state/docket';
import { disableDefault } from '../state/util'; import { disableDefault, handleDropdownLink } from '../state/util';
export interface TileMenuProps { export interface TileMenuProps {
desk: string; desk: string;
@ -41,10 +41,7 @@ export const TileMenu = ({ desk, active, menuColor, lightText, className }: Tile
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const toggleDocket = useDocketState((s) => s.toggleDocket); const toggleDocket = useDocketState((s) => s.toggleDocket);
const menuBg = { backgroundColor: menuColor }; const menuBg = { backgroundColor: menuColor };
const linkOnSelect = useCallback((e: Event) => { const linkOnSelect = useCallback(handleDropdownLink(setOpen), []);
e.preventDefault();
setTimeout(() => setOpen(false), 15);
}, []);
return ( return (
<DropdownMenu.Root open={open} onOpenChange={(isOpen) => setOpen(isOpen)}> <DropdownMenu.Root open={open} onOpenChange={(isOpen) => setOpen(isOpen)}>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff