mirror of
https://github.com/urbit/shrub.git
synced 2024-11-24 04:58:08 +03:00
leap: fixing nav and mobile issues
This commit is contained in:
parent
8c799f9c38
commit
32dda74acf
@ -34,17 +34,18 @@ export function createPreviousPath(current: string): string {
|
||||
type LeapProps = {
|
||||
menu: MenuState;
|
||||
dropdown: string;
|
||||
showClose: boolean;
|
||||
} & HTMLAttributes<HTMLDivElement>;
|
||||
|
||||
export const Leap = React.forwardRef(({ menu, dropdown, className }: LeapProps, ref) => {
|
||||
export const Leap = React.forwardRef(({ menu, dropdown, showClose, className }: LeapProps, ref) => {
|
||||
const { push } = useHistory();
|
||||
const match = useRouteMatch<{ query?: string; desk?: string }>(
|
||||
const match = useRouteMatch<{ menu?: MenuState; query?: string; desk?: string }>(
|
||||
`/leap/${menu}/:query?/(apps)?/:desk?`
|
||||
);
|
||||
const appsMatch = useRouteMatch(`/leap/${menu}/${match?.params.query}/apps`);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
useImperativeHandle(ref, () => inputRef.current);
|
||||
const { rawInput, searchInput, selectedMatch, matches, selection, select } = useLeapStore();
|
||||
const { rawInput, selectedMatch, matches, selection, select } = useLeapStore();
|
||||
|
||||
const toggleSearch = useCallback(() => {
|
||||
if (selection || menu === 'search') {
|
||||
@ -54,15 +55,18 @@ export const Leap = React.forwardRef(({ menu, dropdown, className }: LeapProps,
|
||||
push('/leap/search');
|
||||
}, [selection, menu]);
|
||||
|
||||
const onFocus = useCallback((e: FocusEvent<HTMLInputElement>) => {
|
||||
// refocusing tab with input focused is false trigger
|
||||
const windowFocus = e.nativeEvent.currentTarget === document.body;
|
||||
if (windowFocus) {
|
||||
return;
|
||||
}
|
||||
const onFocus = useCallback(
|
||||
(e: FocusEvent<HTMLInputElement>) => {
|
||||
// refocusing tab with input focused is false trigger
|
||||
const windowFocus = e.nativeEvent.currentTarget === document.body;
|
||||
if (windowFocus) {
|
||||
return;
|
||||
}
|
||||
|
||||
toggleSearch();
|
||||
}, []);
|
||||
toggleSearch();
|
||||
},
|
||||
[toggleSearch]
|
||||
);
|
||||
|
||||
const getMatch = useCallback(
|
||||
(value: string) => {
|
||||
@ -212,7 +216,7 @@ export const Leap = React.forwardRef(({ menu, dropdown, className }: LeapProps,
|
||||
aria-controls={dropdown}
|
||||
aria-activedescendant={selectedMatch?.display || selectedMatch?.value}
|
||||
/>
|
||||
{(selection || searchInput) && (
|
||||
{showClose && (
|
||||
<Link
|
||||
to="/"
|
||||
className="circle-button w-8 h-8 text-gray-400 bg-gray-100 default-ring"
|
||||
|
@ -51,22 +51,32 @@ interface NavProps {
|
||||
menu?: MenuState;
|
||||
}
|
||||
|
||||
export const Nav: FunctionComponent<NavProps> = ({ menu = 'closed' }) => {
|
||||
export const Nav: FunctionComponent<NavProps> = ({ menu }) => {
|
||||
const { push } = useHistory();
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const navRef = useRef<HTMLDivElement>(null);
|
||||
const dialogNavRef = useRef<HTMLDivElement>(null);
|
||||
const [systemMenuOpen, setSystemMenuOpen] = useState(false);
|
||||
const [dialogContentOpen, setDialogContentOpen] = useState(false);
|
||||
const { selection, select } = useLeapStore((state) => ({
|
||||
selectedMatch: state.selectedMatch,
|
||||
selection: state.selection,
|
||||
select: state.select
|
||||
}));
|
||||
const [systemMenuOpen, setSystemMenuOpen] = useState(false);
|
||||
const [dialogContentOpen, setDialogContentOpen] = useState(false);
|
||||
|
||||
const isOpen = menu !== 'closed';
|
||||
const menuState = menu || 'closed';
|
||||
const isOpen = menuState !== 'closed';
|
||||
const eitherOpen = isOpen || systemMenuOpen;
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen) {
|
||||
select(null);
|
||||
setDialogContentOpen(false);
|
||||
} else {
|
||||
inputRef.current?.focus();
|
||||
}
|
||||
}, [selection, isOpen]);
|
||||
|
||||
const onOpen = useCallback(
|
||||
(event: Event) => {
|
||||
event.preventDefault();
|
||||
@ -81,14 +91,8 @@ export const Nav: FunctionComponent<NavProps> = ({ menu = 'closed' }) => {
|
||||
[menu]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
inputRef.current?.focus();
|
||||
}, [selection]);
|
||||
|
||||
const onDialogClose = useCallback((open: boolean) => {
|
||||
if (!open) {
|
||||
select(null);
|
||||
setDialogContentOpen(false);
|
||||
push('/');
|
||||
}
|
||||
}, []);
|
||||
@ -112,8 +116,9 @@ export const Nav: FunctionComponent<NavProps> = ({ menu = 'closed' }) => {
|
||||
</Link>
|
||||
<Leap
|
||||
ref={inputRef}
|
||||
menu={menu}
|
||||
menu={menuState}
|
||||
dropdown="leap-items"
|
||||
showClose={isOpen}
|
||||
className={!isOpen ? 'bg-gray-100' : ''}
|
||||
/>
|
||||
</Portal.Root>
|
||||
|
@ -3,25 +3,25 @@
|
||||
}
|
||||
|
||||
.button {
|
||||
@apply inline-flex items-center justify-center px-4 py-2 font-semibold text-base tracking-tight rounded-lg cursor-pointer;
|
||||
@apply inline-flex items-center justify-center px-4 py-2 font-semibold text-base tracking-tight rounded-lg cursor-pointer;
|
||||
}
|
||||
|
||||
.dialog-container {
|
||||
@apply fixed z-40 top-1/2 left-1/2 min-w-80 transform -translate-x-1/2 -translate-y-1/2;
|
||||
@apply fixed z-40 top-1/2 left-1/2 min-w-80 transform -translate-x-1/2 -translate-y-1/2;
|
||||
}
|
||||
|
||||
.dialog {
|
||||
@apply relative p-5 sm:p-8 bg-white rounded-3xl;
|
||||
@apply relative p-5 sm:p-8 bg-white rounded-3xl;
|
||||
}
|
||||
|
||||
.dialog-inner-container {
|
||||
@apply h-full p-4 md:p-8 space-y-8 overflow-y-auto;
|
||||
@apply h-full p-4 md:p-8 space-y-8 overflow-y-auto;
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
@apply min-w-52 p-4 space-y-4 rounded-xl;
|
||||
@apply min-w-52 p-4 rounded-xl;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
@apply inline-flex items-center w-6 h-6 animate-spin;
|
||||
}
|
||||
@apply inline-flex items-center w-6 h-6 animate-spin;
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ export const Tile: FunctionComponent<TileProps> = ({ docket, desk }) => {
|
||||
active={active}
|
||||
menuColor={menuColor}
|
||||
lightText={lightText}
|
||||
className="absolute z-10 top-2.5 right-2.5 sm:top-4 sm:right-4 opacity-0 hover-none:opacity-100 focus:opacity-100 group-hover:opacity-100"
|
||||
className="absolute z-10 top-2.5 right-2.5 sm:top-4 sm:right-4 opacity-0 hover-none:opacity-100 pointer-coarse:opacity-100 focus:opacity-100 group-hover:opacity-100"
|
||||
/>
|
||||
<div className="h4 absolute bottom-4 left-4 lg:bottom-8 lg:left-8">
|
||||
<h3
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import type * as Polymorphic from '@radix-ui/react-polymorphic';
|
||||
import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
|
||||
import classNames from 'classnames';
|
||||
@ -31,7 +31,7 @@ const Item = React.forwardRef(({ children, ...props }, ref) => (
|
||||
<DropdownMenu.Item
|
||||
ref={ref}
|
||||
{...props}
|
||||
className="block w-full px-4 py-1 leading-none rounded mix-blend-hard-light select-none default-ring ring-gray-600"
|
||||
className="block w-full px-4 py-3 leading-none rounded mix-blend-hard-light select-none default-ring ring-gray-600"
|
||||
>
|
||||
{children}
|
||||
</DropdownMenu.Item>
|
||||
@ -47,6 +47,10 @@ export const TileMenu = ({ desk, active, menuColor, lightText, className }: Tile
|
||||
});
|
||||
|
||||
const menuBg = { backgroundColor: menuColor };
|
||||
const linkOnSelect = useCallback((e: Event) => {
|
||||
e.preventDefault();
|
||||
setTimeout(() => setOpen(false), 15);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<DropdownMenu.Root open={open} onOpenChange={(isOpen) => setOpen(isOpen)}>
|
||||
@ -71,50 +75,29 @@ export const TileMenu = ({ desk, active, menuColor, lightText, className }: Tile
|
||||
sideOffset={4}
|
||||
onCloseAutoFocus={(e) => e.preventDefault()}
|
||||
className={classNames(
|
||||
'dropdown font-semibold',
|
||||
'dropdown py-2 font-semibold',
|
||||
lightText ? 'text-gray-100' : 'text-gray-800'
|
||||
)}
|
||||
style={menuBg}
|
||||
>
|
||||
<DropdownMenu.Group className="space-y-4">
|
||||
<DropdownMenu.Group>
|
||||
{/*
|
||||
TODO: revisit with Liam
|
||||
<Item as={Link} to={`/leap/search/${provider}/apps/${name.toLowerCase()}`} onSelect={(e) => { e.preventDefault(); setTimeout(() => setOpen(false), 0) }}>App Info</Item>
|
||||
*/}
|
||||
<Item
|
||||
as={Link}
|
||||
to={`/app/${desk}`}
|
||||
onSelect={(e) => {
|
||||
e.preventDefault();
|
||||
setTimeout(() => setOpen(false), 0);
|
||||
}}
|
||||
>
|
||||
<Item as={Link} to={`/app/${desk}`} onSelect={linkOnSelect}>
|
||||
App Info
|
||||
</Item>
|
||||
</DropdownMenu.Group>
|
||||
<DropdownMenu.Separator className="-mx-4 my-2 border-t-2 border-solid border-gray-500 mix-blend-soft-light" />
|
||||
<DropdownMenu.Group className="space-y-4">
|
||||
<DropdownMenu.Group>
|
||||
{active && (
|
||||
<Item
|
||||
as={Link}
|
||||
to={`/app/${desk}/suspend`}
|
||||
onSelect={(e) => {
|
||||
e.preventDefault();
|
||||
setTimeout(() => setOpen(false), 0);
|
||||
}}
|
||||
>
|
||||
<Item as={Link} to={`/app/${desk}/suspend`} onSelect={linkOnSelect}>
|
||||
Suspend App
|
||||
</Item>
|
||||
)}
|
||||
{!active && <Item onSelect={() => mutate()}>Resume App</Item>}
|
||||
<Item
|
||||
as={Link}
|
||||
to={`/app/${desk}/remove`}
|
||||
onSelect={(e) => {
|
||||
e.preventDefault();
|
||||
setTimeout(() => setOpen(false), 0);
|
||||
}}
|
||||
>
|
||||
<Item as={Link} to={`/app/${desk}/remove`} onSelect={linkOnSelect}>
|
||||
Remove App
|
||||
</Item>
|
||||
</DropdownMenu.Group>
|
||||
|
Loading…
Reference in New Issue
Block a user