Commit cdb1793c authored by tom's avatar tom

update checkbox, radio and button recipies

parent 2c11ad9b
......@@ -36,7 +36,7 @@ const RESTRICTED_MODULES = {
'Tag', 'Switch', 'Image', 'Popover', 'PopoverTrigger', 'PopoverContent', 'PopoverBody', 'PopoverFooter',
'DrawerRoot', 'DrawerBody', 'DrawerContent', 'DrawerOverlay', 'DrawerBackdrop', 'DrawerTrigger', 'Drawer',
'Alert', 'AlertIcon', 'AlertTitle', 'AlertDescription',
'Heading', 'Badge', 'Tabs', 'Show', 'Hide', 'Checkbox',
'Heading', 'Badge', 'Tabs', 'Show', 'Hide', 'Checkbox', 'CheckboxGroup',
'Table', 'TableRoot', 'TableBody', 'TableHeader', 'TableRow', 'TableCell',
'Menu', 'MenuRoot', 'MenuTrigger', 'MenuContent', 'MenuItem', 'MenuTriggerItem', 'MenuRadioItemGroup', 'MenuContextTrigger',
],
......
......@@ -22,7 +22,7 @@ export interface ButtonProps extends ChakraButtonProps, ButtonLoadingProps {
highlighted?: boolean;
}
export const Button = React.forwardRef<HTMLDivElement, ButtonProps>(
export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
function Button(props, ref) {
const { loading, disabled, loadingText, children, expanded, selected, highlighted, loadingSkeleton = false, ...rest } = props;
......@@ -51,8 +51,9 @@ export const Button = React.forwardRef<HTMLDivElement, ButtonProps>(
})();
return (
<Skeleton loading={ loadingSkeleton } asChild ref={ ref }>
<Skeleton loading={ loadingSkeleton } asChild>
<ChakraButton
ref={ ref }
{ ...(expanded ? { 'data-expanded': true } : {}) }
{ ...(selected ? { 'data-selected': true } : {}) }
{ ...(highlighted ? { 'data-highlighted': true } : {}) }
......
import { Checkbox as ChakraCheckbox } from '@chakra-ui/react';
import type { Checkbox as ArkCheckbox } from '@ark-ui/react/checkbox';
import { Checkbox as ChakraCheckbox, CheckboxGroup as ChakraCheckboxGroup } from '@chakra-ui/react';
import * as React from 'react';
export interface CheckboxProps extends ChakraCheckbox.RootProps {
......@@ -23,3 +24,25 @@ export const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(
);
},
);
export interface CheckboxGroupProps extends ArkCheckbox.GroupProps {
orientation?: 'vertical' | 'horizontal';
}
export const CheckboxGroup = React.forwardRef<HTMLDivElement, CheckboxGroupProps>(
function CheckboxGroup(props, ref) {
const { children, orientation = 'horizontal', ...rest } = props;
return (
<ChakraCheckboxGroup
ref={ ref }
orientation={ orientation }
display="flex"
flexDirection={ orientation === 'vertical' ? 'column' : 'row' }
gap={ orientation === 'vertical' ? 3 : 8 }
{ ...rest }
>
{ children }
</ChakraCheckboxGroup>
);
},
);
......@@ -6,7 +6,7 @@ export interface IconButtonProps extends ButtonProps {}
// TODO @tom2drum variants for icon buttons: prev-next, top-bar, copy-to-clipboard, filter column
export const IconButton = React.forwardRef<HTMLDivElement, IconButtonProps>(
export const IconButton = React.forwardRef<HTMLButtonElement, IconButtonProps>(
function IconButton(props, ref) {
const { size, variant = 'plain', ...rest } = props;
......
......@@ -23,18 +23,4 @@ export const Radio = React.forwardRef<HTMLInputElement, RadioProps>(
export interface RadioGroupProps extends ChakraRadioGroup.RootProps {}
export const RadioGroup = React.forwardRef<HTMLDivElement, RadioGroupProps>(
function RadioGroup(props, ref) {
const { orientation = 'horizontal', ...rest } = props;
return (
<ChakraRadioGroup.Root
ref={ ref }
orientation={ orientation }
display="flex"
flexDirection={ orientation === 'horizontal' ? 'row' : 'column' }
gap={ orientation === 'horizontal' ? 4 : 2 }
{ ...rest }
/>
);
},
);
export const RadioGroup = ChakraRadioGroup.Root;
......@@ -377,18 +377,20 @@ const semanticTokens: ThemingConfig['semanticTokens'] = {
},
},
checkbox: {
icon: {
bg: {
checked: { value: { _light: '{colors.blue.500}', _dark: '{colors.gray.300}' } },
hover: { value: { _light: '{colors.blue.600}', _dark: '{colors.gray.400}' } },
control: {
border: {
DEFAULT: { value: { _light: '{colors.gray.100}', _dark: '{colors.gray.700}' } },
hover: { value: { _light: '{colors.gray.200}', _dark: '{colors.gray.500}' } },
readOnly: { value: { _light: '{colors.gray.200}', _dark: '{colors.gray.800}' } },
},
},
},
radio: {
icon: {
bg: {
checked: { value: { _light: '{colors.blue.500}', _dark: '{colors.gray.300}' } },
hover: { value: { _light: '{colors.blue.600}', _dark: '{colors.gray.400}' } },
control: {
border: {
DEFAULT: { value: { _light: '{colors.gray.100}', _dark: '{colors.gray.700}' } },
hover: { value: { _light: '{colors.gray.200}', _dark: '{colors.gray.500}' } },
readOnly: { value: { _light: '{colors.gray.200}', _dark: '{colors.gray.800}' } },
},
},
},
......@@ -477,6 +479,11 @@ const semanticTokens: ThemingConfig['semanticTokens'] = {
DEFAULT: { value: { _light: '{shadows.size.lg}', _dark: '{shadows.dark-lg}' } },
},
},
opacity: {
control: {
disabled: { value: '0.2' },
},
},
};
export default semanticTokens;
......@@ -4,11 +4,10 @@ export const recipe = defineRecipe({
base: {
display: 'flex',
gap: 0,
borderRadius: 'base',
fontWeight: 600,
overflow: 'hidden',
_disabled: {
opacity: 0.2,
opacity: 'control.disabled',
},
// FIXME have to override the Chakra UI styles for the SVG icon inside the button
// try to find a better solution
......@@ -26,14 +25,17 @@ export const recipe = defineRecipe({
color: 'white',
'&:not([data-loading-skeleton])': {
bgColor: 'blue.600',
_expanded: {
bg: 'blue.400',
},
},
_hover: {
bg: 'blue.400',
},
_expanded: { bg: 'blue.400' },
_loading: {
opacity: 1,
'& .chakra-spinner': {
borderColor: 'white',
borderColor: 'gray.200',
borderBottomColor: 'spinner.track',
borderInlineStartColor: 'spinner.track',
},
......@@ -53,6 +55,14 @@ export const recipe = defineRecipe({
color: 'blue.400',
borderColor: 'blue.400',
},
_loading: {
opacity: 1,
'& .chakra-spinner': {
borderColor: 'button.outline.fg',
borderBottomColor: 'spinner.track',
borderInlineStartColor: 'spinner.track',
},
},
},
dropdown: {
borderWidth: '0px',
......@@ -68,6 +78,14 @@ export const recipe = defineRecipe({
color: 'blue.400',
borderColor: 'blue.400',
},
_loading: {
opacity: 1,
'& .chakra-spinner': {
borderColor: 'blue.500',
borderBottomColor: 'spinner.track',
borderInlineStartColor: 'spinner.track',
},
},
// When the dropdown is open, the button should be active
_expanded: {
bg: 'transparent',
......@@ -99,6 +117,14 @@ export const recipe = defineRecipe({
color: 'blue.400',
borderColor: 'blue.400',
},
_loading: {
opacity: 1,
'& .chakra-spinner': {
borderColor: 'blue.500',
borderBottomColor: 'spinner.track',
borderInlineStartColor: 'spinner.track',
},
},
_selected: {
bg: 'button.header.bg.selected',
color: 'button.header.fg.selected',
......@@ -127,8 +153,9 @@ export const recipe = defineRecipe({
bg: 'button.hero.bg',
},
_loading: {
opacity: 1,
'& .chakra-spinner': {
borderColor: 'white',
borderColor: 'button.hero.fg',
borderBottomColor: 'spinner.track',
borderInlineStartColor: 'spinner.track',
},
......@@ -216,10 +243,10 @@ export const recipe = defineRecipe({
},
},
size: {
xs: { px: 2, h: 6, fontSize: '12px' },
sm: { px: 2, h: 8, fontSize: '14px' },
md: { px: 4, h: 10, fontSize: '16px' },
lg: { px: 6, h: 12, fontSize: '20px' },
'2xs': { px: 2, h: 5, textStyle: 'xs', borderRadius: 'sm', gap: 1 },
xs: { px: 2, h: 6, textStyle: 'sm', borderRadius: 'sm', gap: 1 },
sm: { px: 3, h: 8, textStyle: 'md', borderRadius: 'base', gap: 1 },
md: { px: 3, h: 10, textStyle: 'md', borderRadius: 'base', gap: 2, '& .chakra-spinner': { '--spinner-size': '20px' } },
},
},
defaultVariants: {
......
......@@ -12,6 +12,13 @@ export const recipe = defineSlotRecipe({
alignItems: 'center',
verticalAlign: 'top',
position: 'relative',
cursor: 'pointer',
_disabled: {
cursor: 'disabled',
},
_readOnly: {
cursor: 'default',
},
},
control: checkmarkRecipe.base,
......@@ -20,23 +27,13 @@ export const recipe = defineSlotRecipe({
fontWeight: 'normal',
userSelect: 'none',
_disabled: {
opacity: '0.5',
opacity: 'control.disabled',
},
},
},
variants: {
size: {
xs: {
root: { gap: '1' },
label: { textStyle: 'xs' },
control: checkmarkRecipe.variants?.size?.xs,
},
sm: {
root: { gap: '1' },
label: { textStyle: 'sm' },
control: checkmarkRecipe.variants?.size?.sm,
},
md: {
root: { gap: '2' },
label: { textStyle: 'md' },
......
import { defineRecipe } from '@chakra-ui/react';
// TODO @tom2drum dark mode + border color
export const recipe = defineRecipe({
className: 'chakra-checkmark',
base: {
......@@ -16,7 +15,7 @@ export const recipe = defineRecipe({
boxSize: 'full',
},
_disabled: {
opacity: '0.5',
opacity: 'control.disabled',
},
},
variants: {
......@@ -37,14 +36,30 @@ export const recipe = defineRecipe({
variant: {
solid: {
borderColor: 'border',
borderColor: 'checkbox.control.border',
_hover: {
borderColor: 'checkbox.control.border.hover',
},
_readOnly: {
borderColor: 'checkbox.control.border.readOnly',
_hover: {
borderColor: 'checkbox.control.border.readOnly',
},
'&:is([data-state=checked], [data-state=indeterminate])': {
bg: 'checkbox.control.border.readOnly',
color: 'gray.500',
_hover: {
bg: 'checkbox.control.border.readOnly',
},
},
},
'&:is([data-state=checked], [data-state=indeterminate])': {
bg: 'checkbox.icon.bg.checked',
bg: 'blue.500',
color: 'white',
borderColor: 'checkbox.icon.bg.checked',
borderColor: 'blue.500',
_hover: {
bg: 'checkbox.icon.bg.hover',
borderColor: 'checkbox.icon.bg.hover',
bg: 'blue.400',
borderColor: 'blue.400',
},
},
},
......
......@@ -3,25 +3,33 @@ import { defineSlotRecipe } from '@chakra-ui/react';
import { recipe as radiomarkRecipe } from './radiomark.recipe';
export const recipe = defineSlotRecipe({
slots: [ 'item', 'itemControl', 'label' ],
slots: [ 'item', 'itemControl', 'itemText', 'root' ],
base: {
root: {
display: 'flex',
},
item: {
display: 'inline-flex',
alignItems: 'center',
position: 'relative',
fontWeight: 'normal',
cursor: 'pointer',
_disabled: {
cursor: 'disabled',
},
_readOnly: {
cursor: 'default',
},
},
itemControl: radiomarkRecipe.base,
label: {
itemText: {
userSelect: 'none',
textStyle: 'md',
_disabled: {
opacity: '0.5',
opacity: 'control.disabled',
},
},
},
......@@ -33,25 +41,33 @@ export const recipe = defineSlotRecipe({
},
size: {
xs: {
item: { textStyle: 'xs', gap: '1' },
itemControl: radiomarkRecipe.variants?.size?.xs,
},
sm: {
item: { textStyle: 'sm', gap: '1' },
itemControl: radiomarkRecipe.variants?.size?.sm,
},
md: {
item: { textStyle: 'md', gap: '2' },
itemControl: radiomarkRecipe.variants?.size?.md,
},
},
orientation: {
vertical: {
root: {
flexDirection: 'column',
alignItems: 'flex-start',
rowGap: '12px',
},
},
horizontal: {
root: {
flexDirection: 'row',
alignItems: 'center',
columnGap: '32px',
},
},
},
},
defaultVariants: {
size: 'md',
variant: 'solid',
orientation: 'horizontal',
},
});
import { defineRecipe } from '@chakra-ui/react';
// TODO @tom2drum dark mode + border color
export const recipe = defineRecipe({
base: {
display: 'inline-flex',
......@@ -12,15 +11,14 @@ export const recipe = defineRecipe({
borderWidth: '2px',
borderColor: 'transparent',
borderRadius: 'full',
cursor: 'radio',
_focusVisible: {
outline: '2px solid',
outlineColor: 'colorPalette.focusRing',
outlineOffset: '2px',
},
_disabled: {
opacity: '0.5',
cursor: 'disabled',
opacity: 'control.disabled',
},
'& .dot': {
......@@ -36,14 +34,36 @@ export const recipe = defineRecipe({
variant: {
solid: {
borderWidth: '2px',
borderColor: 'border',
borderColor: 'radio.control.border',
_hover: {
borderColor: 'radio.control.border.hover',
},
_checked: {
bg: 'radio.icon.bg.checked',
bg: 'blue.500',
color: 'white',
borderColor: 'radio.icon.bg.checked',
borderColor: 'blue.500',
_hover: {
bg: 'blue.400',
borderColor: 'blue.400',
},
},
_invalid: {
bg: 'red.500',
borderColor: 'red.500',
},
_readOnly: {
borderColor: 'radio.control.border.readOnly',
_hover: {
bg: 'radio.icon.bg.hover',
borderColor: 'radio.icon.bg.hover',
borderColor: 'radio.control.border.readOnly',
},
_checked: {
bg: 'radio.control.border.readOnly',
_hover: {
bg: 'radio.control.border.readOnly',
},
'& .dot': {
bg: 'gray.500',
},
},
},
},
......
......@@ -17,6 +17,9 @@ const ButtonShowcase = () => {
<Section>
<SectionHeader>Size</SectionHeader>
<SamplesStack>
<Sample label="size: 2xs">
<Button size="2xs">Content</Button>
</Sample>
<Sample label="size: xs">
<Button size="xs">Content</Button>
</Sample>
......@@ -26,9 +29,6 @@ const ButtonShowcase = () => {
<Sample label="size: md">
<Button size="md">Content</Button>
</Sample>
<Sample label="size: lg">
<Button size="lg">Content</Button>
</Sample>
</SamplesStack>
</Section>
......@@ -40,12 +40,14 @@ const ButtonShowcase = () => {
<Button variant="solid">Default</Button>
<Button variant="solid" data-hover>Hovered</Button>
<Button variant="solid" disabled>Disabled</Button>
<Button variant="solid" loading>Loading</Button>
</Sample>
<Sample label="variant: outline">
<Button variant="outline">Default</Button>
<Button variant="outline" data-hover>Hovered</Button>
<Button variant="outline" disabled>Disabled</Button>
<Button variant="outline" loading>Loading</Button>
</Sample>
<Sample label="variant: link">
......@@ -97,6 +99,7 @@ const ButtonShowcase = () => {
</PopoverContent>
</PopoverRoot>
<Button variant="dropdown" disabled>Disabled</Button>
<Button variant="dropdown" loading>Loading</Button>
<PopoverRoot>
<Tooltip content="Tooltip content">
......@@ -202,6 +205,9 @@ const ButtonShowcase = () => {
<SamplesStack>
<Sample label="loading: true, loadingText: 'Loading'">
<Button loading loadingText="Loading">Content</Button>
<Button loading loadingText="Loading" size="sm">Content</Button>
<Button loading loadingText="Loading" size="xs">Content</Button>
<Button loading loadingText="Loading" size="2xs">Content</Button>
</Sample>
<Sample label="loading: true, loadingText: undefined">
<Button loading>Content</Button>
......
import React from 'react';
import { Checkbox } from 'toolkit/chakra/checkbox';
import { Checkbox, CheckboxGroup } from 'toolkit/chakra/checkbox';
import { Section, Container, SectionHeader, SamplesStack, Sample } from './parts';
......@@ -14,8 +14,6 @@ const CheckboxShowcase = () => {
<Sample label="variant: subtle">
<Checkbox>Option 1</Checkbox>
<Checkbox checked>Option 2</Checkbox>
<Checkbox disabled>Option 3</Checkbox>
<Checkbox checked disabled>Option 4</Checkbox>
</Sample>
</SamplesStack>
</Section>
......@@ -23,20 +21,50 @@ const CheckboxShowcase = () => {
<Section>
<SectionHeader>Size</SectionHeader>
<SamplesStack>
<Sample label="size: xs">
<Checkbox size="xs">Option 1</Checkbox>
<Checkbox size="xs">Option 2</Checkbox>
</Sample>
<Sample label="size: sm">
<Checkbox size="sm">Option 1</Checkbox>
<Checkbox size="sm">Option 2</Checkbox>
</Sample>
<Sample label="size: md">
<Checkbox size="md">Option 1</Checkbox>
<Checkbox size="md">Option 2</Checkbox>
</Sample>
</SamplesStack>
</Section>
<Section>
<SectionHeader>Disabled</SectionHeader>
<SamplesStack>
<Sample label="disabled: true">
<Checkbox disabled>Option 1</Checkbox>
<Checkbox checked disabled>Option 2</Checkbox>
</Sample>
</SamplesStack>
</Section>
<Section>
<SectionHeader>Read-only</SectionHeader>
<SamplesStack>
<Sample label="readOnly: true">
<Checkbox readOnly>Option 1</Checkbox>
<Checkbox checked readOnly>Option 2</Checkbox>
</Sample>
</SamplesStack>
</Section>
<Section>
<SectionHeader>Orientation</SectionHeader>
<SamplesStack>
<Sample label="orientation: vertical">
<CheckboxGroup orientation="vertical">
<Checkbox value="1">Option 1</Checkbox>
<Checkbox value="2">Option 2</Checkbox>
</CheckboxGroup>
</Sample>
<Sample label="orientation: horizontal">
<CheckboxGroup orientation="horizontal">
<Checkbox value="1">Option 1</Checkbox>
<Checkbox value="2">Option 2</Checkbox>
</CheckboxGroup>
</Sample>
</SamplesStack>
</Section>
</Container>
);
};
......
......@@ -73,6 +73,18 @@ const RadioShowcase = () => {
</Sample>
</SamplesStack>
</Section>
<Section>
<SectionHeader>Read-only</SectionHeader>
<SamplesStack>
<Sample label="readOnly: true">
<RadioGroup defaultValue="1" readOnly>
<Radio value="1">Option 1</Radio>
<Radio value="2">Option 2</Radio>
</RadioGroup>
</Sample>
</SamplesStack>
</Section>
</Container>
);
};
......
......@@ -38,6 +38,7 @@ const LogoFallback = ({ isCollapsed, isSmall }: { isCollapsed?: boolean; isSmall
const INVERT_FILTER = 'brightness(0) invert(1)';
// TODO @tom2drum check loading state
const NetworkLogo = ({ isCollapsed, onClick, className }: Props) => {
const logoSrc = useColorModeValue(config.UI.navigation.logo.default, config.UI.navigation.logo.dark || config.UI.navigation.logo.default);
......
import { Box, Flex, Link, chakra } from '@chakra-ui/react';
import { Box, Flex, chakra } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import React from 'react';
......@@ -9,8 +9,8 @@ import { space } from 'lib/html-entities';
import getPageType from 'lib/mixpanel/getPageType';
import * as mixpanel from 'lib/mixpanel/index';
import { Button } from 'toolkit/chakra/button';
import { Link } from 'toolkit/chakra/link';
import { PopoverBody, PopoverContent, PopoverRoot, PopoverTrigger } from 'toolkit/chakra/popover';
import { useDisclosure } from 'toolkit/hooks/useDisclosure';
import IconSvg from 'ui/shared/IconSvg';
import DeFiDropdownItem from './DeFiDropdownItem';
......@@ -20,7 +20,6 @@ const feature = config.features.deFiDropdown;
const DeFiDropdown = () => {
const router = useRouter();
const source = getPageType(router.pathname);
const { open, onToggle, onOpenChange } = useDisclosure();
const handleClick = React.useCallback((content: string) => {
mixpanel.logEvent(mixpanel.EventTypes.BUTTON_CLICK, { Content: content, Source: source });
......@@ -30,29 +29,15 @@ const DeFiDropdown = () => {
return null;
}
const buttonStyles = {
variant: 'solid' as const,
borderRadius: 'sm',
textStyle: 'xs',
height: 5,
px: 1.5,
fontWeight: '500',
gap: 0,
};
const items = feature.items.map((item) => ({
...item,
onClick: () => handleClick(item.text),
}));
return items.length > 1 ? (
<PopoverRoot open={ open } onOpenChange={ onOpenChange } positioning={{ placement: 'bottom-start' }} lazyMount>
<PopoverRoot>
<PopoverTrigger>
<Button
onClick={ onToggle }
expanded={ open }
{ ...buttonStyles }
>
<Button size="2xs" gap={ 0 }>
<chakra.span display={{ base: 'none', lg: 'inline' }} whiteSpace="pre-wrap">
Blockscout{ space }
</chakra.span>
......@@ -71,14 +56,7 @@ const DeFiDropdown = () => {
</PopoverContent>
</PopoverRoot>
) : (
<Button
asChild
onClick={ items[0].onClick }
_hover={{
color: 'white',
}}
{ ...buttonStyles }
>
<Link
href={
items[0].dappId ?
......@@ -86,13 +64,15 @@ const DeFiDropdown = () => {
items[0].url
}
target={ items[0].dappId ? '_self' : '_blank' }
asChild
>
<Button onClick={ items[0].onClick } size="2xs">
<IconSvg name={ items[0].icon } boxSize={ 3 } mr={{ base: 0, sm: 1 }}/>
<Box display={{ base: 'none', sm: 'inline' }}>
{ items[0].text }
</Box>
</Link>
</Button>
</Link>
);
};
......
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