@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
JavaScript
'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 };