Commit 31a8d6be authored by tom's avatar tom

styles for sort button

parent 3cfd4d1a
...@@ -28,6 +28,7 @@ const RESTRICTED_MODULES = { ...@@ -28,6 +28,7 @@ const RESTRICTED_MODULES = {
{ name: 'ui/shared/chakra/Skeleton', message: 'Please use Skeleton component from toolkit/chakra instead' }, { name: 'ui/shared/chakra/Skeleton', message: 'Please use Skeleton component from toolkit/chakra instead' },
{ name: 'ui/shared/Tabs/RoutedTabs', message: 'Please use RoutedTabs component from toolkit/components/RoutedTabs instead' }, { name: 'ui/shared/Tabs/RoutedTabs', message: 'Please use RoutedTabs component from toolkit/components/RoutedTabs instead' },
{ name: 'ui/shared/chakra/Tag', message: 'Please use Tag component from toolkit/chakra instead' }, { name: 'ui/shared/chakra/Tag', message: 'Please use Tag component from toolkit/chakra instead' },
{ name: 'ui/shared/select/Select', message: 'Please use Select component from toolkit/chakra instead' },
{ {
name: '@chakra-ui/react', name: '@chakra-ui/react',
importNames: [ importNames: [
......
...@@ -16,6 +16,7 @@ export const IconButton = React.forwardRef<HTMLDivElement, IconButtonProps>( ...@@ -16,6 +16,7 @@ export const IconButton = React.forwardRef<HTMLDivElement, IconButtonProps>(
<Skeleton loading={ loading } ref={ ref } asChild> <Skeleton loading={ loading } ref={ ref } asChild>
<Button <Button
display="inline-flex" display="inline-flex"
justifyContent="center"
px="0" px="0"
py="0" py="0"
{ ...(size ? { size } : { height: 'auto', minW: 'auto' }) } { ...(size ? { size } : { height: 'auto', minW: 'auto' }) }
......
...@@ -4,25 +4,43 @@ import type { CollectionItem } from '@chakra-ui/react'; ...@@ -4,25 +4,43 @@ import type { CollectionItem } from '@chakra-ui/react';
import { Select as ChakraSelect, Portal } from '@chakra-ui/react'; import { Select as ChakraSelect, Portal } from '@chakra-ui/react';
import * as React from 'react'; import * as React from 'react';
import IconSvg from 'ui/shared/IconSvg';
import { CloseButton } from './close-button'; import { CloseButton } from './close-button';
import { Skeleton } from './skeleton';
interface SelectTriggerProps extends ChakraSelect.ControlProps { export interface SelectControlProps extends ChakraSelect.ControlProps {
clearable?: boolean; clearable?: boolean;
noIndicator?: boolean;
triggerProps?: ChakraSelect.TriggerProps;
loading?: boolean;
} }
export const SelectTrigger = React.forwardRef< export const SelectControl = React.forwardRef<
HTMLButtonElement, HTMLButtonElement,
SelectTriggerProps SelectControlProps
>(function SelectTrigger(props, ref) { >(function SelectControl(props, ref) {
const { children, clearable, ...rest } = props; const { children, clearable, noIndicator, triggerProps, loading, ...rest } = props;
return ( return (
<ChakraSelect.Control { ...rest }> <Skeleton loading={ loading } asChild>
<ChakraSelect.Trigger ref={ ref }>{ children }</ChakraSelect.Trigger> <ChakraSelect.Control { ...rest } className="group">
<ChakraSelect.Trigger ref={ ref } { ...triggerProps }>{ children }</ChakraSelect.Trigger>
{ (!noIndicator || clearable) && (
<ChakraSelect.IndicatorGroup> <ChakraSelect.IndicatorGroup>
{ clearable && <SelectClearTrigger/> } { clearable && <SelectClearTrigger/> }
<ChakraSelect.Indicator/> { !noIndicator && (
<ChakraSelect.Indicator
transform="rotate(-90deg)"
_open={{ transform: 'rotate(90deg)' }}
flexShrink={ 0 }
>
<IconSvg name="arrows/east-mini"/>
</ChakraSelect.Indicator>
) }
</ChakraSelect.IndicatorGroup> </ChakraSelect.IndicatorGroup>
) }
</ChakraSelect.Control> </ChakraSelect.Control>
</Skeleton>
); );
}); });
...@@ -68,8 +86,10 @@ export const SelectItem = React.forwardRef< ...@@ -68,8 +86,10 @@ export const SelectItem = React.forwardRef<
const { item, children, ...rest } = props; const { item, children, ...rest } = props;
return ( return (
<ChakraSelect.Item key={ item.value } item={ item } { ...rest } ref={ ref }> <ChakraSelect.Item key={ item.value } item={ item } { ...rest } ref={ ref }>
<ChakraSelect.ItemIndicator asChild>
<IconSvg name="check" boxSize={ 5 } flexShrink={ 0 }/>
</ChakraSelect.ItemIndicator>
{ children } { children }
<ChakraSelect.ItemIndicator/>
</ChakraSelect.Item> </ChakraSelect.Item>
); );
}); });
...@@ -100,6 +120,8 @@ export const SelectValueText = React.forwardRef< ...@@ -100,6 +120,8 @@ export const SelectValueText = React.forwardRef<
); );
}); });
export interface SelectRootProps extends ChakraSelect.RootProps {}
export const SelectRoot = React.forwardRef< export const SelectRoot = React.forwardRef<
HTMLDivElement, HTMLDivElement,
ChakraSelect.RootProps ChakraSelect.RootProps
...@@ -108,7 +130,7 @@ export const SelectRoot = React.forwardRef< ...@@ -108,7 +130,7 @@ export const SelectRoot = React.forwardRef<
<ChakraSelect.Root <ChakraSelect.Root
{ ...props } { ...props }
ref={ ref } ref={ ref }
positioning={{ sameWidth: true, ...props.positioning }} positioning={{ sameWidth: false, ...props.positioning }}
> >
{ props.asChild ? ( { props.asChild ? (
props.children props.children
......
...@@ -238,15 +238,26 @@ const semanticTokens: ThemingConfig['semanticTokens'] = { ...@@ -238,15 +238,26 @@ const semanticTokens: ThemingConfig['semanticTokens'] = {
}, },
}, },
select: { select: {
trigger: {
outline: {
fg: { value: { _light: '{colors.blackAlpha.800}', _dark: '{colors.whiteAlpha.800}' } },
border: { value: { _light: '{colors.gray.200}', _dark: '{colors.gray.600}' } },
},
filter: {
fg: { fg: {
DEFAULT: { value: { _light: '{colors.blackAlpha.800}', _dark: '{colors.whiteAlpha.800}' } }, DEFAULT: { value: { _light: '{colors.blackAlpha.800}', _dark: '{colors.whiteAlpha.800}' } },
hover: { value: '{colors.blue.400}' }, selected: { value: { _light: '{colors.blue.600}', _dark: '{colors.gray.50}' } },
error: { value: '{colors.red.500}' },
}, },
border: { border: {
DEFAULT: { value: { _light: '{colors.gray.300}', _dark: '{colors.gray.600}' } }, DEFAULT: { value: { _light: '{colors.gray.200}', _dark: '{colors.gray.600}' } },
hover: { value: '{colors.blue.400}' }, selected: { value: { _light: '{colors.blue.50}', _dark: '{colors.gray.600}' } },
error: { value: '{colors.red.500}' }, },
},
},
item: {
bg: {
highlighted: { value: { _light: '{colors.blue.50}', _dark: '{colors.whiteAlpha.100}' } },
},
}, },
}, },
spinner: { spinner: {
......
...@@ -10,6 +10,7 @@ export const recipe = defineRecipe({ ...@@ -10,6 +10,7 @@ export const recipe = defineRecipe({
width: 'fit-content', width: 'fit-content',
maxWidth: '100%', maxWidth: '100%',
whiteSpace: 'nowrap', whiteSpace: 'nowrap',
fontVariantNumeric: 'normal',
userSelect: 'none', userSelect: 'none',
_loading: { _loading: {
borderRadius: 'sm', borderRadius: 'sm',
......
...@@ -13,24 +13,20 @@ export const recipe = defineSlotRecipe({ ...@@ -13,24 +13,20 @@ export const recipe = defineSlotRecipe({
trigger: { trigger: {
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
justifyContent: 'space-between', justifyContent: 'flex-start',
width: 'full', width: 'full',
minH: 'var(--select-trigger-height)', minH: 'var(--select-trigger-height)',
px: 'var(--select-trigger-padding-x)', pr: 'var(--select-trigger-padding-right)',
pl: 'var(--select-trigger-padding-left)',
borderRadius: 'base', borderRadius: 'base',
userSelect: 'none', userSelect: 'none',
textAlign: 'start', textAlign: 'start',
fontWeight: '500', fontWeight: 'semibold',
cursor: 'pointer',
focusVisibleRing: 'none', focusVisibleRing: 'none',
_placeholderShown: {
color: 'gray.500',
},
_disabled: { _disabled: {
layerStyle: 'disabled', layerStyle: 'disabled',
}, },
_invalid: {
borderColor: 'select.border.error',
},
}, },
indicatorGroup: { indicatorGroup: {
display: 'flex', display: 'flex',
...@@ -40,7 +36,7 @@ export const recipe = defineSlotRecipe({ ...@@ -40,7 +36,7 @@ export const recipe = defineSlotRecipe({
right: '0', right: '0',
top: '0', top: '0',
bottom: '0', bottom: '0',
px: 'var(--select-trigger-padding-x)', px: '0',
pointerEvents: 'none', pointerEvents: 'none',
}, },
indicator: { indicator: {
...@@ -48,17 +44,27 @@ export const recipe = defineSlotRecipe({ ...@@ -48,17 +44,27 @@ export const recipe = defineSlotRecipe({
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
color: 'inherit', color: 'inherit',
_groupHover: {
color: 'link.primary.hover',
},
_open: {
color: 'link.primary.hover',
},
}, },
content: { content: {
background: 'popover.bg', background: 'popover.bg',
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
zIndex: 'dropdown', zIndex: 'dropdown',
borderRadius: 'base', borderRadius: 'md',
borderWidth: '0',
outline: 0, outline: 0,
boxShadow: 'popover',
boxShadowColor: 'colors.popover.shadow',
maxH: '96', maxH: '96',
overflowY: 'auto', overflowY: 'auto',
boxShadow: 'md', width: 'max-content',
minWidth: '150px',
_open: { _open: {
animationStyle: 'slide-fade-in', animationStyle: 'slide-fade-in',
animationDuration: 'fast', animationDuration: 'fast',
...@@ -74,14 +80,11 @@ export const recipe = defineSlotRecipe({ ...@@ -74,14 +80,11 @@ export const recipe = defineSlotRecipe({
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
gap: '2', gap: '2',
cursor: 'option', cursor: 'pointer',
justifyContent: 'space-between', justifyContent: 'flex-start',
flex: '1', flex: '1',
textAlign: 'start', textAlign: 'start',
borderRadius: 'base', borderRadius: 'none',
_highlighted: {
bg: 'bg.emphasized/60',
},
_disabled: { _disabled: {
pointerEvents: 'none', pointerEvents: 'none',
opacity: '0.5', opacity: '0.5',
...@@ -90,6 +93,9 @@ export const recipe = defineSlotRecipe({ ...@@ -90,6 +93,9 @@ export const recipe = defineSlotRecipe({
width: '4', width: '4',
height: '4', height: '4',
}, },
_highlighted: {
bg: 'select.item.bg.highlighted',
},
}, },
control: { control: {
pos: 'relative', pos: 'relative',
...@@ -122,160 +128,124 @@ export const recipe = defineSlotRecipe({ ...@@ -122,160 +128,124 @@ export const recipe = defineSlotRecipe({
variant: { variant: {
outline: { outline: {
trigger: { trigger: {
bg: 'transparent',
borderWidth: '2px', borderWidth: '2px',
borderColor: 'select.border', color: 'select.trigger.outline.fg',
bgColor: 'transparent',
borderColor: 'select.trigger.outline.border',
_expanded: { _expanded: {
borderColor: 'select.border.hover', color: 'link.primary.hover',
borderColor: 'link.primary.hover',
}, },
_hover: { _hover: {
borderColor: 'select.border.hover', color: 'link.primary.hover',
borderColor: 'link.primary.hover',
}, },
_focusVisible: { _focusVisible: {
borderColor: 'select.border.hover', borderColor: 'link.primary.hover',
focusVisibleRing: 'none', focusVisibleRing: 'none',
}, },
_invalid: {
borderColor: 'border.error',
}, },
}, },
}, },
filter: {
size: {
xs: {
root: {
'--select-trigger-height': 'sizes.8',
'--select-trigger-padding-x': 'spacing.2',
},
content: {
p: '1',
gap: '1',
textStyle: 'xs',
},
trigger: { trigger: {
textStyle: 'xs', borderWidth: '2px',
gap: '1', color: 'select.trigger.filter.fg.selected',
}, bgColor: 'select.trigger.filter.border.selected',
item: { borderColor: 'select.trigger.filter.border.selected',
py: '1', _expanded: {
px: '2', color: 'link.primary.hover',
borderColor: 'link.primary.hover',
}, },
itemGroupLabel: { _hover: {
py: '1', color: 'select.trigger.filter.fg.selected',
px: '2', borderColor: 'select.trigger.filter.border.selected',
}, },
indicator: { _focusVisible: {
_icon: { borderColor: 'link.primary.hover',
width: '3.5', focusVisibleRing: 'none',
height: '3.5',
}, },
_placeholderShown: {
color: 'select.trigger.filter.fg',
borderColor: 'select.trigger.filter.border',
bgColor: 'transparent',
_hover: {
color: 'link.primary.hover',
borderColor: 'link.primary.hover',
}, },
}, },
sm: {
root: {
'--select-trigger-height': 'sizes.9',
'--select-trigger-padding-x': 'spacing.2.5',
}, },
content: {
p: '1',
textStyle: 'sm',
}, },
sort: {
trigger: { trigger: {
textStyle: 'sm', borderWidth: '2px',
gap: '1', borderColor: 'transparent',
bgColor: 'transparent',
_hover: {
color: 'link.primary.hover',
borderColor: 'link.primary.hover',
}, },
indicator: { _open: {
_icon: { bg: 'button.dropdown.border.selected',
width: '4', color: 'button.dropdown.fg.selected',
height: '4', borderColor: 'button.dropdown.border.selected',
_hover: {
bg: 'button.dropdown.border.selected',
color: 'button.dropdown.fg.selected',
borderColor: 'button.dropdown.border.selected',
}, },
}, },
item: {
py: '1',
px: '1.5',
}, },
itemGroup: {
mt: '1',
},
itemGroupLabel: {
py: '1',
px: '1.5',
}, },
}, },
md: { size: {
sm: {
root: { root: {
'--select-trigger-height': 'sizes.10', '--select-trigger-height': 'sizes.8',
'--select-trigger-padding-x': 'spacing.3', '--select-trigger-padding-right': 'spacing.8',
'--select-trigger-padding-left': 'spacing.2',
}, },
content: { content: {
p: '1', px: '0',
textStyle: 'sm', py: '2',
}, textStyle: 'md',
itemGroup: {
mt: '1.5',
},
item: {
py: '1.5',
px: '2',
},
itemIndicator: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
},
itemGroupLabel: {
py: '1.5',
px: '2',
}, },
trigger: { trigger: {
textStyle: 'sm', textStyle: 'sm',
gap: '2', gap: '1',
}, },
indicator: { indicator: {
_icon: { width: '5',
width: '4', height: '5',
height: '4',
},
},
},
lg: {
root: {
'--select-trigger-height': 'sizes.12',
'--select-trigger-padding-x': 'spacing.4',
},
content: {
p: '1.5',
textStyle: 'md',
}, },
itemGroup: { indicatorGroup: {
mt: '2', pr: '2',
pl: '1',
}, },
item: { item: {
py: '2', py: '2',
px: '3', pr: '4',
pl: '44px',
_selected: {
px: '4',
}, },
itemGroupLabel: {
py: '2',
px: '3',
}, },
trigger: { itemGroup: {
textStyle: 'md', mt: '1',
py: '3',
gap: '2',
},
indicator: {
_icon: {
width: '5',
height: '5',
}, },
itemGroupLabel: {
py: '1',
px: '1.5',
}, },
}, },
}, },
}, },
defaultVariants: { defaultVariants: {
size: 'md', size: 'sm',
variant: 'outline', variant: 'outline',
}, },
}); });
...@@ -191,7 +191,7 @@ export interface TransactionsSorting { ...@@ -191,7 +191,7 @@ export interface TransactionsSorting {
export type TransactionsSortingField = TransactionsSorting['sort']; export type TransactionsSortingField = TransactionsSorting['sort'];
export type TransactionsSortingValue = `${ TransactionsSortingField }-${ TransactionsSorting['order'] }`; export type TransactionsSortingValue = `${ TransactionsSortingField }-${ TransactionsSorting['order'] }` | 'default';
export type ScrollTransactionData = { export type ScrollTransactionData = {
l1_fee: string; l1_fee: string;
......
/* eslint-disable max-len */ /* eslint-disable max-len */
/* eslint-disable react/jsx-no-bind */ /* eslint-disable react/jsx-no-bind */
import { createListCollection, HStack, Spinner, VStack } from '@chakra-ui/react'; import { HStack, Spinner, VStack } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { Button } from 'toolkit/chakra/button'; import { Button } from 'toolkit/chakra/button';
...@@ -12,7 +12,6 @@ import { InputGroup } from 'toolkit/chakra/input-group'; ...@@ -12,7 +12,6 @@ import { InputGroup } from 'toolkit/chakra/input-group';
import { NativeSelectField, NativeSelectRoot } from 'toolkit/chakra/native-select'; 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 { TabsContent, TabsList, TabsRoot, TabsTrigger } from 'toolkit/chakra/tabs'; import { TabsContent, TabsList, TabsRoot, TabsTrigger } from 'toolkit/chakra/tabs';
...@@ -27,6 +26,7 @@ import BadgesShowcase from 'ui/showcases/Badges'; ...@@ -27,6 +26,7 @@ import BadgesShowcase from 'ui/showcases/Badges';
import ButtonShowcase from 'ui/showcases/Button'; import ButtonShowcase from 'ui/showcases/Button';
import LinksShowcase from 'ui/showcases/Links'; import LinksShowcase from 'ui/showcases/Links';
import PaginationShowcase from 'ui/showcases/Pagination'; import PaginationShowcase from 'ui/showcases/Pagination';
import SelectsShowcase from 'ui/showcases/Select';
import TabsShowcase from 'ui/showcases/Tabs'; import TabsShowcase from 'ui/showcases/Tabs';
import TagsShowcase from 'ui/showcases/Tags'; import TagsShowcase from 'ui/showcases/Tags';
import TooltipsShowcase from 'ui/showcases/Tooltip'; import TooltipsShowcase from 'ui/showcases/Tooltip';
...@@ -36,15 +36,6 @@ const TEXT = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do ei ...@@ -36,15 +36,6 @@ 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"/>
...@@ -60,6 +51,7 @@ const ChakraShowcases = () => { ...@@ -60,6 +51,7 @@ const ChakraShowcases = () => {
<TabsTrigger value="buttons">Buttons</TabsTrigger> <TabsTrigger value="buttons">Buttons</TabsTrigger>
<TabsTrigger value="links">Links</TabsTrigger> <TabsTrigger value="links">Links</TabsTrigger>
<TabsTrigger value="pagination">Pagination</TabsTrigger> <TabsTrigger value="pagination">Pagination</TabsTrigger>
<TabsTrigger value="selects">Selects</TabsTrigger>
<TabsTrigger value="tabs">Tabs</TabsTrigger> <TabsTrigger value="tabs">Tabs</TabsTrigger>
<TabsTrigger value="tags">Tags</TabsTrigger> <TabsTrigger value="tags">Tags</TabsTrigger>
<TabsTrigger value="tooltips">Tooltips</TabsTrigger> <TabsTrigger value="tooltips">Tooltips</TabsTrigger>
...@@ -72,6 +64,7 @@ const ChakraShowcases = () => { ...@@ -72,6 +64,7 @@ const ChakraShowcases = () => {
<LinksShowcase/> <LinksShowcase/>
<TabsShowcase/> <TabsShowcase/>
<PaginationShowcase/> <PaginationShowcase/>
<SelectsShowcase/>
<TooltipsShowcase/> <TooltipsShowcase/>
<TagsShowcase/> <TagsShowcase/>
...@@ -194,18 +187,6 @@ const ChakraShowcases = () => { ...@@ -194,18 +187,6 @@ const ChakraShowcases = () => {
<section> <section>
<Heading textStyle="heading.md" mb={ 2 }>Select</Heading> <Heading textStyle="heading.md" mb={ 2 }>Select</Heading>
<HStack gap={ 4 } whiteSpace="nowrap" flexWrap="wrap"> <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"> <NativeSelectRoot w="350px">
<NativeSelectField> <NativeSelectField>
<option value="1">Option 1</option> <option value="1">Option 1</option>
......
...@@ -13,13 +13,13 @@ interface Props extends Omit<HTMLChakraProps<'div'>, 'direction'> { ...@@ -13,13 +13,13 @@ interface Props extends Omit<HTMLChakraProps<'div'>, 'direction'> {
const WIDTH = 50; const WIDTH = 50;
const Utilization = ({ value, colorScheme = 'green', isLoading, ...rest }: Props) => { const Utilization = ({ value, colorScheme = 'green', isLoading, ...rest }: Props, ref: React.Ref<HTMLDivElement>) => {
const valueString = (clamp(value * 100 || 0, 0, 100)).toLocaleString(undefined, { maximumFractionDigits: 2 }) + '%'; const valueString = (clamp(value * 100 || 0, 0, 100)).toLocaleString(undefined, { maximumFractionDigits: 2 }) + '%';
const colorGrayScheme = { _light: 'gray.500', _dark: 'gray.400' }; const colorGrayScheme = { _light: 'gray.500', _dark: 'gray.400' };
const color = colorScheme === 'gray' ? colorGrayScheme : 'green.500'; const color = colorScheme === 'gray' ? colorGrayScheme : 'green.500';
return ( return (
<Flex alignItems="center" columnGap={ 2 } { ...rest }> <Flex alignItems="center" columnGap={ 2 } { ...rest } ref={ ref }>
<Skeleton loading={ isLoading } w={ `${ WIDTH }px` } h="4px" borderRadius="full" overflow="hidden"> <Skeleton loading={ isLoading } w={ `${ WIDTH }px` } h="4px" borderRadius="full" overflow="hidden">
<Box bg={{ _light: 'blackAlpha.200', _dark: 'whiteAlpha.200' }} h="100%"> <Box bg={{ _light: 'blackAlpha.200', _dark: 'whiteAlpha.200' }} h="100%">
<Box bg={ color } w={ valueString } h="100%"/> <Box bg={ color } w={ valueString } h="100%"/>
...@@ -34,4 +34,4 @@ const Utilization = ({ value, colorScheme = 'green', isLoading, ...rest }: Props ...@@ -34,4 +34,4 @@ const Utilization = ({ value, colorScheme = 'green', isLoading, ...rest }: Props
); );
}; };
export default React.memo(Utilization); export default React.memo(React.forwardRef(Utilization));
...@@ -24,6 +24,7 @@ export interface Props<Value extends string> { ...@@ -24,6 +24,7 @@ export interface Props<Value extends string> {
children?: (props: InjectedProps<Value>) => React.ReactNode; children?: (props: InjectedProps<Value>) => React.ReactNode;
} }
// TODO @tom2drum remove this component
const Select = <Value extends string>({ className, isLoading, options, name, defaultValue, onChange, children }: Props<Value>) => { const Select = <Value extends string>({ className, isLoading, options, name, defaultValue, onChange, children }: Props<Value>) => {
const { isOpen, onToggle, onClose } = useDisclosure(); const { isOpen, onToggle, onClose } = useDisclosure();
......
export interface SelectOption<Value extends string = string> { export interface SelectOption<Value extends string = string> {
value: Value | undefined; value: Value;
label: string; label: string;
} }
import {
Box,
useColorModeValue,
Button,
chakra,
} from '@chakra-ui/react';
import React from 'react';
import Skeleton from 'ui/shared/chakra/Skeleton';
import IconSvg from 'ui/shared/IconSvg';
type ButtonProps = {
isActive: boolean;
onClick: () => void;
isLoading?: boolean;
children: React.ReactNode;
className?: string;
};
const ButtonDesktop = ({ children, isActive, onClick, isLoading, className }: ButtonProps, ref: React.ForwardedRef<HTMLButtonElement>) => {
const primaryColor = useColorModeValue('blackAlpha.800', 'whiteAlpha.800');
const secondaryColor = useColorModeValue('blackAlpha.600', 'whiteAlpha.600');
return (
<Skeleton isLoaded={ !isLoading }>
<Button
className={ className }
ref={ ref }
size="sm"
variant="outline"
onClick={ onClick }
color={ primaryColor }
fontWeight="600"
borderColor="transparent"
px={ 2 }
selected={ isActive }
>
<Box
as={ isActive ? 'div' : 'span' }
color={ isActive ? 'inherit' : secondaryColor }
fontWeight="400"
mr={ 1 }
transition={ isActive ? 'none' : 'inherit' }
>Sort by</Box>
{ children }
<IconSvg
name="arrows/east-mini"
boxSize={ 5 }
ml={ 1 }
transform={ isActive ? 'rotate(90deg)' : 'rotate(-90deg)' }
/>
</Button>
</Skeleton>
);
};
export default chakra(React.forwardRef(ButtonDesktop));
import { IconButton, chakra } from '@chakra-ui/react';
import React from 'react';
import Skeleton from 'ui/shared/chakra/Skeleton';
import IconSvg from 'ui/shared/IconSvg';
type Props = {
onClick: () => void;
isActive: boolean;
className?: string;
isLoading?: boolean;
};
const ButtonMobile = ({ onClick, isActive, className, isLoading }: Props, ref: React.ForwardedRef<HTMLButtonElement>) => {
if (isLoading) {
return <Skeleton className={ className } w="36px" h="32px" borderRadius="base"/>;
}
return (
<IconButton
ref={ ref }
icon={ <IconSvg name="arrows/up-down" boxSize={ 5 }/> }
aria-label="sort"
size="sm"
variant="outline"
colorScheme="gray"
minWidth="36px"
onClick={ onClick }
isActive={ isActive }
display="flex"
className={ className }
/>
);
};
export default chakra(React.forwardRef(ButtonMobile));
import { chakra } from '@chakra-ui/react'; import { chakra } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { SelectOption } from 'ui/shared/select/types';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import Select, { type Props as SelectProps } from 'ui/shared/select/Select'; import { IconButton } from 'toolkit/chakra/icon-button';
import type { SelectRootProps, SelectControlProps } from 'toolkit/chakra/select';
import SortButtonDesktop from './ButtonDesktop'; import { SelectContent, SelectItem, SelectRoot, SelectControl, SelectValueText } from 'toolkit/chakra/select';
import SortButtonMobile from './ButtonMobile'; import IconSvg from 'ui/shared/IconSvg';
type Props<Value extends string> = Omit<SelectProps<Value>, 'children'>; export interface Props extends SelectRootProps {
controlProps?: SelectControlProps;
isLoading?: boolean;
}
const Sort = <Sort extends string>({ name, options, isLoading, onChange, defaultValue }: Props<Sort>) => { const Sort = (props: Props) => {
const { collection, controlProps, isLoading, ...rest } = props;
const isMobile = useIsMobile(false); const isMobile = useIsMobile(false);
const trigger = (() => {
if (isMobile) {
return ( return (
<Select <SelectControl { ...controlProps } triggerProps={{ asChild: true }} noIndicator>
options={ options } <IconButton
name={ name } loading={ isLoading }
defaultValue={ defaultValue } aria-label="sort"
onChange={ onChange } size="sm"
variant="outline"
colorScheme="gray"
width="36px"
> >
{ ({ isOpen, onToggle, value }) => { <IconSvg name="arrows/up-down" boxSize={ 5 }/>
</IconButton>
</SelectControl>
);
}
return ( return (
isMobile ? ( <SelectControl
<SortButtonMobile isActive={ isOpen || Boolean(value) } onClick={ onToggle } isLoading={ isLoading }/> { ...controlProps }
) : ( loading={ isLoading }
<SortButtonDesktop isActive={ isOpen } isLoading={ isLoading } onClick={ onToggle }> >
{ options.find((option: SelectOption<Sort>) => option.value === value || (!option.value && !value))?.label } <chakra.span
</SortButtonDesktop> flexShrink={ 0 }
) fontWeight="normal"
color={{ _light: 'blackAlpha.600', _dark: 'whiteAlpha.600' }}
_groupHover={{ color: 'inherit' }}
>
Sort by
</chakra.span>
<SelectValueText
color={{ _light: 'blackAlpha.800', _dark: 'whiteAlpha.800' }}
_groupHover={{ color: 'inherit' }}
/>
</SelectControl>
); );
} } })();
</Select>
return (
<SelectRoot variant={{ lgDown: 'outline', lg: 'sort' }} collection={ collection } { ...rest }>
{ trigger }
<SelectContent>
{ collection.items.map((item) => (
<SelectItem item={ item } key={ item.value }>
{ item.label }
</SelectItem>
)) }
</SelectContent>
</SelectRoot>
); );
}; };
......
export default function getNextSortValue<SortField extends string, Sort extends string>( export default function getNextSortValue<SortField extends string, Sort extends string>(
sortSequence: Record<SortField, Array<Sort | undefined>>, field: SortField, sortSequence: Record<SortField, Array<Sort>>, field: SortField,
) { ) {
return (prevValue: Sort | undefined) => { return (prevValue: Sort) => {
const sequence = sortSequence[field]; const sequence = sortSequence[field];
return getNextValueFromSequence(sequence, prevValue); return getNextValueFromSequence(sequence, prevValue);
}; };
......
import { createListCollection } from '@chakra-ui/react';
import React from 'react';
import { SelectContent, SelectItem, SelectRoot, SelectControl, SelectValueText } from 'toolkit/chakra/select';
import Sort from 'ui/shared/sort/Sort';
import { SORT_OPTIONS } from 'ui/txs/useTxsSort';
import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from './parts';
const frameworks = createListCollection({
items: [
{ label: 'React.js', value: 'react' },
{ label: 'Vue.js', value: 'vue' },
{ label: 'Angular', value: 'angular' },
{ label: 'Svelte', value: 'svelte' },
],
});
const txSortingOptions = createListCollection({
items: SORT_OPTIONS,
});
const SelectsShowcase = () => {
return (
<Container value="selects">
<Section>
<SectionHeader>Variant</SectionHeader>
<SamplesStack>
<Sample label="variant: outline">
<SelectRoot collection={ frameworks } variant="outline" defaultValue={ [ frameworks.items[0].value ] }>
<SelectControl w="200px">
<SelectValueText placeholder="Select framework"/>
</SelectControl>
<SelectContent>
{ frameworks.items.map((framework) => (
<SelectItem item={ framework } key={ framework.value }>
{ framework.label }
</SelectItem>
)) }
</SelectContent>
</SelectRoot>
</Sample>
<Sample label="variant: filter">
<SelectRoot collection={ frameworks } variant="filter" multiple>
<SelectControl w="200px">
<SelectValueText placeholder="Select framework"/>
</SelectControl>
<SelectContent>
{ frameworks.items.map((framework) => (
<SelectItem item={ framework } key={ framework.value }>
{ framework.label }
</SelectItem>
)) }
</SelectContent>
</SelectRoot>
</Sample>
</SamplesStack>
</Section>
<Section>
<SectionHeader>Examples</SectionHeader>
<SectionSubHeader>Sort</SectionSubHeader>
<SamplesStack>
<Sample>
<Sort
name="transactions_sorting"
defaultValue={ [ txSortingOptions.items[0].value ] }
collection={ txSortingOptions }
/>
<Sort
name="transactions_sorting"
defaultValue={ [ txSortingOptions.items[0].value ] }
collection={ txSortingOptions }
isLoading
/>
</Sample>
</SamplesStack>
</Section>
</Container>
);
};
export default React.memo(SelectsShowcase);
...@@ -16,10 +16,10 @@ import TxsHeaderMobile from './TxsHeaderMobile'; ...@@ -16,10 +16,10 @@ import TxsHeaderMobile from './TxsHeaderMobile';
import TxsList from './TxsList'; import TxsList from './TxsList';
import TxsTable from './TxsTable'; import TxsTable from './TxsTable';
const SORT_SEQUENCE: Record<TransactionsSortingField, Array<TransactionsSortingValue | undefined>> = { const SORT_SEQUENCE: Record<TransactionsSortingField, Array<TransactionsSortingValue>> = {
value: [ 'value-desc', 'value-asc', undefined ], value: [ 'value-desc', 'value-asc', 'default' ],
fee: [ 'fee-desc', 'fee-asc', undefined ], fee: [ 'fee-desc', 'fee-asc', 'default' ],
block_number: [ 'block_number-asc', undefined ], block_number: [ 'block_number-asc', 'default' ],
}; };
type Props = { type Props = {
...@@ -37,8 +37,8 @@ type Props = { ...@@ -37,8 +37,8 @@ type Props = {
items?: Array<Transaction>; items?: Array<Transaction>;
isPlaceholderData: boolean; isPlaceholderData: boolean;
isError: boolean; isError: boolean;
setSorting: (value: TransactionsSortingValue | undefined) => void; setSorting: (value: TransactionsSortingValue) => void;
sort: TransactionsSortingValue | undefined; sort: TransactionsSortingValue;
}; };
const TxsContent = ({ const TxsContent = ({
......
import { HStack, chakra } from '@chakra-ui/react'; import { HStack, chakra, createListCollection } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { TransactionsSortingValue } from 'types/api/transaction'; import type { TransactionsSortingValue } from 'types/api/transaction';
...@@ -15,8 +15,8 @@ import { SORT_OPTIONS } from './useTxsSort'; ...@@ -15,8 +15,8 @@ import { SORT_OPTIONS } from './useTxsSort';
// import TxsFilters from './TxsFilters'; // import TxsFilters from './TxsFilters';
type Props = { type Props = {
sorting: TransactionsSortingValue | undefined; sorting: TransactionsSortingValue;
setSorting: (val: TransactionsSortingValue | undefined) => void; setSorting: (val: TransactionsSortingValue) => void;
paginationProps: PaginationParams; paginationProps: PaginationParams;
className?: string; className?: string;
showPagination?: boolean; showPagination?: boolean;
...@@ -24,19 +24,26 @@ type Props = { ...@@ -24,19 +24,26 @@ type Props = {
linkSlot?: React.ReactNode; linkSlot?: React.ReactNode;
}; };
const collection = createListCollection({
items: SORT_OPTIONS,
});
const TxsHeaderMobile = ({ filterComponent, sorting, setSorting, paginationProps, className, showPagination = true, linkSlot }: Props) => { const TxsHeaderMobile = ({ filterComponent, sorting, setSorting, paginationProps, className, showPagination = true, linkSlot }: Props) => {
const handleSortValueChange = React.useCallback(({ value }: { value: Array<string> }) => {
setSorting(value[0] as TransactionsSortingValue);
}, [ setSorting ]);
return ( return (
<ActionBar className={ className }> <ActionBar className={ className }>
<HStack> <HStack>
{ filterComponent } { filterComponent }
{ /* TODO @tom2drum fix sort select */ } <Sort
{ /* <Sort
name="transactions_sorting" name="transactions_sorting"
defaultValue={ sorting } defaultValue={ [ sorting ] }
options={ SORT_OPTIONS } collection={ collection }
onChange={ setSorting } onValueChange={ handleSortValueChange }
isLoading={ paginationProps.isLoading } isLoading={ paginationProps.isLoading }
/> */ } />
{ /* api is not implemented */ } { /* api is not implemented */ }
{ /* <FilterInput { /* <FilterInput
// eslint-disable-next-line react/jsx-no-bind // eslint-disable-next-line react/jsx-no-bind
......
...@@ -10,7 +10,7 @@ import * as cookies from 'lib/cookies'; ...@@ -10,7 +10,7 @@ import * as cookies from 'lib/cookies';
import sortTxs from './sortTxs'; import sortTxs from './sortTxs';
export const SORT_OPTIONS: Array<SelectOption<TransactionsSortingValue>> = [ export const SORT_OPTIONS: Array<SelectOption<TransactionsSortingValue>> = [
{ label: 'Default', value: undefined }, { label: 'Default', value: 'default' },
{ label: 'Value ascending', value: 'value-asc' }, { label: 'Value ascending', value: 'value-asc' },
{ label: 'Value descending', value: 'value-desc' }, { label: 'Value descending', value: 'value-desc' },
{ label: 'Fee ascending', value: 'fee-asc' }, { label: 'Fee ascending', value: 'fee-asc' },
...@@ -18,7 +18,7 @@ export const SORT_OPTIONS: Array<SelectOption<TransactionsSortingValue>> = [ ...@@ -18,7 +18,7 @@ export const SORT_OPTIONS: Array<SelectOption<TransactionsSortingValue>> = [
{ label: 'Block number ascending', value: 'block_number-asc' }, { label: 'Block number ascending', value: 'block_number-asc' },
]; ];
type SortingValue = TransactionsSortingValue | undefined; type SortingValue = TransactionsSortingValue;
type HookResult = UseQueryResult<TxsResponse, ResourceError<unknown>> & { type HookResult = UseQueryResult<TxsResponse, ResourceError<unknown>> & {
sorting: SortingValue; sorting: SortingValue;
...@@ -29,11 +29,11 @@ export default function useTxsSort( ...@@ -29,11 +29,11 @@ export default function useTxsSort(
queryResult: UseQueryResult<TxsResponse, ResourceError<unknown>>, queryResult: UseQueryResult<TxsResponse, ResourceError<unknown>>,
): HookResult { ): HookResult {
const [ sorting, setSorting ] = React.useState<SortingValue>(cookies.get(cookies.NAMES.TXS_SORT) as SortingValue); const [ sorting, setSorting ] = React.useState<SortingValue>((cookies.get(cookies.NAMES.TXS_SORT) as SortingValue | undefined) ?? 'default');
const setSortByValue = React.useCallback((value: SortingValue) => { const setSortByValue = React.useCallback((value: SortingValue) => {
setSorting((prevVal: SortingValue) => { setSorting((prevVal: SortingValue) => {
let newVal: SortingValue = undefined; let newVal: SortingValue = 'default';
if (value !== prevVal) { if (value !== prevVal) {
newVal = value as SortingValue; newVal = value as SortingValue;
} }
......
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