diff --git a/packages/react/components/Checkbox.tsx b/packages/react/components/Checkbox.tsx new file mode 100644 index 0000000..0bd0bd8 --- /dev/null +++ b/packages/react/components/Checkbox.tsx @@ -0,0 +1,96 @@ +import React from "react"; +import { VariantProps, vcn } from "../shared"; + +const checkboxColors = { + background: { + default: "bg-neutral-200 dark:bg-neutral-700", + checked: "bg-neutral-300 dark:bg-neutral-400", + disabled: + "peer-disabled/checkbox:bg-neutral-100 dark:peer-disabled/checkbox:bg-neutral-800", + }, + checkmark: "text-black dark:text-white", +}; + +const [checkboxVariant, resolveCheckboxVariantProps] = vcn({ + base: `inline-block rounded-md p-1 size-6 ${checkboxColors.checkmark} ${checkboxColors.background.disabled} peer-disabled/checkbox:cursor-not-allowed transition-colors duration-75 ease-in-out`, + variants: { + checked: { + true: `${checkboxColors.background.checked}`, + false: `${checkboxColors.background.default}`, + }, + }, + defaults: { + checked: false, + }, +}); + +interface CheckboxProps + extends VariantProps, + Omit, "type" | "className"> {} + +const Checkbox = React.forwardRef( + (props, ref) => { + const [variantProps, otherPropsCompressed] = + resolveCheckboxVariantProps(props); + const { defaultChecked, onChange, ...otherPropsExtracted } = + otherPropsCompressed; + + // internally handles checked, so we can use checked value without prop + const [checked, setChecked] = React.useState(defaultChecked ?? false); + React.useEffect(() => { + if (typeof variantProps.checked === "boolean") { + setChecked(variantProps.checked); + } + }, [variantProps.checked]); + + const internalRef = React.useRef(null); + + return ( + <> + + + ); + } +); + +export { Checkbox }; diff --git a/packages/react/stories/Checkbox.stories.tsx b/packages/react/stories/Checkbox.stories.tsx new file mode 100644 index 0000000..bd82940 --- /dev/null +++ b/packages/react/stories/Checkbox.stories.tsx @@ -0,0 +1,59 @@ +import React from "react"; +import { Checkbox } from "../components/Checkbox"; +import { Label } from "../components/Label"; +import { Toaster, useToast } from "../components/Toast"; + +export default { + title: "React/Checkbox", +}; + +export const Default = () => { + return ; +}; + +export const DefaultChecked = () => { + return ; +}; + +export const Controlled = () => { + const [state, setState] = React.useState(false); + const { toast } = useToast(); + + React.useEffect(() => { + toast({ + title: "Checkbox Toggled", + description: `Checkbox state changed to ${state}`, + status: state ? "success" : "error", + }); + }, [state]); + + return ( + <> + + { + setState(e.currentTarget.checked); + }} + /> + + ); +}; + +export const UsingWithLabel = () => { + return ( + + ); +}; + +export const Disabled = () => { + return ( + + ); +};