Commit 0eb4ffd8 authored by tom's avatar tom

fix MobileHeader component

parent 06404103
...@@ -25,11 +25,13 @@ const RESTRICTED_MODULES = { ...@@ -25,11 +25,13 @@ const RESTRICTED_MODULES = {
{ name: '@metamask/providers', message: 'Please lazy-load @metamask/providers or use useProvider hook instead' }, { name: '@metamask/providers', message: 'Please lazy-load @metamask/providers or use useProvider hook instead' },
{ name: '@metamask/post-message-stream', message: 'Please lazy-load @metamask/post-message-stream or use useProvider hook instead' }, { name: '@metamask/post-message-stream', message: 'Please lazy-load @metamask/post-message-stream or use useProvider hook instead' },
{ name: 'playwright/TestApp', message: 'Please use render() fixture from test() function of playwright/lib module' }, { name: 'playwright/TestApp', message: 'Please use render() fixture from test() function of playwright/lib module' },
{ name: 'ui/shared/chakra/Skeleton', message: 'Please use Skeleton component from toolkit/chakra instead' },
{ {
name: '@chakra-ui/react', name: '@chakra-ui/react',
importNames: [ importNames: [
'Menu', 'useToast', 'useDisclosure', 'useClipboard', 'Tooltip', 'Skeleton', 'IconButton', 'Button', 'Menu', 'useToast', 'useDisclosure', 'useClipboard', 'Tooltip', 'Skeleton', 'IconButton', 'Button',
'Image', 'Popover', 'PopoverTrigger', 'PopoverContent', 'PopoverBody', 'PopoverFooter', 'Image', 'Popover', 'PopoverTrigger', 'PopoverContent', 'PopoverBody', 'PopoverFooter',
'DrawerRoot', 'DrawerBody', 'DrawerContent', 'DrawerOverlay', 'DrawerBackdrop', 'DrawerTrigger', 'Drawer',
], ],
message: 'Please use corresponding component or hook from ui/shared/chakra component instead', message: 'Please use corresponding component or hook from ui/shared/chakra component instead',
}, },
......
...@@ -37,7 +37,7 @@ export const DrawerCloseTrigger = React.forwardRef< ...@@ -37,7 +37,7 @@ export const DrawerCloseTrigger = React.forwardRef<
{ ...props } { ...props }
asChild asChild
> >
<CloseButton size="sm" ref={ ref }/> <CloseButton ref={ ref }/>
</ChakraDrawer.CloseTrigger> </ChakraDrawer.CloseTrigger>
); );
}); });
......
'use client';
import { NativeSelect as Select } from '@chakra-ui/react';
import * as React from 'react';
interface NativeSelectRootProps extends Select.RootProps {
icon?: React.ReactNode;
}
export const NativeSelectRoot = React.forwardRef<
HTMLDivElement,
NativeSelectRootProps
>(function NativeSelect(props, ref) {
const { icon, children, ...rest } = props;
return (
<Select.Root ref={ ref } { ...rest }>
{ children }
<Select.Indicator>{ icon }</Select.Indicator>
</Select.Root>
);
});
interface NativeSelectItem {
value: string;
label: string;
disabled?: boolean;
}
interface NativeSelectField extends Select.FieldProps {
items?: Array<string | NativeSelectItem>;
}
export const NativeSelectField = React.forwardRef<
HTMLSelectElement,
NativeSelectField
>(function NativeSelectField(props, ref) {
const { items: itemsProp, children, ...rest } = props;
const items = React.useMemo(
() =>
itemsProp?.map((item) =>
typeof item === 'string' ? { label: item, value: item } : item,
),
[ itemsProp ],
);
return (
<Select.Field ref={ ref } { ...rest }>
{ children }
{ items?.map((item) => (
<option key={ item.value } value={ item.value } disabled={ item.disabled }>
{ item.label }
</option>
)) }
</Select.Field>
);
});
...@@ -75,7 +75,7 @@ export const PopoverTrigger = React.forwardRef< ...@@ -75,7 +75,7 @@ export const PopoverTrigger = React.forwardRef<
HTMLButtonElement, HTMLButtonElement,
ChakraPopover.TriggerProps ChakraPopover.TriggerProps
>(function PopoverTrigger(props, ref) { >(function PopoverTrigger(props, ref) {
return <ChakraPopover.Trigger as="div" asChild ref={ ref } { ...props }/>; return <ChakraPopover.Trigger as="div" display="flex" asChild ref={ ref } { ...props }/>;
}); });
export const PopoverTitle = ChakraPopover.Title; export const PopoverTitle = ChakraPopover.Title;
......
'use client';
import type { CollectionItem } from '@chakra-ui/react';
import { Select as ChakraSelect, Portal } from '@chakra-ui/react';
import * as React from 'react';
import { CloseButton } from './close-button';
interface SelectTriggerProps extends ChakraSelect.ControlProps {
clearable?: boolean;
}
export const SelectTrigger = React.forwardRef<
HTMLButtonElement,
SelectTriggerProps
>(function SelectTrigger(props, ref) {
const { children, clearable, ...rest } = props;
return (
<ChakraSelect.Control { ...rest }>
<ChakraSelect.Trigger ref={ ref }>{ children }</ChakraSelect.Trigger>
<ChakraSelect.IndicatorGroup>
{ clearable && <SelectClearTrigger/> }
<ChakraSelect.Indicator/>
</ChakraSelect.IndicatorGroup>
</ChakraSelect.Control>
);
});
const SelectClearTrigger = React.forwardRef<
HTMLButtonElement,
ChakraSelect.ClearTriggerProps
>(function SelectClearTrigger(props, ref) {
return (
<ChakraSelect.ClearTrigger asChild { ...props } ref={ ref }>
<CloseButton
variant="plain"
focusVisibleRing="inside"
focusRingWidth="2px"
pointerEvents="auto"
/>
</ChakraSelect.ClearTrigger>
);
});
interface SelectContentProps extends ChakraSelect.ContentProps {
portalled?: boolean;
portalRef?: React.RefObject<HTMLElement>;
}
export const SelectContent = React.forwardRef<
HTMLDivElement,
SelectContentProps
>(function SelectContent(props, ref) {
const { portalled = true, portalRef, ...rest } = props;
return (
<Portal disabled={ !portalled } container={ portalRef }>
<ChakraSelect.Positioner>
<ChakraSelect.Content { ...rest } ref={ ref }/>
</ChakraSelect.Positioner>
</Portal>
);
});
export const SelectItem = React.forwardRef<
HTMLDivElement,
ChakraSelect.ItemProps
>(function SelectItem(props, ref) {
const { item, children, ...rest } = props;
return (
<ChakraSelect.Item key={ item.value } item={ item } { ...rest } ref={ ref }>
{ children }
<ChakraSelect.ItemIndicator/>
</ChakraSelect.Item>
);
});
interface SelectValueTextProps
extends Omit<ChakraSelect.ValueTextProps, 'children'> {
children?(items: Array<CollectionItem>): React.ReactNode;
}
export const SelectValueText = React.forwardRef<
HTMLSpanElement,
SelectValueTextProps
>(function SelectValueText(props, ref) {
const { children, ...rest } = props;
return (
<ChakraSelect.ValueText { ...rest } ref={ ref }>
<ChakraSelect.Context>
{ (select) => {
const items = select.selectedItems;
if (items.length === 0) return props.placeholder;
if (children) return children(items);
if (items.length === 1)
return select.collection.stringifyItem(items[0]);
return `${ items.length } selected`;
} }
</ChakraSelect.Context>
</ChakraSelect.ValueText>
);
});
export const SelectRoot = React.forwardRef<
HTMLDivElement,
ChakraSelect.RootProps
>(function SelectRoot(props, ref) {
return (
<ChakraSelect.Root
{ ...props }
ref={ ref }
positioning={{ sameWidth: true, ...props.positioning }}
>
{ props.asChild ? (
props.children
) : (
<>
<ChakraSelect.HiddenSelect/>
{ props.children }
</>
) }
</ChakraSelect.Root>
);
}) as ChakraSelect.RootComponent;
interface SelectItemGroupProps extends ChakraSelect.ItemGroupProps {
label: React.ReactNode;
}
export const SelectItemGroup = React.forwardRef<
HTMLDivElement,
SelectItemGroupProps
>(function SelectItemGroup(props, ref) {
const { children, label, ...rest } = props;
return (
<ChakraSelect.ItemGroup { ...rest } ref={ ref }>
<ChakraSelect.ItemGroupLabel>{ label }</ChakraSelect.ItemGroupLabel>
{ children }
</ChakraSelect.ItemGroup>
);
});
export const SelectLabel = ChakraSelect.Label;
export const SelectItemText = ChakraSelect.ItemText;
import type { TokenDefinition } from '@chakra-ui/react/dist/types/styled-system/types'; import type { ThemingConfig } from '@chakra-ui/react';
const colors: TokenDefinition['colors'] = { import type { ExcludeUndefined } from 'types/utils';
const colors: ExcludeUndefined<ThemingConfig['tokens']>['colors'] = {
green: { green: {
'100': { value: '#C6F6D5' }, '100': { value: '#C6F6D5' },
'400': { value: '#48BB78' }, '400': { value: '#48BB78' },
......
import type { TokenDefinition } from '@chakra-ui/react/dist/types/styled-system/types'; import type { ThemingConfig } from '@chakra-ui/react';
const durations: TokenDefinition['durations'] = { import type { ExcludeUndefined } from 'types/utils';
const durations: ExcludeUndefined<ThemingConfig['tokens']>['durations'] = {
'ultra-fast': { value: '50ms' }, 'ultra-fast': { value: '50ms' },
faster: { value: '100ms' }, faster: { value: '100ms' },
fast: { value: '150ms' }, fast: { value: '150ms' },
......
...@@ -171,6 +171,23 @@ const semanticTokens: ThemingConfig['semanticTokens'] = { ...@@ -171,6 +171,23 @@ const semanticTokens: ThemingConfig['semanticTokens'] = {
DEFAULT: { value: { _light: '{colors.blackAlpha.800}', _dark: '{colors.whiteAlpha.800}' } }, DEFAULT: { value: { _light: '{colors.blackAlpha.800}', _dark: '{colors.whiteAlpha.800}' } },
}, },
}, },
drawer: {
bg: {
DEFAULT: { value: { _light: '{colors.white}', _dark: '{colors.gray.900}' } },
},
},
select: {
fg: {
DEFAULT: { value: { _light: '{colors.blackAlpha.800}', _dark: '{colors.whiteAlpha.800}' } },
hover: { value: '{colors.blue.400}' },
error: { value: '{colors.red.500}' },
},
border: {
DEFAULT: { value: { _light: '{colors.gray.300}', _dark: '{colors.gray.600}' } },
hover: { value: '{colors.blue.400}' },
error: { value: '{colors.red.500}' },
},
},
spinner: { spinner: {
track: { track: {
DEFAULT: { value: { _light: '{colors.blackAlpha.200}', _dark: '{colors.whiteAlpha.200}' } }, DEFAULT: { value: { _light: '{colors.blackAlpha.200}', _dark: '{colors.whiteAlpha.200}' } },
...@@ -236,10 +253,10 @@ const semanticTokens: ThemingConfig['semanticTokens'] = { ...@@ -236,10 +253,10 @@ const semanticTokens: ThemingConfig['semanticTokens'] = {
}, },
shadows: { shadows: {
popover: { popover: {
DEFAULT: { value: { DEFAULT: { value: { _light: '{shadows.size.2xl}', _dark: '{shadows.dark-lg}' } },
_light: '{shadows.size.2xl}', },
_dark: '0px 15px 40px 0px rgba(0, 0, 0, 0.4), 0px 5px 10px 0px rgba(0, 0, 0, 0.2), 0px 0px 0px 1px rgba(0, 0, 0, 0.1)', drawer: {
} }, DEFAULT: { value: { _light: '{shadows.size.lg}', _dark: '{shadows.dark-lg}' } },
}, },
}, },
}; };
......
...@@ -13,6 +13,7 @@ const shadows: ExcludeUndefined<ThemingConfig['tokens']>['shadows'] = { ...@@ -13,6 +13,7 @@ const shadows: ExcludeUndefined<ThemingConfig['tokens']>['shadows'] = {
xl: { value: '0px 10px 10px -5px rgba(0, 0, 0, 0.04), 0px 20px 25px -5px rgba(0, 0, 0, 0.1)' }, xl: { value: '0px 10px 10px -5px rgba(0, 0, 0, 0.04), 0px 20px 25px -5px rgba(0, 0, 0, 0.1)' },
'2xl': { value: '0px 15px 50px -12px rgba(0, 0, 0, 0.25)' }, '2xl': { value: '0px 15px 50px -12px rgba(0, 0, 0, 0.25)' },
}, },
'dark-lg': { value: '0px 15px 40px 0px rgba(0, 0, 0, 0.4), 0px 5px 10px 0px rgba(0, 0, 0, 0.2), 0px 0px 0px 1px rgba(0, 0, 0, 0.1)' },
}; };
export default shadows; export default shadows;
...@@ -120,7 +120,9 @@ export const recipe = defineSlotRecipe({ ...@@ -120,7 +120,9 @@ export const recipe = defineSlotRecipe({
overflow: 'hidden', overflow: 'hidden',
}, },
content: { content: {
minH: 'auto', // source code has minH: 'auto', but I am not sure why
// anyway it will override the minH from the "full" size variant
// minH: 'auto',
maxH: 'calc(100% - 7.5rem)', maxH: 'calc(100% - 7.5rem)',
borderRadius: 'xl', borderRadius: 'xl',
}, },
......
import { defineSlotRecipe } from '@chakra-ui/react';
export const recipe = defineSlotRecipe({
slots: [ 'root', 'backdrop', 'trigger', 'content', 'header', 'body', 'footer', 'title', 'description', 'positioner' ],
className: 'chakra-drawer',
base: {
backdrop: {
bg: 'blackAlpha.800',
pos: 'fixed',
insetInlineStart: 0,
top: 0,
w: '100vw',
h: '100dvh',
zIndex: 'modal',
_open: {
animationName: 'fade-in',
animationDuration: 'slow',
},
_closed: {
animationName: 'fade-out',
animationDuration: 'moderate',
},
},
positioner: {
display: 'flex',
width: '100vw',
height: '100dvh',
position: 'fixed',
insetInlineStart: 0,
top: 0,
zIndex: 'modal',
overscrollBehaviorY: 'none',
},
content: {
display: 'flex',
flexDirection: 'column',
position: 'relative',
width: '100%',
outline: 0,
zIndex: 'modal',
textStyle: 'sm',
maxH: '100dvh',
color: 'inherit',
bg: 'drawer.bg',
boxShadow: 'drawer',
_open: {
animationDuration: 'slowest',
animationTimingFunction: 'ease-in-smooth',
},
_closed: {
animationDuration: 'slower',
animationTimingFunction: 'ease-in-smooth',
},
},
header: {
flex: 0,
px: '6',
pt: '6',
pb: '4',
},
body: {
p: '6',
flex: '1',
overflow: 'auto',
},
footer: {
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
gap: '3',
px: '6',
pt: '2',
pb: '4',
},
title: {
textStyle: 'lg',
fontWeight: 'semibold',
},
description: {
color: 'fg.muted',
},
},
variants: {
size: {
md: {
content: {
maxW: '300px',
},
},
},
placement: {
start: {
positioner: {
justifyContent: 'flex-start',
},
content: {
_open: {
animationName: {
base: 'slide-from-left-full, fade-in',
_rtl: 'slide-from-right-full, fade-in',
},
},
_closed: {
animationName: {
base: 'slide-to-left-full, fade-out',
_rtl: 'slide-to-right-full, fade-out',
},
},
},
},
end: {
positioner: {
justifyContent: 'flex-end',
},
content: {
_open: {
animationName: {
base: 'slide-from-right-full, fade-in',
_rtl: 'slide-from-left-full, fade-in',
},
},
_closed: {
animationName: {
base: 'slide-to-right-full, fade-out',
_rtl: 'slide-to-right-full, fade-out',
},
},
},
},
top: {
positioner: {
alignItems: 'flex-start',
},
content: {
maxW: '100%',
_open: { animationName: 'slide-from-top-full, fade-in' },
_closed: { animationName: 'slide-to-top-full, fade-out' },
},
},
bottom: {
positioner: {
alignItems: 'flex-end',
},
content: {
maxW: '100%',
_open: { animationName: 'slide-from-bottom-full, fade-in' },
_closed: { animationName: 'slide-to-bottom-full, fade-out' },
},
},
},
contained: {
'true': {
positioner: {
padding: '4',
},
content: {
borderRadius: 'l3',
},
},
},
},
defaultVariants: {
size: 'md',
placement: 'end',
},
});
...@@ -2,12 +2,15 @@ import { recipe as alert } from './alert.recipe'; ...@@ -2,12 +2,15 @@ import { recipe as alert } from './alert.recipe';
import { recipe as button } from './button.recipe'; import { recipe as button } from './button.recipe';
import { recipe as closeButton } from './close-button.recipe'; import { recipe as closeButton } from './close-button.recipe';
import { recipe as dialog } from './dialog.recipe'; import { recipe as dialog } from './dialog.recipe';
import { recipe as drawer } from './drawer.recipe';
import { recipe as field } from './field.recipe'; import { recipe as field } from './field.recipe';
import { recipe as input } from './input.recipe'; import { recipe as input } from './input.recipe';
import { recipe as link } from './link.recipe'; import { recipe as link } from './link.recipe';
import { recipe as nativeSelect } from './native-select.recipe';
import { recipe as pinInput } from './pin-input.recipe'; import { recipe as pinInput } from './pin-input.recipe';
import { recipe as popover } from './popover.recipe'; import { recipe as popover } from './popover.recipe';
import { recipe as progressCircle } from './progress-circle.recipe'; import { recipe as progressCircle } from './progress-circle.recipe';
import { recipe as select } from './select.recipe';
import { recipe as skeleton } from './skeleton.recipe'; import { recipe as skeleton } from './skeleton.recipe';
import { recipe as spinner } from './spinner.recipe'; import { recipe as spinner } from './spinner.recipe';
import { recipe as switchRecipe } from './switch.recipe'; import { recipe as switchRecipe } from './switch.recipe';
...@@ -29,10 +32,13 @@ export const recipes = { ...@@ -29,10 +32,13 @@ export const recipes = {
export const slotRecipes = { export const slotRecipes = {
alert, alert,
dialog, dialog,
drawer,
field, field,
nativeSelect,
pinInput, pinInput,
popover, popover,
progressCircle, progressCircle,
select,
'switch': switchRecipe, 'switch': switchRecipe,
tabs, tabs,
toast, toast,
......
import { defineSlotRecipe } from '@chakra-ui/react';
import { recipe as selectSlotRecipe } from './select.recipe';
// TODO @tom2drum check sizes for native select
export const recipe = defineSlotRecipe({
slots: [ 'root', 'field', 'indicator' ],
base: {
root: {
height: 'fit-content',
display: 'flex',
width: '100%',
position: 'relative',
},
field: {
width: '100%',
minWidth: '0',
outline: '0',
appearance: 'none',
borderRadius: 'base',
_disabled: {
layerStyle: 'disabled',
},
_invalid: {
borderColor: 'border.error',
},
focusVisibleRing: 'none',
lineHeight: 'normal',
'& > option, & > optgroup': {
bg: 'inherit',
},
fontWeight: '500',
},
indicator: {
position: 'absolute',
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
pointerEvents: 'none',
top: '50%',
transform: 'translateY(-50%)',
height: '100%',
color: 'inherit',
_disabled: {
opacity: '0.5',
},
_invalid: {
color: 'select.fg.error',
},
_icon: {
width: '1em',
height: '1em',
},
},
},
variants: {
variant: {
outline: {
field: selectSlotRecipe.variants?.variant.outline.trigger,
},
},
size: {
xs: {
field: {
textStyle: 'xs',
ps: '2',
pe: '6',
height: '6',
},
indicator: {
textStyle: 'sm',
insetEnd: '1.5',
},
},
sm: {
field: {
textStyle: 'sm',
ps: '2.5',
pe: '8',
height: '8',
},
indicator: {
textStyle: 'md',
insetEnd: '2',
},
},
md: {
field: {
textStyle: 'sm',
ps: '3',
pe: '8',
height: '10',
},
indicator: {
textStyle: 'lg',
insetEnd: '2',
},
},
lg: {
field: {
textStyle: 'md',
ps: '4',
pe: '8',
height: '11',
},
indicator: {
textStyle: 'xl',
insetEnd: '3',
},
},
xl: {
field: {
textStyle: 'md',
ps: '4.5',
pe: '10',
height: '12',
},
indicator: {
textStyle: 'xl',
insetEnd: '3',
},
},
},
},
defaultVariants: selectSlotRecipe.defaultVariants,
});
import { defineSlotRecipe } from '@chakra-ui/react';
// TODO @tom2drum check sizes for select
export const recipe = defineSlotRecipe({
slots: [ 'root', 'trigger', 'indicatorGroup', 'indicator', 'content', 'item', 'control', 'itemText', 'itemGroup', 'itemGroupLabel', 'label', 'valueText' ],
base: {
root: {
display: 'flex',
flexDirection: 'column',
gap: '1.5',
width: 'full',
},
trigger: {
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
width: 'full',
minH: 'var(--select-trigger-height)',
px: 'var(--select-trigger-padding-x)',
borderRadius: 'base',
userSelect: 'none',
textAlign: 'start',
fontWeight: '500',
focusVisibleRing: 'none',
_placeholderShown: {
color: 'gray.500',
},
_disabled: {
layerStyle: 'disabled',
},
_invalid: {
borderColor: 'select.border.error',
},
},
indicatorGroup: {
display: 'flex',
alignItems: 'center',
gap: '1',
pos: 'absolute',
right: '0',
top: '0',
bottom: '0',
px: 'var(--select-trigger-padding-x)',
pointerEvents: 'none',
},
indicator: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: 'inherit',
},
content: {
background: 'popover.bg',
display: 'flex',
flexDirection: 'column',
zIndex: 'dropdown',
borderRadius: 'base',
outline: 0,
maxH: '96',
overflowY: 'auto',
boxShadow: 'md',
_open: {
animationStyle: 'slide-fade-in',
animationDuration: 'fast',
},
_closed: {
animationStyle: 'slide-fade-out',
animationDuration: 'fastest',
},
},
item: {
position: 'relative',
userSelect: 'none',
display: 'flex',
alignItems: 'center',
gap: '2',
cursor: 'option',
justifyContent: 'space-between',
flex: '1',
textAlign: 'start',
borderRadius: 'base',
_highlighted: {
bg: 'bg.emphasized/60',
},
_disabled: {
pointerEvents: 'none',
opacity: '0.5',
},
_icon: {
width: '4',
height: '4',
},
},
control: {
pos: 'relative',
},
itemText: {
flex: '1',
},
itemGroup: {
_first: { mt: '0' },
},
itemGroupLabel: {
py: '1',
fontWeight: 'medium',
},
label: {
fontWeight: 'medium',
userSelect: 'none',
textStyle: 'sm',
_disabled: {
layerStyle: 'disabled',
},
},
valueText: {
lineClamp: '1',
maxW: '80%',
},
},
variants: {
variant: {
outline: {
trigger: {
bg: 'transparent',
borderWidth: '2px',
borderColor: 'select.border',
_expanded: {
borderColor: 'select.border.hover',
},
_hover: {
borderColor: 'select.border.hover',
},
_focusVisible: {
borderColor: 'select.border.hover',
focusVisibleRing: 'none',
},
},
},
},
size: {
xs: {
root: {
'--select-trigger-height': 'sizes.8',
'--select-trigger-padding-x': 'spacing.2',
},
content: {
p: '1',
gap: '1',
textStyle: 'xs',
},
trigger: {
textStyle: 'xs',
gap: '1',
},
item: {
py: '1',
px: '2',
},
itemGroupLabel: {
py: '1',
px: '2',
},
indicator: {
_icon: {
width: '3.5',
height: '3.5',
},
},
},
sm: {
root: {
'--select-trigger-height': 'sizes.9',
'--select-trigger-padding-x': 'spacing.2.5',
},
content: {
p: '1',
textStyle: 'sm',
},
trigger: {
textStyle: 'sm',
gap: '1',
},
indicator: {
_icon: {
width: '4',
height: '4',
},
},
item: {
py: '1',
px: '1.5',
},
itemGroup: {
mt: '1',
},
itemGroupLabel: {
py: '1',
px: '1.5',
},
},
md: {
root: {
'--select-trigger-height': 'sizes.10',
'--select-trigger-padding-x': 'spacing.3',
},
content: {
p: '1',
textStyle: 'sm',
},
itemGroup: {
mt: '1.5',
},
item: {
py: '1.5',
px: '2',
},
itemIndicator: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
},
itemGroupLabel: {
py: '1.5',
px: '2',
},
trigger: {
textStyle: 'sm',
gap: '2',
},
indicator: {
_icon: {
width: '4',
height: '4',
},
},
},
lg: {
root: {
'--select-trigger-height': 'sizes.12',
'--select-trigger-padding-x': 'spacing.4',
},
content: {
p: '1.5',
textStyle: 'md',
},
itemGroup: {
mt: '2',
},
item: {
py: '2',
px: '3',
},
itemGroupLabel: {
py: '2',
px: '3',
},
trigger: {
textStyle: 'md',
py: '3',
gap: '2',
},
indicator: {
_icon: {
width: '5',
height: '5',
},
},
},
},
},
defaultVariants: {
size: 'md',
variant: 'outline',
},
});
/* eslint-disable max-len */ /* eslint-disable max-len */
/* eslint-disable react/jsx-no-bind */ /* eslint-disable react/jsx-no-bind */
import { Heading, HStack, Link, Spinner, Tabs, VStack } from '@chakra-ui/react'; import { createListCollection, Heading, HStack, Link, Spinner, Tabs, VStack } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { Alert } from 'toolkit/chakra/alert'; import { Alert } from 'toolkit/chakra/alert';
...@@ -9,8 +9,10 @@ import { useColorMode } from 'toolkit/chakra/color-mode'; ...@@ -9,8 +9,10 @@ import { useColorMode } from 'toolkit/chakra/color-mode';
import { Field } from 'toolkit/chakra/field'; import { Field } from 'toolkit/chakra/field';
import { Input } from 'toolkit/chakra/input'; import { Input } from 'toolkit/chakra/input';
import { InputGroup } from 'toolkit/chakra/input-group'; import { InputGroup } from 'toolkit/chakra/input-group';
import { NativeSelectField, NativeSelectRoot } from 'toolkit/chakra/native-select';
import { PinInput } from 'toolkit/chakra/pin-input'; import { PinInput } from 'toolkit/chakra/pin-input';
import { ProgressCircleRing, ProgressCircleRoot } from 'toolkit/chakra/progress-circle'; import { ProgressCircleRing, ProgressCircleRoot } from 'toolkit/chakra/progress-circle';
import { SelectContent, SelectItem, SelectRoot, SelectTrigger, SelectValueText } from 'toolkit/chakra/select';
import { Skeleton } from 'toolkit/chakra/skeleton'; import { Skeleton } from 'toolkit/chakra/skeleton';
import { Switch } from 'toolkit/chakra/switch'; import { Switch } from 'toolkit/chakra/switch';
import { Textarea } from 'toolkit/chakra/textarea'; import { Textarea } from 'toolkit/chakra/textarea';
...@@ -25,6 +27,15 @@ const TEXT = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do ei ...@@ -25,6 +27,15 @@ const TEXT = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do ei
const ChakraShowcases = () => { const ChakraShowcases = () => {
const colorMode = useColorMode(); const colorMode = useColorMode();
const frameworks = createListCollection({
items: [
{ label: 'React.js', value: 'react' },
{ label: 'Vue.js', value: 'vue' },
{ label: 'Angular', value: 'angular' },
{ label: 'Svelte', value: 'svelte' },
],
});
return ( return (
<> <>
<PageTitle title="Chakra UI Showcase"/> <PageTitle title="Chakra UI Showcase"/>
...@@ -53,39 +64,39 @@ const ChakraShowcases = () => { ...@@ -53,39 +64,39 @@ const ChakraShowcases = () => {
<section> <section>
<Heading textStyle="heading.md" mb={ 2 }>Inputs</Heading> <Heading textStyle="heading.md" mb={ 2 }>Inputs</Heading>
<Heading textStyle="heading.sm" mb={ 2 }>Regular</Heading> <Heading textStyle="heading.sm" mb={ 2 }>Regular</Heading>
<HStack gap={ 4 } whiteSpace="nowrap"> <HStack gap={ 4 } whiteSpace="nowrap" flexWrap="wrap">
<Field label="Email" required> <Field label="Email" required maxWidth="300px">
<Input type="email"/> <Input type="email"/>
</Field> </Field>
<Field label="Email"> <Field label="Email" maxWidth="300px">
<Input value="me@example.com"/> <Input value="me@example.com"/>
</Field> </Field>
<Field label="Email" invalid> <Field label="Email" invalid maxWidth="300px">
<Input value="duck"/> <Input value="duck"/>
</Field> </Field>
<Field label="Email" readOnly> <Field label="Email" readOnly maxWidth="300px">
<Input value="duck"/> <Input value="duck"/>
</Field> </Field>
<Field label="Email" disabled> <Field label="Email" disabled maxWidth="300px">
<Input value="duck"/> <Input value="duck"/>
</Field> </Field>
</HStack> </HStack>
<HStack gap={ 4 } whiteSpace="nowrap" mt={ 4 } alignItems="flex-start"> <HStack gap={ 4 } whiteSpace="nowrap" mt={ 4 } alignItems="flex-start" flexWrap="wrap">
<Field label="Email" required size="sm"> <Field label="Email" required size="sm" maxWidth="300px">
<Input/> <Input/>
</Field> </Field>
<Field label="Email" required size="md"> <Field label="Email" required size="md" maxWidth="300px">
<Input/> <Input/>
</Field> </Field>
<Field label="Email" required size="lg"> <Field label="Email" required size="lg" maxWidth="300px">
<Input/> <Input/>
</Field> </Field>
<Field label="Email" required size="xl"> <Field label="Email" required size="xl" maxWidth="300px">
<Input/> <Input/>
</Field> </Field>
</HStack> </HStack>
<Heading textStyle="heading.sm" mb={ 2 } mt={ 6 }>Floating (only XL size)</Heading> <Heading textStyle="heading.sm" mb={ 2 } mt={ 6 }>Floating (only XL size)</Heading>
<HStack gap={ 4 } mt={ 4 } alignItems="flex-start"> <HStack gap={ 4 } mt={ 4 } alignItems="flex-start" flexWrap="wrap">
<Field label="Email" required floating size="xl" helperText="Helper text" w="300px"> <Field label="Email" required floating size="xl" helperText="Helper text" w="300px">
<Input type="email"/> <Input type="email"/>
</Field> </Field>
...@@ -93,7 +104,7 @@ const ChakraShowcases = () => { ...@@ -93,7 +104,7 @@ const ChakraShowcases = () => {
<Input type="email"/> <Input type="email"/>
</Field> </Field>
</HStack> </HStack>
<HStack p={ 6 } mt={ 4 } gap={ 4 } bgColor={{ _light: 'blackAlpha.200', _dark: 'whiteAlpha.200' }} > <HStack p={ 6 } mt={ 4 } gap={ 4 } bgColor={{ _light: 'blackAlpha.200', _dark: 'whiteAlpha.200' }} flexWrap="wrap">
<Field label="Email" required floating size="xl" w="300px"> <Field label="Email" required floating size="xl" w="300px">
<Input type="email"/> <Input type="email"/>
</Field> </Field>
...@@ -105,7 +116,7 @@ const ChakraShowcases = () => { ...@@ -105,7 +116,7 @@ const ChakraShowcases = () => {
</Field> </Field>
</HStack> </HStack>
<Heading textStyle="heading.sm" mb={ 2 } mt={ 6 }>Input group</Heading> <Heading textStyle="heading.sm" mb={ 2 } mt={ 6 }>Input group</Heading>
<HStack gap={ 4 } mt={ 4 } alignItems="flex-start" w="fit-content"> <HStack gap={ 4 } mt={ 4 } alignItems="flex-start" w="fit-content" flexWrap="wrap">
<Field label="Referral code" required floating size="xl" w="300px" flexShrink={ 0 } helperText="Helper text"> <Field label="Referral code" required floating size="xl" w="300px" flexShrink={ 0 } helperText="Helper text">
<InputGroup endElement={ <IconSvg name="copy" boxSize={ 5 }/> }> <InputGroup endElement={ <IconSvg name="copy" boxSize={ 5 }/> }>
<Input/> <Input/>
...@@ -124,11 +135,11 @@ const ChakraShowcases = () => { ...@@ -124,11 +135,11 @@ const ChakraShowcases = () => {
<section> <section>
<Heading textStyle="heading.md" mb={ 2 }>Textarea</Heading> <Heading textStyle="heading.md" mb={ 2 }>Textarea</Heading>
<HStack gap={ 4 }> <HStack gap={ 4 } flexWrap="wrap">
<Field label="Description" required floating size="2xl" w="400px"> <Field label="Description" required floating size="2xl" w="360px">
<Textarea/> <Textarea/>
</Field> </Field>
<Field label="Description" required floating size="2xl" w="400px"> <Field label="Description" required floating size="2xl" w="360px">
<Textarea value={ TEXT }/> <Textarea value={ TEXT }/>
</Field> </Field>
</HStack> </HStack>
...@@ -136,7 +147,7 @@ const ChakraShowcases = () => { ...@@ -136,7 +147,7 @@ const ChakraShowcases = () => {
<section> <section>
<Heading textStyle="heading.md" mb={ 2 }>Links</Heading> <Heading textStyle="heading.md" mb={ 2 }>Links</Heading>
<HStack gap={ 4 }> <HStack gap={ 4 } flexWrap="wrap">
<Link>Primary</Link> <Link>Primary</Link>
<Link visual="secondary">Secondary</Link> <Link visual="secondary">Secondary</Link>
<Link visual="subtle">Subtle</Link> <Link visual="subtle">Subtle</Link>
...@@ -204,12 +215,12 @@ const ChakraShowcases = () => { ...@@ -204,12 +215,12 @@ const ChakraShowcases = () => {
<section> <section>
<Heading textStyle="heading.md" mb={ 2 }>Alerts</Heading> <Heading textStyle="heading.md" mb={ 2 }>Alerts</Heading>
<HStack gap={ 4 } whiteSpace="nowrap"> <HStack gap={ 4 } whiteSpace="nowrap" flexWrap="wrap">
<Alert visual="info" title="Info"> Alert content </Alert> <Alert visual="info" title="Info" maxWidth="300px"> Alert content </Alert>
<Alert visual="neutral" title="Neutral"> Alert content </Alert> <Alert visual="neutral" title="Neutral" maxWidth="300px"> Alert content </Alert>
<Alert visual="warning" title="Warning"> Alert content </Alert> <Alert visual="warning" title="Warning" maxWidth="300px"> Alert content </Alert>
<Alert visual="success" title="Success"> Alert content </Alert> <Alert visual="success" title="Success" maxWidth="300px"> Alert content </Alert>
<Alert visual="error" title="Error" startElement={ null }> Alert content </Alert> <Alert visual="error" title="Error" startElement={ null } maxWidth="300px"> Alert content </Alert>
</HStack> </HStack>
</section> </section>
...@@ -219,6 +230,30 @@ const ChakraShowcases = () => { ...@@ -219,6 +230,30 @@ const ChakraShowcases = () => {
<Button onClick={ () => toaster.success({ title: 'Success', description: 'Toast content' }) }>Success</Button> <Button onClick={ () => toaster.success({ title: 'Success', description: 'Toast content' }) }>Success</Button>
</HStack> </HStack>
</section> </section>
<section>
<Heading textStyle="heading.md" mb={ 2 }>Select</Heading>
<HStack gap={ 4 } whiteSpace="nowrap" flexWrap="wrap">
<SelectRoot collection={ frameworks }>
<SelectTrigger w="350px">
<SelectValueText placeholder="Select framework"/>
</SelectTrigger>
<SelectContent>
{ frameworks.items.map((framework) => (
<SelectItem item={ framework } key={ framework.value }>
{ framework.label }
</SelectItem>
)) }
</SelectContent>
</SelectRoot>
<NativeSelectRoot w="350px">
<NativeSelectField>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
</NativeSelectField>
</NativeSelectRoot>
</HStack>
</section>
</VStack> </VStack>
</> </>
); );
......
...@@ -41,7 +41,7 @@ const RewardsButton = ({ visual = 'header', size }: Props) => { ...@@ -41,7 +41,7 @@ const RewardsButton = ({ visual = 'header', size }: Props) => {
onFocus={ handleFocus } onFocus={ handleFocus }
fontSize="sm" fontSize="sm"
size={ size } size={ size }
px={ !isLoading && Boolean(apiToken) ? 2.5 : 4 } px={{ base: '10px', lg: !isLoading && Boolean(apiToken) ? 2.5 : 4 }}
loading={ isLoading } loading={ isLoading }
_hover={{ _hover={{
textDecoration: 'none', textDecoration: 'none',
......
...@@ -25,7 +25,7 @@ const ContentLoader = ({ className, text }: Props) => { ...@@ -25,7 +25,7 @@ const ContentLoader = ({ className, text }: Props) => {
borderRadius: 'full', borderRadius: 'full',
}} }}
/> />
<Text mt={ 6 } variant="secondary"> <Text mt={ 6 } color="text.secondary">
{ text || 'Loading data, please wait...' } { text || 'Loading data, please wait...' }
</Text> </Text>
</Box> </Box>
......
...@@ -57,7 +57,7 @@ const GasInfoTooltip = ({ children, data, dataUpdatedAt, isOpen, placement }: Pr ...@@ -57,7 +57,7 @@ const GasInfoTooltip = ({ children, data, dataUpdatedAt, isOpen, placement }: Pr
<GasInfoTooltipRow name="Normal" info={ data.gas_prices.average }/> <GasInfoTooltipRow name="Normal" info={ data.gas_prices.average }/>
<GasInfoTooltipRow name="Slow" info={ data.gas_prices.slow }/> <GasInfoTooltipRow name="Slow" info={ data.gas_prices.slow }/>
</Grid> </Grid>
<LinkInternal href={ route({ pathname: '/gas-tracker' }) }> <LinkInternal href={ route({ pathname: '/gas-tracker' }) } className="dark">
Gas tracker overview Gas tracker overview
</LinkInternal> </LinkInternal>
</Flex> </Flex>
...@@ -67,11 +67,10 @@ const GasInfoTooltip = ({ children, data, dataUpdatedAt, isOpen, placement }: Pr ...@@ -67,11 +67,10 @@ const GasInfoTooltip = ({ children, data, dataUpdatedAt, isOpen, placement }: Pr
<Tooltip <Tooltip
content={ content } content={ content }
positioning={{ placement }} positioning={{ placement }}
open={ isOpen } { ...(isOpen ? { open: true } : { }) }
lazyMount lazyMount
interactive interactive
showArrow={ false } showArrow={ false }
// TODO @tom2drum forced light mode doesn't work for now
contentProps={{ p: 4, borderRadius: 'md', className: 'light' }} contentProps={{ p: 4, borderRadius: 'md', className: 'light' }}
> >
{ children } { children }
......
...@@ -14,16 +14,16 @@ interface Props { ...@@ -14,16 +14,16 @@ interface Props {
const GasInfoTooltipRow = ({ name, info }: Props) => { const GasInfoTooltipRow = ({ name, info }: Props) => {
return ( return (
<> <>
<Box> <Box textAlign="left">
<chakra.span>{ name }</chakra.span> <chakra.span>{ name }</chakra.span>
{ info && typeof info.time === 'number' && info.time > 0 && ( { info && typeof info.time === 'number' && info.time > 0 && (
<chakra.span color="text_secondary"> <chakra.span color="text.secondary">
{ space }{ (info.time / 1000).toLocaleString(undefined, { maximumFractionDigits: 1 }) }s { space }{ (info.time / 1000).toLocaleString(undefined, { maximumFractionDigits: 1 }) }s
</chakra.span> </chakra.span>
) } ) }
</Box> </Box>
<GasPrice data={ info } textAlign="right"/> <GasPrice data={ info } textAlign="right"/>
<GasPrice data={ info } unitMode="secondary" color="text_secondary" textAlign="right"/> <GasPrice data={ info } unitMode="secondary" color="text.secondary" textAlign="right"/>
</> </>
); );
}; };
......
...@@ -4,7 +4,6 @@ import type { Props } from './types'; ...@@ -4,7 +4,6 @@ import type { Props } from './types';
import AppErrorBoundary from 'ui/shared/AppError/AppErrorBoundary'; import AppErrorBoundary from 'ui/shared/AppError/AppErrorBoundary';
import HeaderAlert from 'ui/snippets/header/HeaderAlert'; import HeaderAlert from 'ui/snippets/header/HeaderAlert';
// TODO @tom2drum fix main layout
import HeaderDesktop from 'ui/snippets/header/HeaderDesktop'; import HeaderDesktop from 'ui/snippets/header/HeaderDesktop';
import HeaderMobile from 'ui/snippets/header/HeaderMobile'; import HeaderMobile from 'ui/snippets/header/HeaderMobile';
...@@ -15,7 +14,7 @@ const LayoutDefault = ({ children }: Props) => { ...@@ -15,7 +14,7 @@ const LayoutDefault = ({ children }: Props) => {
<Layout.Container> <Layout.Container>
<Layout.TopRow/> <Layout.TopRow/>
<Layout.NavBar/> <Layout.NavBar/>
{ /* <HeaderMobile/> */ } <HeaderMobile/>
<Layout.MainArea> <Layout.MainArea>
<Layout.SideBar/> <Layout.SideBar/>
<Layout.MainColumn> <Layout.MainColumn>
......
import { Box, Flex, Drawer, useDisclosure } from '@chakra-ui/react'; import { Box, Flex } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { useColorModeValue } from 'toolkit/chakra/color-mode';
import config from 'configs/app'; import config from 'configs/app';
import { DrawerBackdrop, DrawerBody, DrawerContent, DrawerRoot, DrawerTrigger } from 'toolkit/chakra/drawer';
import { IconButton } from 'toolkit/chakra/icon-button';
import { useDisclosure } from 'toolkit/hooks/useDisclosure';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import NavigationMobile from 'ui/snippets/navigation/mobile/NavigationMobile'; import NavigationMobile from 'ui/snippets/navigation/mobile/NavigationMobile';
import TestnetBadge from 'ui/snippets/navigation/TestnetBadge'; import TestnetBadge from 'ui/snippets/navigation/TestnetBadge';
...@@ -16,8 +18,7 @@ interface Props { ...@@ -16,8 +18,7 @@ interface Props {
} }
const Burger = ({ isMarketplaceAppPage }: Props) => { const Burger = ({ isMarketplaceAppPage }: Props) => {
const iconColor = useColorModeValue('gray.600', 'white'); const { open, onOpen, onClose, onOpenChange } = useDisclosure();
const { isOpen, onOpen, onClose } = useDisclosure();
const networkMenu = useNetworkMenu(); const networkMenu = useNetworkMenu();
const handleNetworkMenuButtonClick = React.useCallback(() => { const handleNetworkMenuButtonClick = React.useCallback(() => {
...@@ -25,49 +26,48 @@ const Burger = ({ isMarketplaceAppPage }: Props) => { ...@@ -25,49 +26,48 @@ const Burger = ({ isMarketplaceAppPage }: Props) => {
}, [ networkMenu ]); }, [ networkMenu ]);
const handleNetworkLogoClick = React.useCallback((event: React.SyntheticEvent) => { const handleNetworkLogoClick = React.useCallback((event: React.SyntheticEvent) => {
networkMenu.isOpen && event.preventDefault(); networkMenu.open && event.preventDefault();
networkMenu.onClose(); networkMenu.onClose();
}, [ networkMenu ]); }, [ networkMenu ]);
return ( return (
<> <DrawerRoot
<Box padding={ 2 } onClick={ onOpen } cursor="pointer"> open={ open }
<IconSvg onOpenChange={ onOpenChange }
name="burger" placement="start"
boxSize={ 6 } >
display="block" <DrawerBackdrop/>
color={ iconColor } <DrawerTrigger>
aria-label="Menu button" <IconButton onClick={ onOpen } p={ 2 }>
/> <IconSvg
</Box> name="burger"
<Drawer.Root boxSize={ 6 }
isOpen={ isOpen } display="block"
placement="left" color={{ _light: 'gray.600', _dark: 'white' }}
onClose={ onClose } aria-label="Menu button"
autoFocus={ false } />
> </IconButton>
<Drawer.Backdrop/> </DrawerTrigger>
<Drawer.Content maxWidth="330px"> <DrawerContent >
<Drawer.Body p={ 6 } display="flex" flexDirection="column"> <DrawerBody display="flex" flexDirection="column" overflow="hidden">
<TestnetBadge alignSelf="flex-start"/> <TestnetBadge alignSelf="flex-start"/>
<Flex alignItems="center" justifyContent="space-between"> <Flex alignItems="center" justifyContent="space-between">
<NetworkLogo onClick={ handleNetworkLogoClick }/> <NetworkLogo onClick={ handleNetworkLogoClick }/>
{ config.UI.navigation.featuredNetworks ? ( { config.UI.navigation.featuredNetworks ? (
<NetworkMenuButton <NetworkMenuButton
isMobile w={ 9 }
isActive={ networkMenu.isOpen } isActive={ networkMenu.open }
onClick={ handleNetworkMenuButtonClick } onClick={ handleNetworkMenuButtonClick }
/> />
) : <Box boxSize={ 9 }/> } ) : <Box boxSize={ 9 }/> }
</Flex> </Flex>
{ networkMenu.isOpen ? { networkMenu.open ?
<NetworkMenuContentMobile tabs={ networkMenu.availableTabs } items={ networkMenu.data }/> : <NetworkMenuContentMobile tabs={ networkMenu.availableTabs } items={ networkMenu.data }/> :
<NavigationMobile onNavLinkClick={ onClose } isMarketplaceAppPage={ isMarketplaceAppPage }/> <NavigationMobile onNavLinkClick={ onClose } isMarketplaceAppPage={ isMarketplaceAppPage }/>
} }
</Drawer.Body> </DrawerBody>
</Drawer.Content> </DrawerContent>
</Drawer.Root> </DrawerRoot>
</>
); );
}; };
......
import { Box, Flex, useColorModeValue } from '@chakra-ui/react'; import { Box, Flex } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { useInView } from 'react-intersection-observer'; import { useInView } from 'react-intersection-observer';
...@@ -18,7 +18,6 @@ type Props = { ...@@ -18,7 +18,6 @@ type Props = {
}; };
const HeaderMobile = ({ hideSearchBar, renderSearchBar }: Props) => { const HeaderMobile = ({ hideSearchBar, renderSearchBar }: Props) => {
const bgColor = useColorModeValue('white', 'black');
const scrollDirection = useScrollDirection(); const scrollDirection = useScrollDirection();
const { ref, inView } = useInView({ threshold: 1 }); const { ref, inView } = useInView({ threshold: 1 });
...@@ -27,7 +26,7 @@ const HeaderMobile = ({ hideSearchBar, renderSearchBar }: Props) => { ...@@ -27,7 +26,7 @@ const HeaderMobile = ({ hideSearchBar, renderSearchBar }: Props) => {
return ( return (
<Box <Box
ref={ ref } ref={ ref }
bgColor={ bgColor } bgColor={{ _light: 'white', _dark: 'black' }}
display={{ base: 'block', lg: 'none' }} display={{ base: 'block', lg: 'none' }}
position="sticky" position="sticky"
top="-1px" top="-1px"
...@@ -39,7 +38,7 @@ const HeaderMobile = ({ hideSearchBar, renderSearchBar }: Props) => { ...@@ -39,7 +38,7 @@ const HeaderMobile = ({ hideSearchBar, renderSearchBar }: Props) => {
as="header" as="header"
paddingX={ 3 } paddingX={ 3 }
paddingY={ 2 } paddingY={ 2 }
bgColor={ bgColor } bgColor={{ _light: 'white', _dark: 'black' }}
width="100%" width="100%"
alignItems="center" alignItems="center"
transitionProperty="box-shadow" transitionProperty="box-shadow"
......
...@@ -33,9 +33,10 @@ const NavLinkGroup = ({ item, onClick, isExpanded }: Props) => { ...@@ -33,9 +33,10 @@ const NavLinkGroup = ({ item, onClick, isExpanded }: Props) => {
w="100%" w="100%"
px={ 2 } px={ 2 }
aria-label={ `${ item.text } link group` } aria-label={ `${ item.text } link group` }
bgColor={ item.isActive ? 'link.navigation.bg.selected' : 'link.navigation.bg' }
> >
<Flex justifyContent="space-between" width="100%" alignItems="center" pr={ 1 }> <Flex justifyContent="space-between" width="100%" alignItems="center" pr={ 1 }>
<HStack spacing={ 0 } overflow="hidden"> <HStack gap={ 0 } overflow="hidden">
<NavLinkIcon item={ item }/> <NavLinkIcon item={ item }/>
<Text <Text
{ ...styleProps.textProps } { ...styleProps.textProps }
...@@ -43,7 +44,7 @@ const NavLinkGroup = ({ item, onClick, isExpanded }: Props) => { ...@@ -43,7 +44,7 @@ const NavLinkGroup = ({ item, onClick, isExpanded }: Props) => {
> >
{ item.text } { item.text }
</Text> </Text>
{ isHighlighted && (<LightningLabel iconColor={ styleProps.itemProps.bgColor }/>) } { isHighlighted && (<LightningLabel iconColor={ item.isActive ? 'link.navigation.bg.selected' : 'link.navigation.bg' }/>) }
</HStack> </HStack>
<IconSvg name="arrows/east-mini" transform="rotate(180deg)" boxSize={ 6 }/> <IconSvg name="arrows/east-mini" transform="rotate(180deg)" boxSize={ 6 }/>
</Flex> </Flex>
......
import { Box, Flex, Text, VStack } from '@chakra-ui/react'; import { Box, Flex, Text, VStack } from '@chakra-ui/react';
// import { animate, motion, useMotionValue } from 'framer-motion';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import { useColorModeValue } from 'toolkit/chakra/color-mode';
import useNavItems, { isGroupItem } from 'lib/hooks/useNavItems'; import useNavItems, { isGroupItem } from 'lib/hooks/useNavItems';
import { useColorModeValue } from 'toolkit/chakra/color-mode';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import useIsAuth from 'ui/snippets/auth/useIsAuth'; import useIsAuth from 'ui/snippets/auth/useIsAuth';
...@@ -11,7 +10,7 @@ import NavLink from '../vertical/NavLink'; ...@@ -11,7 +10,7 @@ import NavLink from '../vertical/NavLink';
import NavLinkRewards from '../vertical/NavLinkRewards'; import NavLinkRewards from '../vertical/NavLinkRewards';
import NavLinkGroup from './NavLinkGroup'; import NavLinkGroup from './NavLinkGroup';
// const DRAWER_WIDTH = 330; const ANIMATION_DURATION = 300;
interface Props { interface Props {
onNavLinkClick?: () => void; onNavLinkClick?: () => void;
...@@ -19,25 +18,34 @@ interface Props { ...@@ -19,25 +18,34 @@ interface Props {
} }
const NavigationMobile = ({ onNavLinkClick, isMarketplaceAppPage }: Props) => { const NavigationMobile = ({ onNavLinkClick, isMarketplaceAppPage }: Props) => {
const timeoutRef = React.useRef<number | null>(null);
const { mainNavItems, accountNavItems } = useNavItems(); const { mainNavItems, accountNavItems } = useNavItems();
const [ openedGroupIndex, setOpenedGroupIndex ] = React.useState(-1); const [ openedGroupIndex, setOpenedGroupIndex ] = React.useState(-1);
const [ isOpen, setIsOpen ] = React.useState(false);
// const mainX = useMotionValue(0);
// const subX = useMotionValue(DRAWER_WIDTH);
const onGroupItemOpen = (index: number) => () => { const onGroupItemOpen = (index: number) => () => {
setOpenedGroupIndex(index); setOpenedGroupIndex(index);
// TODO @tom2drum animate navigation group menu timeoutRef.current = window.setTimeout(() => {
// animate(mainX, -DRAWER_WIDTH, { ease: 'easeInOut' }); setIsOpen(true);
// animate(subX, 0, { ease: 'easeInOut' }); }, 100);
}; };
const onGroupItemClose = useCallback(() => { const onGroupItemClose = useCallback(() => {
// animate(mainX, 0, { ease: 'easeInOut' }); setIsOpen(false);
// animate(subX, DRAWER_WIDTH, { ease: 'easeInOut', onComplete: () => setOpenedGroupIndex(-1) }); timeoutRef.current = window.setTimeout(() => {
setOpenedGroupIndex(-1);
}, ANIMATION_DURATION);
}, []); }, []);
React.useEffect(() => {
return () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
};
}, [ ]);
const isAuth = useIsAuth(); const isAuth = useIsAuth();
const iconColor = useColorModeValue('blue.600', 'blue.300'); const iconColor = useColorModeValue('blue.600', 'blue.300');
...@@ -52,8 +60,8 @@ const NavigationMobile = ({ onNavLinkClick, isMarketplaceAppPage }: Props) => { ...@@ -52,8 +60,8 @@ const NavigationMobile = ({ onNavLinkClick, isMarketplaceAppPage }: Props) => {
display="flex" display="flex"
flexDirection="column" flexDirection="column"
flexGrow={ 1 } flexGrow={ 1 }
// as={ motion.div } transform={ isOpen ? 'translateX(calc(-100% - 24px))' : 'translateX(0)' }
// style={{ x: mainX }} transition={ `transform ${ ANIMATION_DURATION }ms ease-in-out` }
maxHeight={ openedGroupIndex > -1 ? '100vh' : 'unset' } maxHeight={ openedGroupIndex > -1 ? '100vh' : 'unset' }
overflowY={ openedGroupIndex > -1 ? 'hidden' : 'unset' } overflowY={ openedGroupIndex > -1 ? 'hidden' : 'unset' }
> >
...@@ -64,7 +72,7 @@ const NavigationMobile = ({ onNavLinkClick, isMarketplaceAppPage }: Props) => { ...@@ -64,7 +72,7 @@ const NavigationMobile = ({ onNavLinkClick, isMarketplaceAppPage }: Props) => {
<VStack <VStack
w="100%" w="100%"
as="ul" as="ul"
spacing="1" gap="1"
alignItems="flex-start" alignItems="flex-start"
> >
{ mainNavItems.map((item, index) => { { mainNavItems.map((item, index) => {
...@@ -84,55 +92,53 @@ const NavigationMobile = ({ onNavLinkClick, isMarketplaceAppPage }: Props) => { ...@@ -84,55 +92,53 @@ const NavigationMobile = ({ onNavLinkClick, isMarketplaceAppPage }: Props) => {
borderTopWidth="1px" borderTopWidth="1px"
borderColor="border.divider" borderColor="border.divider"
> >
<VStack as="ul" spacing="1" alignItems="flex-start"> <VStack as="ul" gap="1" alignItems="flex-start">
<NavLinkRewards onClick={ onNavLinkClick } isCollapsed={ isCollapsed }/> <NavLinkRewards onClick={ onNavLinkClick } isCollapsed={ isCollapsed }/>
{ accountNavItems.map((item) => <NavLink key={ item.text } item={ item } onClick={ onNavLinkClick } isCollapsed={ isCollapsed }/>) } { accountNavItems.map((item) => <NavLink key={ item.text } item={ item } onClick={ onNavLinkClick } isCollapsed={ isCollapsed }/>) }
</VStack> </VStack>
</Box> </Box>
) } ) }
</Box> </Box>
{ openedGroupIndex >= 0 && ( <Box
key="sub"
w="100%"
mt={ 6 }
position="absolute"
top={ 0 }
left={ isOpen ? 0 : 'calc(100% + 24px)' }
transition={ `left ${ ANIMATION_DURATION }ms ease-in-out` }
>
<Flex alignItems="center" px={ 2 } py={ 2.5 } w="100%" h="50px" onClick={ onGroupItemClose } mb={ 1 }>
<IconSvg name="arrows/east-mini" boxSize={ 6 } mr={ 2 } color={ iconColor }/>
<Text color="text.secondary" fontSize="sm">{ mainNavItems[openedGroupIndex]?.text }</Text>
</Flex>
<Box <Box
// as={ motion.nav }
w="100%" w="100%"
mt={ 6 } as="ul"
position="absolute"
top={ 0 }
// style={{ x: subX }}
key="sub"
> >
<Flex alignItems="center" px={ 2 } py={ 2.5 } w="100%" h="50px" onClick={ onGroupItemClose } mb={ 1 }> { openedItem && isGroupItem(openedItem) && openedItem.subItems?.map(
<IconSvg name="arrows/east-mini" boxSize={ 6 } mr={ 2 } color={ iconColor }/> (item, index) => Array.isArray(item) ? (
<Text variant="secondary" fontSize="sm">{ mainNavItems[openedGroupIndex].text }</Text> <Box
</Flex> key={ index }
<Box w="100%"
w="100%" as="ul"
as="ul" _notLast={{
> mb: 2,
{ isGroupItem(openedItem) && openedItem.subItems?.map( pb: 2,
(item, index) => Array.isArray(item) ? ( borderBottomWidth: '1px',
<Box borderColor: 'border.divider',
key={ index } }}
w="100%" >
as="ul" { item.map(subItem => <NavLink key={ subItem.text } item={ subItem } onClick={ onNavLinkClick } isCollapsed={ isCollapsed }/>) }
_notLast={{ </Box>
mb: 2, ) : (
pb: 2, <Box key={ item.text } mb={ 1 }>
borderBottomWidth: '1px', <NavLink item={ item } onClick={ onNavLinkClick } isCollapsed={ isCollapsed }/>
borderColor: 'border.divider', </Box>
}} ),
> ) }
{ item.map(subItem => <NavLink key={ subItem.text } item={ subItem } onClick={ onNavLinkClick } isCollapsed={ isCollapsed }/>) }
</Box>
) : (
<Box key={ item.text } mb={ 1 }>
<NavLink item={ item } onClick={ onNavLinkClick } isCollapsed={ isCollapsed }/>
</Box>
),
) }
</Box>
</Box> </Box>
) } </Box>
</Flex> </Flex>
); );
}; };
......
...@@ -6,7 +6,7 @@ type Props = { ...@@ -6,7 +6,7 @@ type Props = {
isActive?: boolean; isActive?: boolean;
}; };
export default function useNavLinkProps({ isExpanded, isCollapsed, isActive }: Props) { export default function useNavLinkStyleProps({ isExpanded, isCollapsed, isActive }: Props) {
return { return {
itemProps: { itemProps: {
visual: 'navigation' as const, visual: 'navigation' as const,
......
...@@ -5,13 +5,12 @@ import { IconButton } from 'toolkit/chakra/icon-button'; ...@@ -5,13 +5,12 @@ import { IconButton } from 'toolkit/chakra/icon-button';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
interface Props { interface Props {
isMobile?: boolean;
isActive?: boolean; isActive?: boolean;
onClick?: () => void; onClick?: () => void;
className?: string; className?: string;
} }
const NetworkMenuButton = ({ isMobile, isActive, onClick, className }: Props, ref: React.ForwardedRef<HTMLButtonElement>) => { const NetworkMenuButton = ({ isActive, onClick, className }: Props, ref: React.ForwardedRef<HTMLButtonElement>) => {
return ( return (
<IconButton <IconButton
className={ className } className={ className }
...@@ -21,7 +20,7 @@ const NetworkMenuButton = ({ isMobile, isActive, onClick, className }: Props, re ...@@ -21,7 +20,7 @@ const NetworkMenuButton = ({ isMobile, isActive, onClick, className }: Props, re
ref={ ref } ref={ ref }
h={ 9 } h={ 9 }
borderRadius="base" borderRadius="base"
backgroundColor={ isActive ? { _light: 'blue.50', _dark: 'gray.400' } : 'none' } backgroundColor={ isActive ? { _light: 'blue.50', _dark: 'gray.800' } : 'transparent' }
onClick={ onClick } onClick={ onClick }
aria-label="Network menu" aria-label="Network menu"
aria-roledescription="menu" aria-roledescription="menu"
...@@ -30,7 +29,7 @@ const NetworkMenuButton = ({ isMobile, isActive, onClick, className }: Props, re ...@@ -30,7 +29,7 @@ const NetworkMenuButton = ({ isMobile, isActive, onClick, className }: Props, re
name="networks" name="networks"
boxSize={ 4 } boxSize={ 4 }
color={ isActive ? { _light: 'blue.700', _dark: 'blue.50' } : { _light: 'gray.600', _dark: 'gray.400' } } color={ isActive ? { _light: 'blue.700', _dark: 'blue.50' } : { _light: 'gray.600', _dark: 'gray.400' } }
_hover={{ color: isMobile ? undefined : 'link_hovered' }} _hover={{ color: 'link.hovered' }}
cursor="pointer" cursor="pointer"
/> />
</IconButton> </IconButton>
......
import { Box, Select, VStack, Flex } from '@chakra-ui/react'; import { Box, VStack, Flex } from '@chakra-ui/react';
import { capitalize } from 'es-toolkit'; import { capitalize } from 'es-toolkit';
import React from 'react'; import React from 'react';
import type { NetworkGroup, FeaturedNetwork } from 'types/networks'; import type { NetworkGroup, FeaturedNetwork } from 'types/networks';
import Skeleton from 'ui/shared/chakra/Skeleton'; import { NativeSelectField, NativeSelectRoot } from 'toolkit/chakra/native-select';
import { Skeleton } from 'toolkit/chakra/skeleton';
import NetworkMenuLink from './NetworkMenuLink'; import NetworkMenuLink from './NetworkMenuLink';
...@@ -24,7 +25,7 @@ const NetworkMenuContentMobile = ({ items, tabs }: Props) => { ...@@ -24,7 +25,7 @@ const NetworkMenuContentMobile = ({ items, tabs }: Props) => {
}, [ items, selectedNetwork?.group, tabs ]); }, [ items, selectedNetwork?.group, tabs ]);
const handleSelectChange = React.useCallback((event: React.ChangeEvent<HTMLSelectElement>) => { const handleSelectChange = React.useCallback((event: React.ChangeEvent<HTMLSelectElement>) => {
setSelectedTab(event.target.value as NetworkGroup); setSelectedTab(event.currentTarget.value as NetworkGroup);
}, []); }, []);
const content = !items || items.length === 0 ? ( const content = !items || items.length === 0 ? (
...@@ -45,11 +46,13 @@ const NetworkMenuContentMobile = ({ items, tabs }: Props) => { ...@@ -45,11 +46,13 @@ const NetworkMenuContentMobile = ({ items, tabs }: Props) => {
) : ( ) : (
<> <>
{ tabs.length > 1 && ( { tabs.length > 1 && (
<Select size="xs" borderRadius="base" value={ selectedTab } onChange={ handleSelectChange } mb={ 3 }> <NativeSelectRoot size="sm" borderRadius="base" mb={ 3 }>
{ tabs.map((tab) => <option key={ tab } value={ tab }>{ capitalize(tab) }</option>) } <NativeSelectField value={ selectedTab } onChange={ handleSelectChange }>
</Select> { tabs.map((tab) => <option key={ tab } value={ tab }>{ capitalize(tab) }</option>) }
</NativeSelectField>
</NativeSelectRoot>
) } ) }
<VStack as="ul" spacing={ 2 } alignItems="stretch"> <VStack as="ul" gap={ 2 } alignItems="stretch">
{ items { items
.filter(({ group }) => group === selectedTab) .filter(({ group }) => group === selectedTab)
.map((network) => ( .map((network) => (
......
import { Box, Text, Image, chakra } from '@chakra-ui/react'; import { Box, Text, chakra } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { FeaturedNetwork } from 'types/networks'; import type { FeaturedNetwork } from 'types/networks';
import { useColorModeValue } from 'toolkit/chakra/color-mode'; import { useColorModeValue } from 'toolkit/chakra/color-mode';
import { Image } from 'toolkit/chakra/image';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
interface Props extends FeaturedNetwork { interface Props extends FeaturedNetwork {
......
...@@ -126,6 +126,7 @@ const SearchBar = ({ isHomepage }: Props) => { ...@@ -126,6 +126,7 @@ const SearchBar = ({ isHomepage }: Props) => {
open={ open && (searchTerm.trim().length > 0 || recentSearchKeywords.length > 0) } open={ open && (searchTerm.trim().length > 0 || recentSearchKeywords.length > 0) }
autoFocus={ false } autoFocus={ false }
onOpenChange={ handleOpenChange } onOpenChange={ handleOpenChange }
// TODO @tom2drum fix positioning on mobile
positioning={{ offset: isMobile && !isHomepage ? { mainAxis: -4, crossAxis: 12 } : { mainAxis: 8, crossAxis: 0 } }} positioning={{ offset: isMobile && !isHomepage ? { mainAxis: -4, crossAxis: 12 } : { mainAxis: 8, crossAxis: 0 } }}
lazyMount lazyMount
closeOnInteractOutside={ false } closeOnInteractOutside={ false }
......
...@@ -33,7 +33,7 @@ const TopBar = () => { ...@@ -33,7 +33,7 @@ const TopBar = () => {
) } ) }
<Settings/> <Settings/>
{ config.UI.navigation.layout === 'horizontal' && Boolean(config.UI.navigation.featuredNetworks) && ( { config.UI.navigation.layout === 'horizontal' && Boolean(config.UI.navigation.featuredNetworks) && (
<Box display={{ base: 'none', lg: 'flex' }}> <Box display={{ base: 'none', lg: 'flex' }} alignItems="center">
<Separator mx={ 3 } height={ 4 } orientation="vertical"/> <Separator mx={ 3 } height={ 4 } orientation="vertical"/>
<NetworkMenu/> <NetworkMenu/>
</Box> </Box>
......
import { Drawer, DrawerBody, DrawerContent, DrawerOverlay, useDisclosure } from '@chakra-ui/react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
...@@ -7,6 +6,8 @@ import type { Screen } from 'ui/snippets/auth/types'; ...@@ -7,6 +6,8 @@ import type { Screen } from 'ui/snippets/auth/types';
import config from 'configs/app'; import config from 'configs/app';
import * as mixpanel from 'lib/mixpanel'; import * as mixpanel from 'lib/mixpanel';
import useAccount from 'lib/web3/useAccount'; import useAccount from 'lib/web3/useAccount';
import { DrawerBackdrop, DrawerBody, DrawerContent, DrawerRoot, DrawerTrigger } from 'toolkit/chakra/drawer';
import { useDisclosure } from 'toolkit/hooks/useDisclosure';
import AuthModal from 'ui/snippets/auth/AuthModal'; import AuthModal from 'ui/snippets/auth/AuthModal';
import useProfileQuery from 'ui/snippets/auth/useProfileQuery'; import useProfileQuery from 'ui/snippets/auth/useProfileQuery';
...@@ -56,35 +57,41 @@ const UserProfileMobile = () => { ...@@ -56,35 +57,41 @@ const UserProfileMobile = () => {
authModal.onClose(); authModal.onClose();
}, [ authModal ]); }, [ authModal ]);
const handleDrawerOpenChange = React.useCallback(({ open }: { open: boolean }) => {
if (profileQuery.data || web3Address) {
open ? profileMenu.onOpen() : profileMenu.onClose();
} else {
authModal.onOpen();
}
}, [ profileQuery.data, web3Address, authModal, profileMenu ]);
return ( return (
<> <>
<UserProfileButton <DrawerRoot
profileQuery={ profileQuery } open={ profileMenu.open }
variant="header" onOpenChange={ handleDrawerOpenChange }
onClick={ handleProfileButtonClick } >
/> <DrawerBackdrop/>
{ (profileQuery.data || web3Address) && ( <DrawerTrigger>
<Drawer <UserProfileButton
isOpen={ profileMenu.isOpen } profileQuery={ profileQuery }
placement="right" visual="header"
onClose={ profileMenu.onClose } onClick={ handleProfileButtonClick }
autoFocus={ false } />
> </DrawerTrigger>
<DrawerOverlay/> <DrawerContent>
<DrawerContent maxWidth="300px"> <DrawerBody>
<DrawerBody p={ 6 }> <UserProfileContent
<UserProfileContent data={ profileQuery.data }
data={ profileQuery.data } onClose={ profileMenu.onClose }
onClose={ profileMenu.onClose } onLogin={ authModal.onOpen }
onLogin={ authModal.onOpen } onAddEmail={ handleAddEmailClick }
onAddEmail={ handleAddEmailClick } onAddAddress={ handleAddAddressClick }
onAddAddress={ handleAddAddressClick } />
/> </DrawerBody>
</DrawerBody> </DrawerContent>
</DrawerContent> </DrawerRoot>
</Drawer> { authModal.open && (
) }
{ authModal.isOpen && (
<AuthModal <AuthModal
onClose={ handleAuthModalClose } onClose={ handleAuthModalClose }
initialScreen={ authInitialScreen } initialScreen={ authInitialScreen }
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment