UNPKG

@nopendsui/checkbox-group

Version:

Checkbox Group is a component that allows users to select multiple options from a list of options.

271 lines (267 loc) 8.99 kB
'use client'; import { createContext, useControllableState, useId, useDirection, Primitive, useFormControl, useComposedRefs, composeEventHandlers, VisuallyHiddenInput, Presence } from '@nopendsui/shared'; import * as React from 'react'; var ROOT_NAME = "CheckboxGroupRoot"; var [CheckboxGroupProvider, useCheckboxGroup] = createContext(ROOT_NAME); var CheckboxGroupRoot = React.forwardRef((props, ref) => { const { value: valueProp, defaultValue, onValueChange, onValidate, disabled = false, invalid = false, readOnly = false, required = false, dir: dirProp, orientation = "vertical", name, children, ...rootProps } = props; const [validationMessage, setValidationMessage] = React.useState(); const isInvalid = invalid || !!validationMessage; const [value = [], setValue] = useControllableState({ prop: valueProp, defaultProp: defaultValue, onChange: (newValue) => { if (onValidate) { const validationResult = onValidate(newValue); if (typeof validationResult === "string" || Array.isArray(validationResult)) { setValidationMessage(validationResult); } else if (validationResult === true || validationResult == null) { setValidationMessage(void 0); } } onValueChange?.(newValue); } }); const id = useId(); const labelId = useId(); const descriptionId = useId(); const messageId = useId(); const dir = useDirection(dirProp); const onItemCheckedChange = React.useCallback( (payload, checked) => { if (readOnly) return; const newValue = checked ? [...value, payload] : value.filter((v) => v !== payload); setValue(newValue); }, [setValue, value, readOnly] ); const onReset = React.useCallback(() => { setValue(defaultValue ?? []); setValidationMessage(void 0); }, [defaultValue, setValue]); return /* @__PURE__ */ React.createElement( CheckboxGroupProvider, { value, onValueChange: setValue, onItemCheckedChange, onReset, disabled, required, dir, orientation, isInvalid, id, labelId, descriptionId, messageId, validationMessage, readOnly }, /* @__PURE__ */ React.createElement( Primitive.div, { role: "group", "aria-labelledby": labelId, "aria-describedby": `${descriptionId} ${isInvalid ? messageId : ""}`, "aria-readonly": readOnly, "aria-orientation": orientation, "data-orientation": orientation, "data-disabled": disabled ? "" : void 0, "data-invalid": isInvalid ? "" : void 0, dir, ...rootProps, ref }, children ) ); }); CheckboxGroupRoot.displayName = ROOT_NAME; var Root = CheckboxGroupRoot; var LABEL_NAME = "CheckboxGroupLabel"; var CheckboxGroupLabel = React.forwardRef((props, ref) => { const context = useCheckboxGroup(LABEL_NAME); return /* @__PURE__ */ React.createElement( Primitive.label, { "data-disabled": context.disabled ? "" : void 0, id: context.labelId, ...props, ref } ); }); CheckboxGroupLabel.displayName = LABEL_NAME; var Label = CheckboxGroupLabel; var LIST_NAME = "CheckboxGroupList"; var CheckboxGroupList = React.forwardRef((props, ref) => { const context = useCheckboxGroup(LIST_NAME); const id = useId(); return /* @__PURE__ */ React.createElement( Primitive.div, { role: "group", id, "data-orientation": context.orientation, "data-invalid": context.isInvalid ? "" : void 0, ...props, ref } ); }); CheckboxGroupList.displayName = LIST_NAME; var List = CheckboxGroupList; var ITEM_NAME = "CheckboxGroupItem"; var [CheckboxGroupItemProvider, useCheckboxGroupItem] = createContext(ITEM_NAME); var CheckboxGroupItem = React.forwardRef((props, ref) => { const { value, disabled, required = false, name, ...itemProps } = props; const context = useCheckboxGroup(ITEM_NAME); const id = useId(); const isDisabled = disabled || context.disabled || false; const isChecked = context.value.includes(value); const isRequired = context.required && context.value.length === 0 || required && !isChecked; const { isFormControl, trigger, onTriggerChange } = useFormControl(); const composedRef = useComposedRefs(ref, (node) => onTriggerChange(node)); const lastClickTimeRef = React.useRef(0); const hasConsumerStoppedPropagationRef = React.useRef(false); return /* @__PURE__ */ React.createElement( CheckboxGroupItemProvider, { value, checked: isChecked, disabled: isDisabled }, /* @__PURE__ */ React.createElement( Primitive.button, { type: "button", role: "checkbox", "aria-checked": isChecked, "aria-disabled": isDisabled, "aria-invalid": context.isInvalid, "data-state": getDataState(isChecked), "data-orientation": context.orientation, "data-disabled": isDisabled ? "" : void 0, "data-invalid": context.isInvalid ? "" : void 0, disabled: isDisabled, id, ...itemProps, ref: composedRef, onClick: composeEventHandlers(props.onClick, (event) => { const now = Date.now(); if (now - lastClickTimeRef.current < 50) { event.stopPropagation(); return; } lastClickTimeRef.current = now; context.onItemCheckedChange(value, !isChecked); if (isFormControl) { hasConsumerStoppedPropagationRef.current = event.isPropagationStopped(); if (!hasConsumerStoppedPropagationRef.current) event.stopPropagation(); } }), onKeyDown: composeEventHandlers(props.onKeyDown, (event) => { if (event.key === "Enter") event.preventDefault(); }) } ), isFormControl && /* @__PURE__ */ React.createElement( VisuallyHiddenInput, { type: "checkbox", control: trigger, bubbles: !hasConsumerStoppedPropagationRef.current, name, value, checked: isChecked, disabled: isDisabled, readOnly: context.readOnly, required: isRequired, onReset: () => context.onReset() } ) ); }); function getDataState(checked) { return checked ? "checked" : "unchecked"; } CheckboxGroupItem.displayName = ITEM_NAME; var Item = CheckboxGroupItem; var INDICATOR_NAME = "CheckboxGroupIndicator"; var CheckboxGroupIndicator = React.forwardRef((props, ref) => { const { forceMount = false, ...indicatorProps } = props; const itemContext = useCheckboxGroupItem(INDICATOR_NAME); return /* @__PURE__ */ React.createElement(Presence, { present: forceMount || itemContext.checked }, /* @__PURE__ */ React.createElement( Primitive.span, { "data-state": getDataState(itemContext.checked), "data-disabled": itemContext.disabled ? "" : void 0, ...indicatorProps, ref } )); }); CheckboxGroupIndicator.displayName = INDICATOR_NAME; var Indicator = CheckboxGroupIndicator; var DESCRIPTION_NAME = "CheckboxGroupDescription"; var CheckboxGroupDescription = React.forwardRef((props, ref) => { const { announce = false, hideOnError = false, ...descriptionProps } = props; const context = useCheckboxGroup(DESCRIPTION_NAME); if (hideOnError && context.isInvalid) { return null; } return /* @__PURE__ */ React.createElement( Primitive.div, { id: context.descriptionId, "aria-live": announce ? "polite" : "off", "aria-describedby": context.labelId, "aria-invalid": context.isInvalid, "data-disabled": context.disabled ? "" : void 0, "data-invalid": context.isInvalid ? "" : void 0, ...descriptionProps, ref } ); }); CheckboxGroupDescription.displayName = DESCRIPTION_NAME; var Description = CheckboxGroupDescription; var MESSAGE_NAME = "CheckboxGroupMessage"; var CheckboxGroupMessage = React.forwardRef((props, ref) => { const { announce = false, children, ...messageProps } = props; const context = useCheckboxGroup(MESSAGE_NAME); if (!context.isInvalid) return null; const message = context.validationMessage || children; const messageContent = Array.isArray(message) ? message.join(" ") : message; return /* @__PURE__ */ React.createElement( Primitive.div, { id: context.messageId, "aria-live": announce ? "polite" : "off", "data-disabled": context.disabled ? "" : void 0, "data-invalid": context.isInvalid ? "" : void 0, ...messageProps, ref }, messageContent ); }); CheckboxGroupMessage.displayName = MESSAGE_NAME; var Message = CheckboxGroupMessage; export { CheckboxGroupDescription, CheckboxGroupIndicator, CheckboxGroupItem, CheckboxGroupLabel, CheckboxGroupList, CheckboxGroupMessage, CheckboxGroupRoot, Description, Indicator, Item, Label, List, Message, Root };