️ feat: implement card style for radio

This commit is contained in:
Andre H 2024-03-01 10:41:12 +08:00
parent d9392c095d
commit a8ad6c6eec
3 changed files with 47 additions and 10 deletions

View File

@ -2,7 +2,7 @@ import { VariantProps, tv } from 'tailwind-variants';
export const radioTheme = tv({ export const radioTheme = tv({
slots: { slots: {
root: ['flex', 'gap-3', 'flex-wrap'], root: ['flex', 'gap-3'],
wrapper: ['flex', 'items-center', 'gap-2', 'group'], wrapper: ['flex', 'items-center', 'gap-2', 'group'],
label: ['text-sm', 'tracking-[-0.006em]', 'text-elements-high-em'], label: ['text-sm', 'tracking-[-0.006em]', 'text-elements-high-em'],
radio: [ radio: [
@ -39,15 +39,34 @@ export const radioTheme = tv({
'after:data-[state=checked]:group-hover:bg-elements-on-primary', 'after:data-[state=checked]:group-hover:bg-elements-on-primary',
'after:data-[state=checked]:group-focus-visible:bg-elements-on-primary', 'after:data-[state=checked]:group-focus-visible:bg-elements-on-primary',
], ],
icon: ['w-[18px]', 'h-[18px]'],
}, },
variants: { variants: {
orientation: { orientation: {
vertical: { root: ['flex-col'] }, vertical: { root: ['flex-col'] },
horizontal: { root: ['flex-row'] }, horizontal: { root: ['flex-row'] },
}, },
variant: {
unstyled: {},
card: {
wrapper: [
'px-4',
'py-3',
'rounded-lg',
'border',
'border-border-interactive',
'bg-controls-tertiary',
'shadow-button',
'w-full',
'cursor-pointer',
],
label: ['select-none', 'cursor-pointer'],
},
},
}, },
defaultVariants: { defaultVariants: {
orientation: 'vertical', orientation: 'vertical',
variant: 'unstyled',
}, },
}); });

View File

@ -49,14 +49,15 @@ export const Radio = ({
className, className,
options, options,
orientation, orientation,
variant,
...props ...props
}: RadioProps) => { }: RadioProps) => {
const { root } = radioTheme({ orientation }); const { root } = radioTheme({ orientation, variant });
return ( return (
<RadixRoot {...props} className={root({ className })}> <RadixRoot {...props} className={root({ className })}>
{options.map((option) => ( {options.map((option) => (
<RadioItem key={option.value} {...option} /> <RadioItem key={option.value} variant={variant} {...option} />
))} ))}
</RadixRoot> </RadixRoot>
); );

View File

@ -1,13 +1,16 @@
import React, { ComponentPropsWithoutRef } from 'react'; import React, { ReactNode, ComponentPropsWithoutRef } from 'react';
import { import {
Item as RadixRadio, Item as RadixRadio,
Indicator as RadixIndicator, Indicator as RadixIndicator,
RadioGroupItemProps, RadioGroupItemProps,
RadioGroupIndicatorProps, RadioGroupIndicatorProps,
} from '@radix-ui/react-radio-group'; } from '@radix-ui/react-radio-group';
import { radioTheme } from './Radio.theme'; import { RadioTheme, radioTheme } from './Radio.theme';
import { cloneIcon } from 'utils/cloneIcon';
export interface RadioItemProps extends RadioGroupItemProps { export interface RadioItemProps
extends RadioGroupItemProps,
Pick<RadioTheme, 'variant'> {
/** /**
* The wrapper props of the radio item. * The wrapper props of the radio item.
* You can use this prop to customize the wrapper props. * You can use this prop to customize the wrapper props.
@ -27,6 +30,10 @@ export interface RadioItemProps extends RadioGroupItemProps {
* The id of the radio item. * The id of the radio item.
*/ */
id?: string; id?: string;
/**
* The left icon of the radio item.
*/
leftIcon?: ReactNode;
/** /**
* The label of the radio item. * The label of the radio item.
*/ */
@ -41,18 +48,26 @@ export const RadioItem = ({
wrapperProps, wrapperProps,
labelProps, labelProps,
indicatorProps, indicatorProps,
leftIcon,
label, label,
id, id,
variant,
...props ...props
}: RadioItemProps) => { }: RadioItemProps) => {
const { wrapper, label: labelClass, radio, indicator } = radioTheme(); const {
wrapper,
label: labelClass,
radio,
indicator,
icon,
} = radioTheme({ variant });
// Generate a unique id for the radio item from the label if the id is not provided // Generate a unique id for the radio item from the label if the id is not provided
const kebabCaseLabel = label?.toLowerCase().replace(/\s+/g, '-'); const kebabCaseLabel = label?.toLowerCase().replace(/\s+/g, '-');
const componentId = id ?? kebabCaseLabel; const componentId = id ?? kebabCaseLabel;
return ( return (
<div className={wrapper({ className: wrapperProps?.className })}> <label className={wrapper({ className: wrapperProps?.className })}>
<RadixRadio {...props} className={radio({ className })} id={componentId}> <RadixRadio {...props} className={radio({ className })} id={componentId}>
<RadixIndicator <RadixIndicator
forceMount forceMount
@ -60,15 +75,17 @@ export const RadioItem = ({
className={indicator({ className: indicatorProps?.className })} className={indicator({ className: indicatorProps?.className })}
/> />
</RadixRadio> </RadixRadio>
{leftIcon &&
cloneIcon(leftIcon, { className: icon(), 'aria-hidden': true })}
{label && ( {label && (
<label <label
{...labelProps} {...labelProps}
className={labelClass({ className: labelProps?.className })}
htmlFor={componentId} htmlFor={componentId}
className={labelClass({ className: labelProps?.className })}
> >
{label} {label}
</label> </label>
)} )}
</div> </label>
); );
}; };