Commit 0eb4ffd8 authored by tom's avatar tom

fix MobileHeader component

parent 06404103
......@@ -25,11 +25,13 @@ const RESTRICTED_MODULES = {
{ 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: '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',
importNames: [
'Menu', 'useToast', 'useDisclosure', 'useClipboard', 'Tooltip', 'Skeleton', 'IconButton', 'Button',
'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',
},
......
......@@ -37,7 +37,7 @@ export const DrawerCloseTrigger = React.forwardRef<
{ ...props }
asChild
>
<CloseButton size="sm" ref={ ref }/>
<CloseButton ref={ ref }/>
</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<
HTMLButtonElement,
ChakraPopover.TriggerProps
>(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;
......
'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: {
'100': { value: '#C6F6D5' },
'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' },
faster: { value: '100ms' },
fast: { value: '150ms' },
......
......@@ -171,6 +171,23 @@ const semanticTokens: ThemingConfig['semanticTokens'] = {
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: {
track: {
DEFAULT: { value: { _light: '{colors.blackAlpha.200}', _dark: '{colors.whiteAlpha.200}' } },
......@@ -236,10 +253,10 @@ const semanticTokens: ThemingConfig['semanticTokens'] = {
},
shadows: {
popover: {
DEFAULT: { value: {
_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)',
} },
DEFAULT: { value: { _light: '{shadows.size.2xl}', _dark: '{shadows.dark-lg}' } },
},
drawer: {
DEFAULT: { value: { _light: '{shadows.size.lg}', _dark: '{shadows.dark-lg}' } },
},
},
};
......
......@@ -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)' },
'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;
......@@ -120,7 +120,9 @@ export const recipe = defineSlotRecipe({
overflow: 'hidden',
},
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)',
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';
import { recipe as button } from './button.recipe';
import { recipe as closeButton } from './close-button.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 input } from './input.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 popover } from './popover.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 spinner } from './spinner.recipe';
import { recipe as switchRecipe } from './switch.recipe';
......@@ -29,10 +32,13 @@ export const recipes = {
export const slotRecipes = {
alert,
dialog,
drawer,
field,
nativeSelect,
pinInput,
popover,
progressCircle,
select,
'switch': switchRecipe,
tabs,
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 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 { Alert } from 'toolkit/chakra/alert';
......@@ -9,8 +9,10 @@ import { useColorMode } from 'toolkit/chakra/color-mode';
import { Field } from 'toolkit/chakra/field';
import { Input } from 'toolkit/chakra/input';
import { InputGroup } from 'toolkit/chakra/input-group';
import { NativeSelectField, NativeSelectRoot } from 'toolkit/chakra/native-select';
import { PinInput } from 'toolkit/chakra/pin-input';
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 { Switch } from 'toolkit/chakra/switch';
import { Textarea } from 'toolkit/chakra/textarea';
......@@ -25,6 +27,15 @@ const TEXT = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do ei
const ChakraShowcases = () => {
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 (
<>
<PageTitle title="Chakra UI Showcase"/>
......@@ -53,39 +64,39 @@ const ChakraShowcases = () => {
<section>
<Heading textStyle="heading.md" mb={ 2 }>Inputs</Heading>
<Heading textStyle="heading.sm" mb={ 2 }>Regular</Heading>
<HStack gap={ 4 } whiteSpace="nowrap">
<Field label="Email" required>
<HStack gap={ 4 } whiteSpace="nowrap" flexWrap="wrap">
<Field label="Email" required maxWidth="300px">
<Input type="email"/>
</Field>
<Field label="Email">
<Field label="Email" maxWidth="300px">
<Input value="me@example.com"/>
</Field>
<Field label="Email" invalid>
<Field label="Email" invalid maxWidth="300px">
<Input value="duck"/>
</Field>
<Field label="Email" readOnly>
<Field label="Email" readOnly maxWidth="300px">
<Input value="duck"/>
</Field>
<Field label="Email" disabled>
<Field label="Email" disabled maxWidth="300px">
<Input value="duck"/>
</Field>
</HStack>
<HStack gap={ 4 } whiteSpace="nowrap" mt={ 4 } alignItems="flex-start">
<Field label="Email" required size="sm">
<HStack gap={ 4 } whiteSpace="nowrap" mt={ 4 } alignItems="flex-start" flexWrap="wrap">
<Field label="Email" required size="sm" maxWidth="300px">
<Input/>
</Field>
<Field label="Email" required size="md">
<Field label="Email" required size="md" maxWidth="300px">
<Input/>
</Field>
<Field label="Email" required size="lg">
<Field label="Email" required size="lg" maxWidth="300px">
<Input/>
</Field>
<Field label="Email" required size="xl">
<Field label="Email" required size="xl" maxWidth="300px">
<Input/>
</Field>
</HStack>
<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">
<Input type="email"/>
</Field>
......@@ -93,7 +104,7 @@ const ChakraShowcases = () => {
<Input type="email"/>
</Field>
</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">
<Input type="email"/>
</Field>
......@@ -105,7 +116,7 @@ const ChakraShowcases = () => {
</Field>
</HStack>
<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">
<InputGroup endElement={ <IconSvg name="copy" boxSize={ 5 }/> }>
<Input/>
......@@ -124,11 +135,11 @@ const ChakraShowcases = () => {
<section>
<Heading textStyle="heading.md" mb={ 2 }>Textarea</Heading>
<HStack gap={ 4 }>
<Field label="Description" required floating size="2xl" w="400px">
<HStack gap={ 4 } flexWrap="wrap">
<Field label="Description" required floating size="2xl" w="360px">
<Textarea/>
</Field>
<Field label="Description" required floating size="2xl" w="400px">
<Field label="Description" required floating size="2xl" w="360px">
<Textarea value={ TEXT }/>
</Field>
</HStack>
......@@ -136,7 +147,7 @@ const ChakraShowcases = () => {
<section>
<Heading textStyle="heading.md" mb={ 2 }>Links</Heading>
<HStack gap={ 4 }>
<HStack gap={ 4 } flexWrap="wrap">
<Link>Primary</Link>
<Link visual="secondary">Secondary</Link>
<Link visual="subtle">Subtle</Link>
......@@ -204,12 +215,12 @@ const ChakraShowcases = () => {
<section>
<Heading textStyle="heading.md" mb={ 2 }>Alerts</Heading>
<HStack gap={ 4 } whiteSpace="nowrap">
<Alert visual="info" title="Info"> Alert content </Alert>
<Alert visual="neutral" title="Neutral"> Alert content </Alert>
<Alert visual="warning" title="Warning"> Alert content </Alert>
<Alert visual="success" title="Success"> Alert content </Alert>
<Alert visual="error" title="Error" startElement={ null }> Alert content </Alert>
<HStack gap={ 4 } whiteSpace="nowrap" flexWrap="wrap">
<Alert visual="info" title="Info" maxWidth="300px"> Alert content </Alert>
<Alert visual="neutral" title="Neutral" maxWidth="300px"> Alert content </Alert>
<Alert visual="warning" title="Warning" maxWidth="300px"> Alert content </Alert>
<Alert visual="success" title="Success" maxWidth="300px"> Alert content </Alert>
<Alert visual="error" title="Error" startElement={ null } maxWidth="300px"> Alert content </Alert>
</HStack>
</section>
......@@ -219,6 +230,30 @@ const ChakraShowcases = () => {
<Button onClick={ () => toaster.success({ title: 'Success', description: 'Toast content' }) }>Success</Button>
</HStack>
</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>
</>
);
......
......@@ -41,7 +41,7 @@ const RewardsButton = ({ visual = 'header', size }: Props) => {
onFocus={ handleFocus }
fontSize="sm"
size={ size }
px={ !isLoading && Boolean(apiToken) ? 2.5 : 4 }
px={{ base: '10px', lg: !isLoading && Boolean(apiToken) ? 2.5 : 4 }}
loading={ isLoading }
_hover={{
textDecoration: 'none',
......
......@@ -25,7 +25,7 @@ const ContentLoader = ({ className, text }: Props) => {
borderRadius: 'full',
}}
/>
<Text mt={ 6 } variant="secondary">
<Text mt={ 6 } color="text.secondary">
{ text || 'Loading data, please wait...' }
</Text>
</Box>
......
......@@ -57,7 +57,7 @@ const GasInfoTooltip = ({ children, data, dataUpdatedAt, isOpen, placement }: Pr
<GasInfoTooltipRow name="Normal" info={ data.gas_prices.average }/>
<GasInfoTooltipRow name="Slow" info={ data.gas_prices.slow }/>
</Grid>
<LinkInternal href={ route({ pathname: '/gas-tracker' }) }>
<LinkInternal href={ route({ pathname: '/gas-tracker' }) } className="dark">
Gas tracker overview
</LinkInternal>
</Flex>
......@@ -67,11 +67,10 @@ const GasInfoTooltip = ({ children, data, dataUpdatedAt, isOpen, placement }: Pr
<Tooltip
content={ content }
positioning={{ placement }}
open={ isOpen }
{ ...(isOpen ? { open: true } : { }) }
lazyMount
interactive
showArrow={ false }
// TODO @tom2drum forced light mode doesn't work for now
contentProps={{ p: 4, borderRadius: 'md', className: 'light' }}
>
{ children }
......
......@@ -14,16 +14,16 @@ interface Props {
const GasInfoTooltipRow = ({ name, info }: Props) => {
return (
<>
<Box>
<Box textAlign="left">
<chakra.span>{ name }</chakra.span>
{ 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
</chakra.span>
) }
</Box>
<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';
import AppErrorBoundary from 'ui/shared/AppError/AppErrorBoundary';
import HeaderAlert from 'ui/snippets/header/HeaderAlert';
// TODO @tom2drum fix main layout
import HeaderDesktop from 'ui/snippets/header/HeaderDesktop';
import HeaderMobile from 'ui/snippets/header/HeaderMobile';
......@@ -15,7 +14,7 @@ const LayoutDefault = ({ children }: Props) => {
<Layout.Container>
<Layout.TopRow/>
<Layout.NavBar/>
{ /* <HeaderMobile/> */ }
<HeaderMobile/>
<Layout.MainArea>
<Layout.SideBar/>
<Layout.MainColumn>
......
import { Box, Flex, Drawer, useDisclosure } from '@chakra-ui/react';
import { Box, Flex } from '@chakra-ui/react';
import React from 'react';
import { useColorModeValue } from 'toolkit/chakra/color-mode';
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 NavigationMobile from 'ui/snippets/navigation/mobile/NavigationMobile';
import TestnetBadge from 'ui/snippets/navigation/TestnetBadge';
......@@ -16,8 +18,7 @@ interface Props {
}
const Burger = ({ isMarketplaceAppPage }: Props) => {
const iconColor = useColorModeValue('gray.600', 'white');
const { isOpen, onOpen, onClose } = useDisclosure();
const { open, onOpen, onClose, onOpenChange } = useDisclosure();
const networkMenu = useNetworkMenu();
const handleNetworkMenuButtonClick = React.useCallback(() => {
......@@ -25,49 +26,48 @@ const Burger = ({ isMarketplaceAppPage }: Props) => {
}, [ networkMenu ]);
const handleNetworkLogoClick = React.useCallback((event: React.SyntheticEvent) => {
networkMenu.isOpen && event.preventDefault();
networkMenu.open && event.preventDefault();
networkMenu.onClose();
}, [ networkMenu ]);
return (
<>
<Box padding={ 2 } onClick={ onOpen } cursor="pointer">
<IconSvg
name="burger"
boxSize={ 6 }
display="block"
color={ iconColor }
aria-label="Menu button"
/>
</Box>
<Drawer.Root
isOpen={ isOpen }
placement="left"
onClose={ onClose }
autoFocus={ false }
>
<Drawer.Backdrop/>
<Drawer.Content maxWidth="330px">
<Drawer.Body p={ 6 } display="flex" flexDirection="column">
<TestnetBadge alignSelf="flex-start"/>
<Flex alignItems="center" justifyContent="space-between">
<NetworkLogo onClick={ handleNetworkLogoClick }/>
{ config.UI.navigation.featuredNetworks ? (
<NetworkMenuButton
isMobile
isActive={ networkMenu.isOpen }
onClick={ handleNetworkMenuButtonClick }
/>
) : <Box boxSize={ 9 }/> }
</Flex>
{ networkMenu.isOpen ?
<NetworkMenuContentMobile tabs={ networkMenu.availableTabs } items={ networkMenu.data }/> :
<NavigationMobile onNavLinkClick={ onClose } isMarketplaceAppPage={ isMarketplaceAppPage }/>
}
</Drawer.Body>
</Drawer.Content>
</Drawer.Root>
</>
<DrawerRoot
open={ open }
onOpenChange={ onOpenChange }
placement="start"
>
<DrawerBackdrop/>
<DrawerTrigger>
<IconButton onClick={ onOpen } p={ 2 }>
<IconSvg
name="burger"
boxSize={ 6 }
display="block"
color={{ _light: 'gray.600', _dark: 'white' }}
aria-label="Menu button"
/>
</IconButton>
</DrawerTrigger>
<DrawerContent >
<DrawerBody display="flex" flexDirection="column" overflow="hidden">
<TestnetBadge alignSelf="flex-start"/>
<Flex alignItems="center" justifyContent="space-between">
<NetworkLogo onClick={ handleNetworkLogoClick }/>
{ config.UI.navigation.featuredNetworks ? (
<NetworkMenuButton
w={ 9 }
isActive={ networkMenu.open }
onClick={ handleNetworkMenuButtonClick }
/>
) : <Box boxSize={ 9 }/> }
</Flex>
{ networkMenu.open ?
<NetworkMenuContentMobile tabs={ networkMenu.availableTabs } items={ networkMenu.data }/> :
<NavigationMobile onNavLinkClick={ onClose } isMarketplaceAppPage={ isMarketplaceAppPage }/>
}
</DrawerBody>
</DrawerContent>
</DrawerRoot>
);
};
......
import { Box, Flex, useColorModeValue } from '@chakra-ui/react';
import { Box, Flex } from '@chakra-ui/react';
import React from 'react';
import { useInView } from 'react-intersection-observer';
......@@ -18,7 +18,6 @@ type Props = {
};
const HeaderMobile = ({ hideSearchBar, renderSearchBar }: Props) => {
const bgColor = useColorModeValue('white', 'black');
const scrollDirection = useScrollDirection();
const { ref, inView } = useInView({ threshold: 1 });
......@@ -27,7 +26,7 @@ const HeaderMobile = ({ hideSearchBar, renderSearchBar }: Props) => {
return (
<Box
ref={ ref }
bgColor={ bgColor }
bgColor={{ _light: 'white', _dark: 'black' }}
display={{ base: 'block', lg: 'none' }}
position="sticky"
top="-1px"
......@@ -39,7 +38,7 @@ const HeaderMobile = ({ hideSearchBar, renderSearchBar }: Props) => {
as="header"
paddingX={ 3 }
paddingY={ 2 }
bgColor={ bgColor }
bgColor={{ _light: 'white', _dark: 'black' }}
width="100%"
alignItems="center"
transitionProperty="box-shadow"
......
......@@ -33,9 +33,10 @@ const NavLinkGroup = ({ item, onClick, isExpanded }: Props) => {
w="100%"
px={ 2 }
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 }>
<HStack spacing={ 0 } overflow="hidden">
<HStack gap={ 0 } overflow="hidden">
<NavLinkIcon item={ item }/>
<Text
{ ...styleProps.textProps }
......@@ -43,7 +44,7 @@ const NavLinkGroup = ({ item, onClick, isExpanded }: Props) => {
>
{ item.text }
</Text>
{ isHighlighted && (<LightningLabel iconColor={ styleProps.itemProps.bgColor }/>) }
{ isHighlighted && (<LightningLabel iconColor={ item.isActive ? 'link.navigation.bg.selected' : 'link.navigation.bg' }/>) }
</HStack>
<IconSvg name="arrows/east-mini" transform="rotate(180deg)" boxSize={ 6 }/>
</Flex>
......
import { Box, Flex, Text, VStack } from '@chakra-ui/react';
// import { animate, motion, useMotionValue } from 'framer-motion';
import React, { useCallback } from 'react';
import { useColorModeValue } from 'toolkit/chakra/color-mode';
import useNavItems, { isGroupItem } from 'lib/hooks/useNavItems';
import { useColorModeValue } from 'toolkit/chakra/color-mode';
import IconSvg from 'ui/shared/IconSvg';
import useIsAuth from 'ui/snippets/auth/useIsAuth';
......@@ -11,7 +10,7 @@ import NavLink from '../vertical/NavLink';
import NavLinkRewards from '../vertical/NavLinkRewards';
import NavLinkGroup from './NavLinkGroup';
// const DRAWER_WIDTH = 330;
const ANIMATION_DURATION = 300;
interface Props {
onNavLinkClick?: () => void;
......@@ -19,25 +18,34 @@ interface Props {
}
const NavigationMobile = ({ onNavLinkClick, isMarketplaceAppPage }: Props) => {
const timeoutRef = React.useRef<number | null>(null);
const { mainNavItems, accountNavItems } = useNavItems();
const [ openedGroupIndex, setOpenedGroupIndex ] = React.useState(-1);
// const mainX = useMotionValue(0);
// const subX = useMotionValue(DRAWER_WIDTH);
const [ isOpen, setIsOpen ] = React.useState(false);
const onGroupItemOpen = (index: number) => () => {
setOpenedGroupIndex(index);
// TODO @tom2drum animate navigation group menu
// animate(mainX, -DRAWER_WIDTH, { ease: 'easeInOut' });
// animate(subX, 0, { ease: 'easeInOut' });
timeoutRef.current = window.setTimeout(() => {
setIsOpen(true);
}, 100);
};
const onGroupItemClose = useCallback(() => {
// animate(mainX, 0, { ease: 'easeInOut' });
// animate(subX, DRAWER_WIDTH, { ease: 'easeInOut', onComplete: () => setOpenedGroupIndex(-1) });
setIsOpen(false);
timeoutRef.current = window.setTimeout(() => {
setOpenedGroupIndex(-1);
}, ANIMATION_DURATION);
}, []);
React.useEffect(() => {
return () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
};
}, [ ]);
const isAuth = useIsAuth();
const iconColor = useColorModeValue('blue.600', 'blue.300');
......@@ -52,8 +60,8 @@ const NavigationMobile = ({ onNavLinkClick, isMarketplaceAppPage }: Props) => {
display="flex"
flexDirection="column"
flexGrow={ 1 }
// as={ motion.div }
// style={{ x: mainX }}
transform={ isOpen ? 'translateX(calc(-100% - 24px))' : 'translateX(0)' }
transition={ `transform ${ ANIMATION_DURATION }ms ease-in-out` }
maxHeight={ openedGroupIndex > -1 ? '100vh' : 'unset' }
overflowY={ openedGroupIndex > -1 ? 'hidden' : 'unset' }
>
......@@ -64,7 +72,7 @@ const NavigationMobile = ({ onNavLinkClick, isMarketplaceAppPage }: Props) => {
<VStack
w="100%"
as="ul"
spacing="1"
gap="1"
alignItems="flex-start"
>
{ mainNavItems.map((item, index) => {
......@@ -84,55 +92,53 @@ const NavigationMobile = ({ onNavLinkClick, isMarketplaceAppPage }: Props) => {
borderTopWidth="1px"
borderColor="border.divider"
>
<VStack as="ul" spacing="1" alignItems="flex-start">
<VStack as="ul" gap="1" alignItems="flex-start">
<NavLinkRewards onClick={ onNavLinkClick } isCollapsed={ isCollapsed }/>
{ accountNavItems.map((item) => <NavLink key={ item.text } item={ item } onClick={ onNavLinkClick } isCollapsed={ isCollapsed }/>) }
</VStack>
</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
// as={ motion.nav }
w="100%"
mt={ 6 }
position="absolute"
top={ 0 }
// style={{ x: subX }}
key="sub"
as="ul"
>
<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 variant="secondary" fontSize="sm">{ mainNavItems[openedGroupIndex].text }</Text>
</Flex>
<Box
w="100%"
as="ul"
>
{ isGroupItem(openedItem) && openedItem.subItems?.map(
(item, index) => Array.isArray(item) ? (
<Box
key={ index }
w="100%"
as="ul"
_notLast={{
mb: 2,
pb: 2,
borderBottomWidth: '1px',
borderColor: 'border.divider',
}}
>
{ 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>
{ openedItem && isGroupItem(openedItem) && openedItem.subItems?.map(
(item, index) => Array.isArray(item) ? (
<Box
key={ index }
w="100%"
as="ul"
_notLast={{
mb: 2,
pb: 2,
borderBottomWidth: '1px',
borderColor: 'border.divider',
}}
>
{ 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>
</Flex>
);
};
......
......@@ -6,7 +6,7 @@ type Props = {
isActive?: boolean;
};
export default function useNavLinkProps({ isExpanded, isCollapsed, isActive }: Props) {
export default function useNavLinkStyleProps({ isExpanded, isCollapsed, isActive }: Props) {
return {
itemProps: {
visual: 'navigation' as const,
......
......@@ -5,13 +5,12 @@ import { IconButton } from 'toolkit/chakra/icon-button';
import IconSvg from 'ui/shared/IconSvg';
interface Props {
isMobile?: boolean;
isActive?: boolean;
onClick?: () => void;
className?: string;
}
const NetworkMenuButton = ({ isMobile, isActive, onClick, className }: Props, ref: React.ForwardedRef<HTMLButtonElement>) => {
const NetworkMenuButton = ({ isActive, onClick, className }: Props, ref: React.ForwardedRef<HTMLButtonElement>) => {
return (
<IconButton
className={ className }
......@@ -21,7 +20,7 @@ const NetworkMenuButton = ({ isMobile, isActive, onClick, className }: Props, re
ref={ ref }
h={ 9 }
borderRadius="base"
backgroundColor={ isActive ? { _light: 'blue.50', _dark: 'gray.400' } : 'none' }
backgroundColor={ isActive ? { _light: 'blue.50', _dark: 'gray.800' } : 'transparent' }
onClick={ onClick }
aria-label="Network menu"
aria-roledescription="menu"
......@@ -30,7 +29,7 @@ const NetworkMenuButton = ({ isMobile, isActive, onClick, className }: Props, re
name="networks"
boxSize={ 4 }
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"
/>
</IconButton>
......
import { Box, Select, VStack, Flex } from '@chakra-ui/react';
import { Box, VStack, Flex } from '@chakra-ui/react';
import { capitalize } from 'es-toolkit';
import React from 'react';
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';
......@@ -24,7 +25,7 @@ const NetworkMenuContentMobile = ({ items, tabs }: Props) => {
}, [ items, selectedNetwork?.group, tabs ]);
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 ? (
......@@ -45,11 +46,13 @@ const NetworkMenuContentMobile = ({ items, tabs }: Props) => {
) : (
<>
{ tabs.length > 1 && (
<Select size="xs" borderRadius="base" value={ selectedTab } onChange={ handleSelectChange } mb={ 3 }>
{ tabs.map((tab) => <option key={ tab } value={ tab }>{ capitalize(tab) }</option>) }
</Select>
<NativeSelectRoot size="sm" borderRadius="base" mb={ 3 }>
<NativeSelectField value={ selectedTab } onChange={ handleSelectChange }>
{ 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
.filter(({ group }) => group === selectedTab)
.map((network) => (
......
import { Box, Text, Image, chakra } from '@chakra-ui/react';
import { Box, Text, chakra } from '@chakra-ui/react';
import React from 'react';
import type { FeaturedNetwork } from 'types/networks';
import { useColorModeValue } from 'toolkit/chakra/color-mode';
import { Image } from 'toolkit/chakra/image';
import IconSvg from 'ui/shared/IconSvg';
interface Props extends FeaturedNetwork {
......
......@@ -126,6 +126,7 @@ const SearchBar = ({ isHomepage }: Props) => {
open={ open && (searchTerm.trim().length > 0 || recentSearchKeywords.length > 0) }
autoFocus={ false }
onOpenChange={ handleOpenChange }
// TODO @tom2drum fix positioning on mobile
positioning={{ offset: isMobile && !isHomepage ? { mainAxis: -4, crossAxis: 12 } : { mainAxis: 8, crossAxis: 0 } }}
lazyMount
closeOnInteractOutside={ false }
......
......@@ -33,7 +33,7 @@ const TopBar = () => {
) }
<Settings/>
{ 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"/>
<NetworkMenu/>
</Box>
......
import { Drawer, DrawerBody, DrawerContent, DrawerOverlay, useDisclosure } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import React from 'react';
......@@ -7,6 +6,8 @@ import type { Screen } from 'ui/snippets/auth/types';
import config from 'configs/app';
import * as mixpanel from 'lib/mixpanel';
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 useProfileQuery from 'ui/snippets/auth/useProfileQuery';
......@@ -56,35 +57,41 @@ const UserProfileMobile = () => {
authModal.onClose();
}, [ 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 (
<>
<UserProfileButton
profileQuery={ profileQuery }
variant="header"
onClick={ handleProfileButtonClick }
/>
{ (profileQuery.data || web3Address) && (
<Drawer
isOpen={ profileMenu.isOpen }
placement="right"
onClose={ profileMenu.onClose }
autoFocus={ false }
>
<DrawerOverlay/>
<DrawerContent maxWidth="300px">
<DrawerBody p={ 6 }>
<UserProfileContent
data={ profileQuery.data }
onClose={ profileMenu.onClose }
onLogin={ authModal.onOpen }
onAddEmail={ handleAddEmailClick }
onAddAddress={ handleAddAddressClick }
/>
</DrawerBody>
</DrawerContent>
</Drawer>
) }
{ authModal.isOpen && (
<DrawerRoot
open={ profileMenu.open }
onOpenChange={ handleDrawerOpenChange }
>
<DrawerBackdrop/>
<DrawerTrigger>
<UserProfileButton
profileQuery={ profileQuery }
visual="header"
onClick={ handleProfileButtonClick }
/>
</DrawerTrigger>
<DrawerContent>
<DrawerBody>
<UserProfileContent
data={ profileQuery.data }
onClose={ profileMenu.onClose }
onLogin={ authModal.onOpen }
onAddEmail={ handleAddEmailClick }
onAddAddress={ handleAddAddressClick }
/>
</DrawerBody>
</DrawerContent>
</DrawerRoot>
{ authModal.open && (
<AuthModal
onClose={ handleAuthModalClose }
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