package.dist.index.mjs Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of checkbox Show documentation
Show all versions of checkbox Show documentation
Core logic for the checkbox widget implemented as a state machine
The newest version!
import { createAnatomy } from '@zag-js/anatomy';
import { createScope, dataAttr, getEventTarget, visuallyHiddenStyle } from '@zag-js/dom-query';
import { isFocusVisible, trackFocusVisible } from '@zag-js/focus-visible';
import { createMachine, guards } from '@zag-js/core';
import { trackPress } from '@zag-js/dom-event';
import { trackFormControl, setElementChecked, dispatchInputCheckedEvent } from '@zag-js/form-utils';
import { createSplitProps, compact, isEqual } from '@zag-js/utils';
import { createProps } from '@zag-js/types';
// src/checkbox.anatomy.ts
var anatomy = createAnatomy("checkbox").parts("root", "label", "control", "indicator");
var parts = anatomy.build();
var dom = createScope({
getRootId: (ctx) => ctx.ids?.root ?? `checkbox:${ctx.id}`,
getLabelId: (ctx) => ctx.ids?.label ?? `checkbox:${ctx.id}:label`,
getControlId: (ctx) => ctx.ids?.control ?? `checkbox:${ctx.id}:control`,
getHiddenInputId: (ctx) => ctx.ids?.hiddenInput ?? `checkbox:${ctx.id}:input`,
getRootEl: (ctx) => dom.getById(ctx, dom.getRootId(ctx)),
getHiddenInputEl: (ctx) => dom.getById(ctx, dom.getHiddenInputId(ctx))
});
function connect(state, send, normalize) {
const disabled = state.context.isDisabled;
const readOnly = state.context.readOnly;
const focused = !disabled && state.context.focused;
const focusVisible = !disabled && state.context.focusVisible;
const checked = state.context.isChecked;
const indeterminate = state.context.isIndeterminate;
const dataAttrs = {
"data-active": dataAttr(state.context.active),
"data-focus": dataAttr(focused),
"data-focus-visible": dataAttr(focusVisible),
"data-readonly": dataAttr(readOnly),
"data-hover": dataAttr(state.context.hovered),
"data-disabled": dataAttr(disabled),
"data-state": indeterminate ? "indeterminate" : state.context.checked ? "checked" : "unchecked",
"data-invalid": dataAttr(state.context.invalid)
};
return {
checked,
disabled,
indeterminate,
focused,
checkedState: state.context.checked,
setChecked(checked2) {
send({ type: "CHECKED.SET", checked: checked2, isTrusted: false });
},
toggleChecked() {
send({ type: "CHECKED.TOGGLE", checked, isTrusted: false });
},
getRootProps() {
return normalize.label({
...parts.root.attrs,
...dataAttrs,
dir: state.context.dir,
id: dom.getRootId(state.context),
htmlFor: dom.getHiddenInputId(state.context),
onPointerMove() {
if (disabled) return;
send({ type: "CONTEXT.SET", context: { hovered: true } });
},
onPointerLeave() {
if (disabled) return;
send({ type: "CONTEXT.SET", context: { hovered: false } });
},
onClick(event) {
const target = getEventTarget(event);
if (target === dom.getHiddenInputEl(state.context)) {
event.stopPropagation();
}
}
});
},
getLabelProps() {
return normalize.element({
...parts.label.attrs,
...dataAttrs,
dir: state.context.dir,
id: dom.getLabelId(state.context)
});
},
getControlProps() {
return normalize.element({
...parts.control.attrs,
...dataAttrs,
dir: state.context.dir,
id: dom.getControlId(state.context),
"aria-hidden": true
});
},
getIndicatorProps() {
return normalize.element({
...parts.indicator.attrs,
...dataAttrs,
dir: state.context.dir,
hidden: !indeterminate && !state.context.checked
});
},
getHiddenInputProps() {
return normalize.input({
id: dom.getHiddenInputId(state.context),
type: "checkbox",
required: state.context.required,
defaultChecked: checked,
disabled,
"aria-labelledby": dom.getLabelId(state.context),
"aria-invalid": state.context.invalid,
name: state.context.name,
form: state.context.form,
value: state.context.value,
style: visuallyHiddenStyle,
onFocus() {
const focusVisible2 = isFocusVisible();
send({ type: "CONTEXT.SET", context: { focused: true, focusVisible: focusVisible2 } });
},
onBlur() {
send({ type: "CONTEXT.SET", context: { focused: false, focusVisible: false } });
},
onClick(event) {
if (readOnly) {
event.preventDefault();
return;
}
const checked2 = event.currentTarget.checked;
send({ type: "CHECKED.SET", checked: checked2, isTrusted: true });
}
});
}
};
}
var { not } = guards;
function machine(userContext) {
const ctx = compact(userContext);
return createMachine(
{
id: "checkbox",
initial: "ready",
context: {
checked: false,
value: "on",
disabled: false,
...ctx,
fieldsetDisabled: false,
focusVisible: false
},
watch: {
disabled: "removeFocusIfNeeded",
checked: "syncInputElement"
},
activities: ["trackFormControlState", "trackPressEvent", "trackFocusVisible"],
on: {
"CHECKED.TOGGLE": [
{
guard: not("isTrusted"),
actions: ["toggleChecked", "dispatchChangeEvent"]
},
{
actions: ["toggleChecked"]
}
],
"CHECKED.SET": [
{
guard: not("isTrusted"),
actions: ["setChecked", "dispatchChangeEvent"]
},
{
actions: ["setChecked"]
}
],
"CONTEXT.SET": {
actions: ["setContext"]
}
},
computed: {
isIndeterminate: (ctx2) => isIndeterminate(ctx2.checked),
isChecked: (ctx2) => isChecked(ctx2.checked),
isDisabled: (ctx2) => !!ctx2.disabled || ctx2.fieldsetDisabled
},
states: {
ready: {}
}
},
{
guards: {
isTrusted: (_ctx, evt) => !!evt.isTrusted
},
activities: {
trackPressEvent(ctx2) {
if (ctx2.isDisabled) return;
return trackPress({
pointerNode: dom.getRootEl(ctx2),
keyboardNode: dom.getHiddenInputEl(ctx2),
isValidKey: (event) => event.key === " ",
onPress: () => ctx2.active = false,
onPressStart: () => ctx2.active = true,
onPressEnd: () => ctx2.active = false
});
},
trackFocusVisible(ctx2) {
if (ctx2.isDisabled) return;
return trackFocusVisible({ root: dom.getRootNode(ctx2) });
},
trackFormControlState(ctx2, _evt, { send, initialContext }) {
return trackFormControl(dom.getHiddenInputEl(ctx2), {
onFieldsetDisabledChange(disabled) {
ctx2.fieldsetDisabled = disabled;
},
onFormReset() {
send({ type: "CHECKED.SET", checked: !!initialContext.checked });
}
});
}
},
actions: {
setContext(ctx2, evt) {
Object.assign(ctx2, evt.context);
},
syncInputElement(ctx2) {
const inputEl = dom.getHiddenInputEl(ctx2);
if (!inputEl) return;
setElementChecked(inputEl, ctx2.isChecked);
inputEl.indeterminate = ctx2.isIndeterminate;
},
removeFocusIfNeeded(ctx2) {
if (ctx2.disabled && ctx2.focused) {
ctx2.focused = false;
ctx2.focusVisible = false;
}
},
setChecked(ctx2, evt) {
set.checked(ctx2, evt.checked);
},
toggleChecked(ctx2) {
const checked = isIndeterminate(ctx2.checked) ? true : !ctx2.checked;
set.checked(ctx2, checked);
},
dispatchChangeEvent(ctx2) {
const inputEl = dom.getHiddenInputEl(ctx2);
dispatchInputCheckedEvent(inputEl, { checked: isChecked(ctx2.checked) });
}
}
}
);
}
function isIndeterminate(checked) {
return checked === "indeterminate";
}
function isChecked(checked) {
return isIndeterminate(checked) ? false : !!checked;
}
var invoke = {
change: (ctx) => {
ctx.onCheckedChange?.({ checked: ctx.checked });
}
};
var set = {
checked: (ctx, checked) => {
if (isEqual(ctx.checked, checked)) return;
ctx.checked = checked;
invoke.change(ctx);
}
};
var props = createProps()([
"checked",
"dir",
"disabled",
"form",
"getRootNode",
"id",
"ids",
"invalid",
"name",
"onCheckedChange",
"readOnly",
"required",
"value"
]);
var splitProps = createSplitProps(props);
export { anatomy, connect, machine, props, splitProps };
© 2015 - 2025 Weber Informatics LLC | Privacy Policy