feat(ui-toolkit, explorer): navigation

This commit is contained in:
asiaznik
2023-03-10 15:56:42 +00:00
committed by Edd
parent 0ce84eba11
commit d1129bd70a
3 changed files with 89 additions and 51 deletions
@@ -55,13 +55,23 @@ export const Header = () => {
<Navigation
appName="Explorer"
theme="system"
breakpoints={[400, 800]}
breakpoints={[490, 900]}
actions={
<>
<ThemeSwitcher />
<Search />
</>
}
onResize={(width, ref) => {
if (width < 1157) {
// switch to magnifying glass trigger when widht < 1157
ref.current?.classList.remove('nav-search-full');
ref.current?.classList.add('nav-search-compact');
} else {
ref.current?.classList.remove('nav-search-compact');
ref.current?.classList.add('nav-search-full');
}
}}
>
<NavigationList hide={[NavigationBreakpoint.Small]}>
<NavigationItem>
@@ -70,7 +70,7 @@ export const Search = () => {
);
const searchForm = (
<form onSubmit={handleSubmit(onSubmit)}>
<form className="block min-w-[290px]" onSubmit={handleSubmit(onSubmit)}>
<div className="flex relative items-stretch gap-2 text-xs">
<label htmlFor="search" className="sr-only">
{t('Search by block number or transaction hash')}
@@ -126,34 +126,38 @@ export const Search = () => {
</form>
);
const searchTrigger = (
<DropdownMenu.Root>
<DropdownMenu.Trigger asChild>
<button className="mt-[-1px] text-vega-light-300 dark:text-vega-dark-300 data-open:text-black dark:data-open:text-white">
<Icon name="search" size={4} />
</button>
</DropdownMenu.Trigger>
<DropdownMenu.Portal>
<DropdownMenu.Content
className={classNames(
'search-dropdown',
'p-2 min-w-[290px] z-20',
'text-vega-light-300 dark:text-vega-dark-300',
'bg-white dark:bg-black',
'border rounded border-vega-light-200 dark:border-vega-dark-200',
'shadow-[8px_8px_16px_0_rgba(0,0,0,0.4)]'
)}
align="end"
sideOffset={10}
>
{searchForm}
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu.Root>
);
return (
<>
<div className="[.nav-size-narrow_&]:hidden [.nav-size-small_&]:hidden min-w-[290px]">
{searchForm}
<div className="hidden [.nav-search-full_&]:block">{searchForm}</div>
<div className="hidden [.nav-search-compact_&]:block">
{searchTrigger}
</div>
<DropdownMenu.Root>
<DropdownMenu.Trigger asChild>
<button className="mt-[-1px] text-vega-light-300 dark:text-vega-dark-300 data-open:text-black dark:data-open:text-white [.nav-size-full_&]:hidden">
<Icon name="search" size={4} />
</button>
</DropdownMenu.Trigger>
<DropdownMenu.Portal>
<DropdownMenu.Content
className={classNames(
'search-dropdown',
'p-2 min-w-[290px] z-20',
'text-vega-light-300 dark:text-vega-dark-300',
'bg-white dark:bg-black',
'border rounded border-vega-light-200 dark:border-vega-dark-200',
'shadow-[8px_8px_16px_0_rgba(0,0,0,0.4)]'
)}
align="end"
sideOffset={10}
>
{searchForm}
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu.Root>
</>
);
};
@@ -1,5 +1,6 @@
import classNames from 'classnames';
import type { ComponentProps, ReactNode } from 'react';
import type { ComponentProps, ReactNode, RefObject } from 'react';
import type { MutableRefObject } from 'react';
import { createContext } from 'react';
import { useContext } from 'react';
import { useRef } from 'react';
@@ -37,6 +38,7 @@ type NavigationProps = {
* Size variants breakpoints
*/
breakpoints?: [number, number];
onResize?: (width: number, ref: RefObject<HTMLElement>) => void;
};
export enum NavigationBreakpoint {
@@ -80,7 +82,7 @@ const Logo = ({
appName,
homeLink,
}: Pick<NavigationProps, 'theme' | 'appName' | 'homeLink'>) => {
const theme = useContext(NavigationThemeContext);
const { theme } = useContext(NavigationContext);
return (
<div className="flex h-full gap-4 items-center">
{homeLink ? (
@@ -166,7 +168,7 @@ export const NavigationTrigger = ({
}: ComponentProps<typeof NavigationMenu.Trigger> & {
isActive?: boolean;
} & NavigationElementProps) => {
const theme = useContext(NavigationThemeContext);
const { theme } = useContext(NavigationContext);
return (
<NavigationMenu.Trigger
className={classNames(
@@ -210,7 +212,7 @@ export const NavigationContent = ({
className,
...props
}: ComponentProps<typeof NavigationMenu.Content>) => {
const theme = useContext(NavigationThemeContext);
const { theme } = useContext(NavigationContext);
const insideDrawer = useContext(DrawerContext);
const content = (
@@ -254,7 +256,7 @@ export const NavigationLink = ({
to,
...props
}: ComponentProps<typeof Link>) => {
const theme = useContext(NavigationThemeContext);
const { theme } = useContext(NavigationContext);
const setDrawerOpen = useDrawer((state) => state.setDrawerOpen);
return (
<NavigationMenu.Link
@@ -304,8 +306,9 @@ export const NavigationLink = ({
);
};
const NavigationThemeContext =
createContext<NavigationProps['theme']>('system');
export const NavigationContext = createContext<{
theme: NavigationProps['theme'];
}>({ theme: 'system' });
export const Navigation = ({
appName,
@@ -314,27 +317,47 @@ export const Navigation = ({
actions,
theme = 'system',
breakpoints = [478, 1000],
onResize,
}: NavigationProps) => {
const navigationRef = useRef<HTMLElement>(null);
const navigationRef = useRef<HTMLElement>(
null
) as MutableRefObject<HTMLElement>;
const actionsRef = useRef<HTMLDivElement>(null);
useResizeObserver(navigationRef.current, (entries) => {
if (entries.length === 0 && !navigationRef.current) return;
if (entries.length === 0 || !navigationRef.current) return;
const width = entries[0].borderBoxSize[0].inlineSize;
navigationRef.current?.classList.remove(
NavigationBreakpoint.Full,
NavigationBreakpoint.Narrow,
NavigationBreakpoint.Small
);
if (width <= breakpoints[0]) {
navigationRef.current?.classList.add(NavigationBreakpoint.Small);
const w = entries[0].borderBoxSize[0].inlineSize;
if (onResize) onResize(w, navigationRef);
if (
w <= breakpoints[0] &&
!navigationRef.current.classList.contains(NavigationBreakpoint.Small)
) {
navigationRef.current.classList.remove(
NavigationBreakpoint.Full,
NavigationBreakpoint.Narrow
);
navigationRef.current.classList.add(NavigationBreakpoint.Small);
}
if (width > breakpoints[0] && width <= breakpoints[1]) {
navigationRef.current?.classList.add(NavigationBreakpoint.Narrow);
if (
w > breakpoints[0] &&
w <= breakpoints[1] &&
!navigationRef.current.classList.contains(NavigationBreakpoint.Narrow)
) {
navigationRef.current.classList.remove(
NavigationBreakpoint.Full,
NavigationBreakpoint.Small
);
navigationRef.current.classList.add(NavigationBreakpoint.Narrow);
}
if (width > breakpoints[1]) {
navigationRef.current?.classList.add(NavigationBreakpoint.Full);
if (
w > breakpoints[1] &&
!navigationRef.current.classList.contains(NavigationBreakpoint.Full)
) {
navigationRef.current.classList.remove(
NavigationBreakpoint.Narrow,
NavigationBreakpoint.Small
);
navigationRef.current.classList.add(NavigationBreakpoint.Full);
}
});
@@ -425,9 +448,10 @@ export const Navigation = ({
);
return (
<NavigationThemeContext.Provider value={theme}>
<NavigationContext.Provider value={{ theme }}>
<NavigationMenu.Root
ref={navigationRef}
id="navigation"
className={classNames(
'h-12',
'group flex gap-4 items-center',
@@ -486,6 +510,6 @@ export const Navigation = ({
</div>
)}
</NavigationMenu.Root>
</NavigationThemeContext.Provider>
</NavigationContext.Provider>
);
};