import { composeRefs } from "@radix-ui/react-compose-refs"; import { selectBox, type SelectBoxVariantProps } from "@seed-design/css/recipes/select-box"; import { selectBoxCheckmark, type SelectBoxCheckmarkVariantProps, } from "@seed-design/css/recipes/select-box-checkmark"; import { selectBoxGroup, type SelectBoxGroupVariantProps, } from "@seed-design/css/recipes/select-box-group"; import { Checkbox as CheckboxPrimitive, useCheckboxContext } from "@seed-design/react-checkbox"; import { Collapsible, CollapsibleProvider, useCollapsible, useCollapsibleContext, } from "@seed-design/react-collapsible"; import { Primitive, type PrimitiveProps } from "@seed-design/react-primitive"; import clsx from "clsx"; import { createContext, forwardRef, useCallback, useContext, useState, type PropsWithChildren, } from "react"; import { createSlotRecipeContext } from "../../utils/createSlotRecipeContext"; import { createWithStateProps } from "../../utils/createWithStateProps"; import { InternalIcon, type InternalIconProps } from "../private/Icon"; const { PropsProvider, ClassNamesProvider, withContext, useProps, useClassNames } = createSlotRecipeContext(selectBox); const withStateProps = createWithStateProps([useCheckboxContext]); const FooterContext = createContext<{ isFooterRendered: boolean; footerRef: (node: HTMLDivElement | null) => void; footerVisibility: Exclude, "always">; } | null>(null); export interface CheckSelectBoxGroupProps extends SelectBoxGroupVariantProps, PrimitiveProps, React.HTMLAttributes { /** * Number of columns in the grid layout. When bigger than 1, child `CheckSelectBoxRoot` will have a default layout of "vertical". * @default 1 */ columns?: number; } export const CheckSelectBoxGroup = forwardRef( ({ columns = 1, className, style, ...props }, ref) => { const [variantProps, otherProps] = selectBoxGroup.splitVariantProps(props); const recipeClassName = selectBoxGroup(variantProps); const layout = columns === 1 ? "horizontal" : "vertical"; return ( ); }, ); function FooterVisibilityProvider({ children, footerVisibility, }: PropsWithChildren<{ footerVisibility: Exclude, "always">; }>) { const { checked } = useCheckboxContext(); const collapsible = useCollapsible({ open: { "when-selected": checked, "when-not-selected": !checked, }[footerVisibility], }); const [isFooterRendered, setIsFooterRendered] = useState(false); const footerRef = useCallback((node: HTMLDivElement | null) => { setIsFooterRendered(!!node); }, []); return ( {children} ); } export interface CheckSelectBoxRootProps extends SelectBoxVariantProps, CheckboxPrimitive.RootProps { /** * Controls when the footer is visible. * @default "when-selected" */ footerVisibility?: "when-selected" | "when-not-selected" | "always"; } export const CheckSelectBoxRoot = forwardRef( ({ footerVisibility = "when-selected", className, children, ...props }, ref) => { const [variantProps, otherProps] = selectBox.splitVariantProps(props); const classNames = selectBox({ ...useProps(), ...variantProps, }); return ( {footerVisibility === "always" ? ( children ) : ( {children} )} ); }, ); export interface CheckSelectBoxTriggerProps extends PrimitiveProps, React.HTMLAttributes {} export const CheckSelectBoxTrigger = withContext( withStateProps(Primitive.div), "trigger", ); export interface CheckSelectBoxContentProps extends PrimitiveProps, React.HTMLAttributes {} export const CheckSelectBoxContent = withContext( withStateProps(Primitive.div), "content", ); export interface CheckSelectBoxBodyProps extends PrimitiveProps, React.HTMLAttributes {} export const CheckSelectBoxBody = withContext( withStateProps(Primitive.div), "body", ); export interface CheckSelectBoxLabelProps extends PrimitiveProps, React.HTMLAttributes {} export const CheckSelectBoxLabel = withContext( withStateProps(Primitive.div), "label", ); export interface CheckSelectBoxDescriptionProps extends PrimitiveProps, React.HTMLAttributes {} export const CheckSelectBoxDescription = withContext< HTMLDivElement, CheckSelectBoxDescriptionProps >(withStateProps(Primitive.div), "description"); const { withProvider: withCheckmarkProvider, withContext: withCheckmarkContext } = createSlotRecipeContext(selectBoxCheckmark); const withCheckmarkStateProps = createWithStateProps([useCheckboxContext]); export interface CheckSelectBoxCheckmarkControlProps extends SelectBoxCheckmarkVariantProps, CheckboxPrimitive.ControlProps {} export const CheckSelectBoxCheckmarkControl = withCheckmarkProvider< HTMLDivElement, CheckSelectBoxCheckmarkControlProps >(CheckboxPrimitive.Control, "root"); export interface CheckSelectBoxCheckmarkIconProps extends InternalIconProps {} export const CheckSelectBoxCheckmarkIcon = withCheckmarkContext< SVGSVGElement, CheckSelectBoxCheckmarkIconProps >(withCheckmarkStateProps(InternalIcon), "icon"); export interface CheckSelectBoxHiddenInputProps extends CheckboxPrimitive.HiddenInputProps {} export const CheckSelectBoxHiddenInput = forwardRef< HTMLInputElement, CheckSelectBoxHiddenInputProps >((props, ref) => { // when footerVisibility !== "when-selected", this context is automatically unavailable since it's not wrapped in CollapsibleProvider const collapsibleContext = useCollapsibleContext({ strict: false }); const footerContext = useContext(FooterContext); const triggerAriaProps = footerContext?.isFooterRendered ? collapsibleContext?.triggerAriaProps : undefined; return ; }); CheckSelectBoxHiddenInput.displayName = "CheckSelectBoxHiddenInput"; export interface CheckSelectBoxFooterProps extends PrimitiveProps, React.HTMLAttributes {} export const CheckSelectBoxFooter = forwardRef( ({ className, children, ...props }, ref) => { const classNames = useClassNames(); const { stateProps } = useCheckboxContext(); const collapsibleContext = useCollapsibleContext({ strict: false }); const footerContext = useContext(FooterContext); const composedRef = composeRefs(ref, footerContext?.footerRef ?? null); if (collapsibleContext) { return ( {children} ); } return ( {children} ); }, ); CheckSelectBoxFooter.displayName = "CheckSelectBoxFooter";