package.dist.index.js Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of rating-group Show documentation
Show all versions of rating-group Show documentation
Core logic for the rating-group widget implemented as a state machine
The newest version!
'use strict';
var anatomy$1 = require('@zag-js/anatomy');
var domEvent = require('@zag-js/dom-event');
var domQuery = require('@zag-js/dom-query');
var formUtils = require('@zag-js/form-utils');
var core = require('@zag-js/core');
var utils = require('@zag-js/utils');
var types = require('@zag-js/types');
// src/rating-group.anatomy.ts
var anatomy = anatomy$1.createAnatomy("rating-group").parts("root", "label", "item", "control");
var parts = anatomy.build();
var dom = domQuery.createScope({
getRootId: (ctx) => ctx.ids?.root ?? `rating:${ctx.id}`,
getLabelId: (ctx) => ctx.ids?.label ?? `rating:${ctx.id}:label`,
getHiddenInputId: (ctx) => ctx.ids?.hiddenInput ?? `rating:${ctx.id}:input`,
getControlId: (ctx) => ctx.ids?.control ?? `rating:${ctx.id}:control`,
getItemId: (ctx, id) => ctx.ids?.item?.(id) ?? `rating:${ctx.id}:item:${id}`,
getRootEl: (ctx) => dom.getById(ctx, dom.getRootId(ctx)),
getControlEl: (ctx) => dom.getById(ctx, dom.getControlId(ctx)),
getRadioEl: (ctx, value = ctx.value) => {
const selector = `[role=radio][aria-posinset='${Math.ceil(value)}']`;
return domQuery.query(dom.getControlEl(ctx), selector);
},
getHiddenInputEl: (ctx) => dom.getById(ctx, dom.getHiddenInputId(ctx)),
dispatchChangeEvent: (ctx) => {
const inputEl = dom.getHiddenInputEl(ctx);
if (!inputEl) return;
formUtils.dispatchInputValueEvent(inputEl, { value: ctx.value });
}
});
// src/rating-group.connect.ts
function connect(state, send, normalize) {
const interactive = state.context.isInteractive;
const disabled = state.context.isDisabled;
const readOnly = state.context.readOnly;
const value = state.context.value;
const hoveredValue = state.context.hoveredValue;
const translations = state.context.translations;
function getItemState(props2) {
const value2 = state.context.isHovering ? state.context.hoveredValue : state.context.value;
const equal = Math.ceil(value2) === props2.index;
const highlighted = props2.index <= value2 || equal;
const half = equal && Math.abs(value2 - props2.index) === 0.5;
return {
highlighted,
half,
checked: equal || state.context.value === -1 && props2.index === 1
};
}
return {
hovering: state.context.isHovering,
value,
hoveredValue,
count: state.context.count,
items: Array.from({ length: state.context.count }).map((_, index) => index + 1),
setValue(value2) {
send({ type: "SET_VALUE", value: value2 });
},
clearValue() {
send("CLEAR_VALUE");
},
getRootProps() {
return normalize.element({
...parts.root.attrs,
dir: state.context.dir,
id: dom.getRootId(state.context)
});
},
getHiddenInputProps() {
return normalize.input({
name: state.context.name,
form: state.context.form,
type: "text",
hidden: true,
disabled,
readOnly,
required: state.context.required,
id: dom.getHiddenInputId(state.context),
defaultValue: state.context.value
});
},
getLabelProps() {
return normalize.label({
...parts.label.attrs,
dir: state.context.dir,
id: dom.getLabelId(state.context),
"data-disabled": domQuery.dataAttr(disabled),
htmlFor: dom.getHiddenInputId(state.context),
onClick(event) {
if (event.defaultPrevented) return;
if (!interactive) return;
event.preventDefault();
const radioEl = dom.getRadioEl(state.context, 1);
radioEl?.focus({ preventScroll: true });
}
});
},
getControlProps() {
return normalize.element({
id: dom.getControlId(state.context),
...parts.control.attrs,
dir: state.context.dir,
role: "radiogroup",
"aria-orientation": "horizontal",
"aria-labelledby": dom.getLabelId(state.context),
"aria-readonly": domQuery.ariaAttr(readOnly),
"data-readonly": domQuery.dataAttr(readOnly),
"data-disabled": domQuery.dataAttr(disabled),
onPointerMove(event) {
if (!interactive) return;
if (event.pointerType === "touch") return;
send("GROUP_POINTER_OVER");
},
onPointerLeave(event) {
if (!interactive) return;
if (event.pointerType === "touch") return;
send("GROUP_POINTER_LEAVE");
}
});
},
getItemState,
getItemProps(props2) {
const { index } = props2;
const itemState = getItemState(props2);
const valueText = translations.ratingValueText(index);
return normalize.element({
...parts.item.attrs,
dir: state.context.dir,
id: dom.getItemId(state.context, index.toString()),
role: "radio",
tabIndex: (() => {
if (readOnly) return itemState.checked ? 0 : void 0;
if (disabled) return void 0;
return itemState.checked ? 0 : -1;
})(),
"aria-roledescription": "rating",
"aria-label": valueText,
"aria-disabled": disabled,
"data-disabled": domQuery.dataAttr(disabled),
"data-readonly": domQuery.dataAttr(readOnly),
"aria-setsize": state.context.count,
"aria-checked": itemState.checked,
"data-checked": domQuery.dataAttr(itemState.checked),
"aria-posinset": index,
"data-highlighted": domQuery.dataAttr(itemState.highlighted),
"data-half": domQuery.dataAttr(itemState.half),
onPointerDown(event) {
if (!interactive) return;
if (!domEvent.isLeftClick(event)) return;
event.preventDefault();
},
onPointerMove(event) {
if (!interactive) return;
const point = domEvent.getEventPoint(event);
const relativePoint = domEvent.getRelativePoint(point, event.currentTarget);
const percentX = relativePoint.getPercentValue({
orientation: "horizontal",
dir: state.context.dir
});
const isMidway = percentX < 0.5;
send({ type: "POINTER_OVER", index, isMidway });
},
onKeyDown(event) {
if (event.defaultPrevented) return;
if (!interactive) return;
const keyMap = {
ArrowLeft() {
send("ARROW_LEFT");
},
ArrowRight() {
send("ARROW_RIGHT");
},
ArrowUp() {
send("ARROW_LEFT");
},
ArrowDown() {
send("ARROW_RIGHT");
},
Space() {
send({ type: "SPACE", value: index });
},
Home() {
send("HOME");
},
End() {
send("END");
}
};
const key = domEvent.getEventKey(event, state.context);
const exec = keyMap[key];
if (exec) {
event.preventDefault();
exec(event);
}
},
onClick() {
if (!interactive) return;
send({ type: "CLICK", value: index });
},
onFocus() {
if (!interactive) return;
send("FOCUS");
},
onBlur() {
if (!interactive) return;
send("BLUR");
}
});
}
};
}
function machine(userContext) {
const ctx = utils.compact(userContext);
return core.createMachine(
{
id: "rating",
initial: "idle",
context: {
name: "rating",
count: 5,
dir: "ltr",
value: -1,
readOnly: false,
disabled: false,
...ctx,
hoveredValue: -1,
fieldsetDisabled: false,
translations: {
ratingValueText: (index) => `${index} stars`,
...ctx.translations
}
},
created: ["roundValueIfNeeded"],
watch: {
allowHalf: ["roundValueIfNeeded"]
},
computed: {
isDisabled: (ctx2) => !!ctx2.disabled || ctx2.fieldsetDisabled,
isInteractive: (ctx2) => !(ctx2.isDisabled || ctx2.readOnly),
isHovering: (ctx2) => ctx2.hoveredValue > -1
},
activities: ["trackFormControlState"],
on: {
SET_VALUE: {
actions: ["setValue"]
},
CLEAR_VALUE: {
actions: ["clearValue"]
}
},
states: {
idle: {
entry: "clearHoveredValue",
on: {
GROUP_POINTER_OVER: "hover",
FOCUS: "focus",
CLICK: {
actions: ["setValue", "focusActiveRadio"]
}
}
},
focus: {
on: {
POINTER_OVER: {
actions: "setHoveredValue"
},
GROUP_POINTER_LEAVE: {
actions: "clearHoveredValue"
},
BLUR: "idle",
SPACE: {
guard: "isValueEmpty",
actions: ["setValue"]
},
CLICK: {
actions: ["setValue", "focusActiveRadio"]
},
ARROW_LEFT: {
actions: ["setPrevValue", "focusActiveRadio"]
},
ARROW_RIGHT: {
actions: ["setNextValue", "focusActiveRadio"]
},
HOME: {
actions: ["setValueToMin", "focusActiveRadio"]
},
END: {
actions: ["setValueToMax", "focusActiveRadio"]
}
}
},
hover: {
on: {
POINTER_OVER: {
actions: "setHoveredValue"
},
GROUP_POINTER_LEAVE: [
{
guard: "isRadioFocused",
target: "focus",
actions: "clearHoveredValue"
},
{
target: "idle",
actions: "clearHoveredValue"
}
],
CLICK: {
actions: ["setValue", "focusActiveRadio"]
}
}
}
}
},
{
guards: {
isInteractive: (ctx2) => !(ctx2.disabled || ctx2.readOnly),
isHoveredValueEmpty: (ctx2) => ctx2.hoveredValue === -1,
isValueEmpty: (ctx2) => ctx2.value <= 0,
isRadioFocused: (ctx2) => !!dom.getControlEl(ctx2)?.contains(dom.getActiveElement(ctx2))
},
activities: {
trackFormControlState(ctx2, _evt, { initialContext }) {
return formUtils.trackFormControl(dom.getHiddenInputEl(ctx2), {
onFieldsetDisabledChange(disabled) {
ctx2.fieldsetDisabled = disabled;
},
onFormReset() {
set.value(ctx2, initialContext.value);
}
});
}
},
actions: {
clearHoveredValue(ctx2) {
set.hoveredValue(ctx2, -1);
},
focusActiveRadio(ctx2) {
domQuery.raf(() => dom.getRadioEl(ctx2)?.focus());
},
setPrevValue(ctx2) {
const factor = ctx2.allowHalf ? 0.5 : 1;
set.value(ctx2, Math.max(0, ctx2.value - factor));
},
setNextValue(ctx2) {
const factor = ctx2.allowHalf ? 0.5 : 1;
const value = ctx2.value === -1 ? 0 : ctx2.value;
set.value(ctx2, Math.min(ctx2.count, value + factor));
},
setValueToMin(ctx2) {
set.value(ctx2, 1);
},
setValueToMax(ctx2) {
set.value(ctx2, ctx2.count);
},
setValue(ctx2, evt) {
const value = ctx2.hoveredValue === -1 ? evt.value : ctx2.hoveredValue;
set.value(ctx2, value);
},
clearValue(ctx2) {
set.value(ctx2, -1);
},
setHoveredValue(ctx2, evt) {
const half = ctx2.allowHalf && evt.isMidway;
const factor = half ? 0.5 : 0;
set.hoveredValue(ctx2, evt.index - factor);
},
roundValueIfNeeded(ctx2) {
if (ctx2.allowHalf) return;
ctx2.value = Math.round(ctx2.value);
}
}
}
);
}
var invoke = {
change: (ctx) => {
ctx.onValueChange?.({ value: ctx.value });
dom.dispatchChangeEvent(ctx);
},
hoverChange: (ctx) => {
ctx.onHoverChange?.({ hoveredValue: ctx.hoveredValue });
}
};
var set = {
value: (ctx, value) => {
if (utils.isEqual(ctx.value, value)) return;
ctx.value = value;
invoke.change(ctx);
},
hoveredValue: (ctx, value) => {
if (utils.isEqual(ctx.hoveredValue, value)) return;
ctx.hoveredValue = value;
invoke.hoverChange(ctx);
}
};
var props = types.createProps()([
"allowHalf",
"autoFocus",
"count",
"dir",
"disabled",
"form",
"getRootNode",
"id",
"ids",
"name",
"onHoverChange",
"onValueChange",
"required",
"readOnly",
"translations",
"value"
]);
var splitProps = utils.createSplitProps(props);
var itemProps = types.createProps()(["index"]);
var splitItemProps = utils.createSplitProps(itemProps);
exports.anatomy = anatomy;
exports.connect = connect;
exports.itemProps = itemProps;
exports.machine = machine;
exports.props = props;
exports.splitItemProps = splitItemProps;
exports.splitProps = splitProps;
© 2015 - 2025 Weber Informatics LLC | Privacy Policy