⚡️ feat: create switch component
This commit is contained in:
parent
ea44efa0f2
commit
ab2d87fd09
@ -0,0 +1,84 @@
|
||||
import { tv, type VariantProps } from 'tailwind-variants';
|
||||
|
||||
export const switchTheme = tv({
|
||||
slots: {
|
||||
wrapper: ['flex', 'items-start', 'gap-4', 'w-[375px]'],
|
||||
switch: [
|
||||
'h-6',
|
||||
'w-12',
|
||||
'rounded-full',
|
||||
'transition-all',
|
||||
'duration-500',
|
||||
'relative',
|
||||
'cursor-default',
|
||||
'shadow-inset',
|
||||
'focus-ring',
|
||||
'outline-none',
|
||||
],
|
||||
thumb: [
|
||||
'block',
|
||||
'h-4',
|
||||
'w-4',
|
||||
'translate-x-1',
|
||||
'transition-transform',
|
||||
'duration-100',
|
||||
'will-change-transform',
|
||||
'rounded-full',
|
||||
'shadow-button',
|
||||
'data-[state=checked]:translate-x-7',
|
||||
'bg-controls-elevated',
|
||||
],
|
||||
label: [
|
||||
'flex',
|
||||
'flex-1',
|
||||
'flex-col',
|
||||
'px-1',
|
||||
'gap-1',
|
||||
'text-sm',
|
||||
'text-elements-high-em',
|
||||
'tracking-[-0.006em]',
|
||||
],
|
||||
description: ['text-xs', 'text-elements-low-em'],
|
||||
},
|
||||
variants: {
|
||||
checked: {
|
||||
true: {
|
||||
switch: [
|
||||
'bg-controls-primary',
|
||||
'hover:bg-controls-primary-hovered',
|
||||
'focus-visible:bg-controls-primary-hovered',
|
||||
],
|
||||
},
|
||||
false: {
|
||||
switch: [
|
||||
'bg-controls-inset',
|
||||
'hover:bg-controls-inset-hovered',
|
||||
'focus-visible:bg-controls-inset-hovered',
|
||||
],
|
||||
},
|
||||
},
|
||||
disabled: {
|
||||
true: {
|
||||
switch: ['bg-controls-disabled', 'cursor-not-allowed'],
|
||||
thumb: ['bg-elements-on-disabled'],
|
||||
},
|
||||
},
|
||||
fullWidth: {
|
||||
true: {
|
||||
wrapper: ['w-full', 'justify-between'],
|
||||
},
|
||||
},
|
||||
},
|
||||
compoundVariants: [
|
||||
{
|
||||
checked: true,
|
||||
disabled: true,
|
||||
class: {
|
||||
switch: ['bg-controls-disabled-active'],
|
||||
thumb: ['bg-snowball-900'],
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
export type SwitchVariants = VariantProps<typeof switchTheme>;
|
85
packages/frontend/src/components/shared/Switch/Switch.tsx
Normal file
85
packages/frontend/src/components/shared/Switch/Switch.tsx
Normal file
@ -0,0 +1,85 @@
|
||||
import React, { type ComponentPropsWithoutRef } from 'react';
|
||||
import { type SwitchProps as SwitchRadixProps } from '@radix-ui/react-switch';
|
||||
import * as SwitchRadix from '@radix-ui/react-switch';
|
||||
|
||||
import { switchTheme, type SwitchVariants } from './Switch.theme';
|
||||
|
||||
interface SwitchProps
|
||||
extends Omit<SwitchRadixProps, 'checked'>,
|
||||
SwitchVariants {
|
||||
/**
|
||||
* The label of the switch.
|
||||
*/
|
||||
label?: string;
|
||||
/**
|
||||
* The description of the switch.
|
||||
*/
|
||||
description?: string;
|
||||
/**
|
||||
* Custom wrapper props for the switch.
|
||||
*/
|
||||
wrapperProps?: ComponentPropsWithoutRef<'div'>;
|
||||
/**
|
||||
* Function that is called when the checked state of the switch changes.
|
||||
* @param checked The new checked state of the switch.
|
||||
*/
|
||||
onCheckedChange?(checked: boolean): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* A switch is a component used for toggling between two states.
|
||||
*/
|
||||
export const Switch = ({
|
||||
className,
|
||||
checked,
|
||||
label,
|
||||
description,
|
||||
disabled,
|
||||
name,
|
||||
wrapperProps,
|
||||
fullWidth,
|
||||
...props
|
||||
}: SwitchProps) => {
|
||||
const {
|
||||
wrapper,
|
||||
switch: switchClass,
|
||||
thumb,
|
||||
label: labelClass,
|
||||
description: descriptionClass,
|
||||
} = switchTheme({
|
||||
checked,
|
||||
disabled,
|
||||
fullWidth,
|
||||
});
|
||||
|
||||
const switchComponent = (
|
||||
<SwitchRadix.Root
|
||||
{...props}
|
||||
checked={checked}
|
||||
disabled={disabled}
|
||||
className={switchClass({ className })}
|
||||
>
|
||||
<SwitchRadix.Thumb className={thumb()} />
|
||||
</SwitchRadix.Root>
|
||||
);
|
||||
|
||||
// If a label is provided, wrap the switch in a label element.
|
||||
if (label) {
|
||||
return (
|
||||
<div
|
||||
{...wrapperProps}
|
||||
className={wrapper({ className: wrapperProps?.className })}
|
||||
>
|
||||
<label className={labelClass()} htmlFor={name}>
|
||||
{label}
|
||||
{description && (
|
||||
<span className={descriptionClass()}>{description}</span>
|
||||
)}
|
||||
</label>
|
||||
{switchComponent}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return switchComponent;
|
||||
};
|
1
packages/frontend/src/components/shared/Switch/index.ts
Normal file
1
packages/frontend/src/components/shared/Switch/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './Switch';
|
Loading…
Reference in New Issue
Block a user