mirror of
https://github.com/urbit/shrub.git
synced 2024-11-30 22:15:47 +03:00
grid: tweaking focus styles first pass
This commit is contained in:
parent
5de3919e8c
commit
2ff5a39bf7
2
.gitignore
vendored
2
.gitignore
vendored
@ -78,3 +78,5 @@ pkg/interface/link-webext/web-ext-artifacts
|
|||||||
|
|
||||||
# Logs
|
# Logs
|
||||||
*.log
|
*.log
|
||||||
|
|
||||||
|
.vercel
|
||||||
|
13693
pkg/btc-wallet/package-lock.json
generated
13693
pkg/btc-wallet/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -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) =>
|
||||||
|
@ -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}
|
||||||
|
@ -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}
|
||||||
|
@ -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) => (
|
||||||
|
@ -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="" />
|
||||||
|
@ -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,47 +196,51 @@ export const Leap = React.forwardRef(({ menu, dropdown, showClose, className }:
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<div className="relative z-50 w-full">
|
||||||
className={classNames(
|
<form
|
||||||
'relative z-50 flex items-center w-full px-2 rounded-full bg-white default-ring focus-within:ring-4',
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
onSubmit={onSubmit}
|
|
||||||
>
|
|
||||||
<label
|
|
||||||
htmlFor="leap"
|
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'inline-block flex-none p-2 h4 text-blue-400',
|
'flex items-center h-full w-full px-2 rounded-full bg-white default-ring focus-within:ring-2',
|
||||||
!selection && 'sr-only'
|
navOpen && menu !== 'search' && 'opacity-80',
|
||||||
|
!navOpen ? 'bg-gray-100' : '',
|
||||||
|
className
|
||||||
)}
|
)}
|
||||||
|
onSubmit={onSubmit}
|
||||||
>
|
>
|
||||||
{selection || 'Search Landscape'}
|
<label
|
||||||
</label>
|
htmlFor="leap"
|
||||||
<input
|
className={classNames(
|
||||||
id="leap"
|
'inline-block flex-none p-2 h4 text-blue-400',
|
||||||
type="text"
|
!selection && 'sr-only'
|
||||||
ref={inputRef}
|
)}
|
||||||
placeholder={selection ? '' : 'Search Landscape'}
|
>
|
||||||
className="flex-1 w-full h-full px-2 h4 rounded-full bg-transparent outline-none"
|
{selection || 'Search Landscape'}
|
||||||
value={rawInput}
|
</label>
|
||||||
onClick={toggleSearch}
|
<input
|
||||||
onFocus={onFocus}
|
id="leap"
|
||||||
onChange={onChange}
|
type="text"
|
||||||
onKeyDown={onKeyDown}
|
ref={inputRef}
|
||||||
aria-autocomplete="both"
|
placeholder={selection ? '' : 'Search Landscape'}
|
||||||
aria-controls={dropdown}
|
className="flex-1 w-full px-2 h4 rounded-full bg-transparent outline-none"
|
||||||
aria-activedescendant={selectedMatch?.display || selectedMatch?.value}
|
value={rawInput}
|
||||||
/>
|
onClick={toggleSearch}
|
||||||
{showClose && (
|
onFocus={onFocus}
|
||||||
|
onChange={onChange}
|
||||||
|
onKeyDown={onKeyDown}
|
||||||
|
aria-autocomplete="both"
|
||||||
|
aria-controls={dropdown}
|
||||||
|
aria-activedescendant={selectedMatch?.display || selectedMatch?.value}
|
||||||
|
/>
|
||||||
|
</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>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -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"
|
||||||
>
|
>
|
||||||
|
@ -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">
|
||||||
|
@ -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'
|
||||||
)}
|
)}
|
||||||
|
@ -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" />
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -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') {
|
||||||
|
@ -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>
|
||||||
|
@ -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]);
|
||||||
|
@ -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 && (
|
||||||
|
@ -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
|
||||||
e.preventDefault();
|
export function handleDropdownLink(setOpen: (open: boolean) => void): (e: Event) => void {
|
||||||
|
return (e: Event) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
setTimeout(() => setOpen(false), 15);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
@ -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 }}
|
||||||
|
@ -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)}>
|
||||||
|
77763
pkg/interface/package-lock.json
generated
77763
pkg/interface/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
4340
pkg/npm/api/package-lock.json
generated
4340
pkg/npm/api/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
6052
pkg/npm/eslint-config/package-lock.json
generated
6052
pkg/npm/eslint-config/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user