import * as React from "react"; import { forwardRef, useEffect, useState } from "react"; import { cn } from "~/utils/cn"; import { Badge } from "./Badge"; import { Paragraph } from "./Paragraph"; const variants = { "simple/small": { button: "w-fit pr-4", label: "text-sm text-text-bright mt-0.5 select-none", description: "text-text-dimmed", inputPosition: "mt-1", isChecked: "", isDisabled: "opacity-70", }, simple: { button: "w-fit pr-4", label: "text-text-bright select-none", description: "text-text-dimmed", inputPosition: "mt-1", isChecked: "", isDisabled: "opacity-70", }, "button/small": { button: "flex items-center w-fit h-8 pl-2 pr-3 rounded border border-border-bright hover:bg-background-dimmed hover:border-border-brightest transition", label: "text-sm text-text-bright select-none", description: "text-text-dimmed", inputPosition: "mt-0", isChecked: "bg-background-dimmed border-grid-dimmed hover:bg-background-dimmed!", isDisabled: "opacity-70 hover:bg-transparent", }, button: { button: "w-fit py-2 pl-3 pr-4 rounded border border-border-bright hover:bg-background-dimmed hover:border-border-brightest transition", label: "text-text-bright select-none", description: "text-text-dimmed", inputPosition: "mt-1", isChecked: "bg-background-dimmed border-grid-dimmed hover:bg-background-dimmed!", isDisabled: "opacity-70 hover:bg-transparent", }, description: { button: "w-full py-2 pl-3 pr-4 checked:hover:bg-background-dimmed transition", label: "text-text-bright font-semibold", description: "text-text-dimmed", inputPosition: "mt-1", isChecked: "bg-background-dimmed", isDisabled: "opacity-70", }, }; export type CheckboxProps = Omit< React.InputHTMLAttributes, "checked" | "onChange" > & { id?: string; name?: string; value?: string; variant?: keyof typeof variants; label: React.ReactNode; description?: string; badges?: string[]; className?: string; labelClassName?: string; onChange?: (isChecked: boolean) => void; }; export const CheckboxWithLabel = React.forwardRef( ( { id, name, value, variant = "simple", type, label, description, defaultChecked, badges, disabled, className, labelClassName: externalLabelClassName, ...props }, ref ) => { const [isChecked, setIsChecked] = useState(defaultChecked ?? false); const [isDisabled, setIsDisabled] = useState(disabled ?? false); const buttonClassName = variants[variant].button; const labelClassName = variants[variant].label; const descriptionClassName = variants[variant].description; const isCheckedClassName = variants[variant].isChecked; const isDisabledClassName = variants[variant].isDisabled; const inputPositionClasses = variants[variant].inputPosition; useEffect(() => { setIsDisabled(disabled ?? false); }, [disabled]); useEffect(() => { if (props.onChange) { props.onChange(isChecked); } }, [isChecked]); useEffect(() => { setIsChecked(defaultChecked ?? false); }, [defaultChecked]); return (
{ //returning false is not setting the state to false, it stops the event from bubbling up if (isDisabled || props.readOnly === true) return false; setIsChecked((c) => !c); }} > { //returning false is not setting the state to false, it stops the event from bubbling up if (isDisabled || props.readOnly === true) return false; setIsChecked(!isChecked); }} disabled={isDisabled} className={cn( inputPositionClasses, props.readOnly || disabled ? "cursor-default" : "cursor-pointer", "read-only:border-border-bright disabled:border-border-bright rounded-sm border border-border-bright bg-transparent transition checked:bg-indigo-500! read-only:bg-background-raised! group-hover:bg-background-deep checked:group-hover:bg-indigo-500 group-focus:ring-1 focus:ring-indigo-500 focus:ring-offset-0 focus:ring-offset-transparent focus-visible:outline-hidden focus-visible:ring-indigo-500 disabled:bg-background-raised!" )} id={id} ref={ref} />
{badges && ( {badges.map((badge) => ( {badge} ))} )}
{variant === "description" && ( {description} )}
); } ); type SimpleCheckboxProps = Omit, "type">; export const Checkbox = forwardRef( ({ className, ...props }: SimpleCheckboxProps, ref) => { return ( ); } );