UNPKG

@zag-js/rating-group

Version:

Core logic for the rating-group widget implemented as a state machine

235 lines (233 loc) • 7.28 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/rating-group.machine.ts var rating_group_machine_exports = {}; __export(rating_group_machine_exports, { machine: () => machine }); module.exports = __toCommonJS(rating_group_machine_exports); var import_core = require("@zag-js/core"); var import_dom_query = require("@zag-js/dom-query"); var dom = __toESM(require("./rating-group.dom.js")); var machine = (0, import_core.createMachine)({ props({ props }) { return { name: "rating", count: 5, dir: "ltr", defaultValue: -1, ...props, translations: { ratingValueText: (index) => `${index} stars`, ...props.translations } }; }, initialState() { return "idle"; }, context({ prop, bindable }) { return { value: bindable(() => ({ defaultValue: prop("defaultValue"), value: prop("value"), onChange(value) { prop("onValueChange")?.({ value }); } })), hoveredValue: bindable(() => ({ defaultValue: -1, onChange(value) { prop("onHoverChange")?.({ hoveredValue: value }); } })), fieldsetDisabled: bindable(() => ({ defaultValue: false })) }; }, watch({ track, action, prop, context }) { track([() => prop("allowHalf")], () => { action(["roundValueIfNeeded"]); }); track([() => context.get("value")], () => { action(["dispatchChangeEvent"]); }); }, computed: { isDisabled: ({ context, prop }) => !!prop("disabled") || context.get("fieldsetDisabled"), isInteractive: ({ computed, prop }) => !(computed("isDisabled") || prop("readOnly")), isHovering: ({ context }) => context.get("hoveredValue") > -1 }, effects: ["trackFormControlState"], on: { SET_VALUE: { actions: ["setValue"] }, CLEAR_VALUE: { actions: ["clearValue"] } }, states: { idle: { entry: ["clearHoveredValue"], on: { GROUP_POINTER_OVER: { target: "hover" }, FOCUS: { target: "focus" }, CLICK: { actions: ["setValue", "focusActiveRadio"] } } }, focus: { on: { POINTER_OVER: { actions: ["setHoveredValue"] }, GROUP_POINTER_LEAVE: { actions: ["clearHoveredValue"] }, BLUR: { target: "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"] } } } }, implementations: { guards: { isInteractive: ({ prop }) => !(prop("disabled") || prop("readOnly")), isHoveredValueEmpty: ({ context }) => context.get("hoveredValue") === -1, isValueEmpty: ({ context }) => context.get("value") <= 0, isRadioFocused: ({ scope }) => !!dom.getControlEl(scope)?.contains(scope.getActiveElement()) }, effects: { trackFormControlState({ context, scope }) { return (0, import_dom_query.trackFormControl)(dom.getHiddenInputEl(scope), { onFieldsetDisabledChange(disabled) { context.set("fieldsetDisabled", disabled); }, onFormReset() { context.set("value", context.initial("value")); } }); } }, actions: { clearHoveredValue({ context }) { context.set("hoveredValue", -1); }, focusActiveRadio({ scope, context }) { (0, import_dom_query.raf)(() => dom.getRadioEl(scope, context.get("value"))?.focus()); }, setPrevValue({ context, prop }) { const factor = prop("allowHalf") ? 0.5 : 1; context.set("value", Math.max(0, context.get("value") - factor)); }, setNextValue({ context, prop }) { const factor = prop("allowHalf") ? 0.5 : 1; const value = context.get("value") === -1 ? 0 : context.get("value"); context.set("value", Math.min(prop("count"), value + factor)); }, setValueToMin({ context }) { context.set("value", 1); }, setValueToMax({ context, prop }) { context.set("value", prop("count")); }, setValue({ context, event }) { const hoveredValue = context.get("hoveredValue"); const value = hoveredValue === -1 ? event.value : hoveredValue; context.set("value", value); }, clearValue({ context }) { context.set("value", -1); }, setHoveredValue({ context, prop, event }) { const half = prop("allowHalf") && event.isMidway; const factor = half ? 0.5 : 0; context.set("hoveredValue", event.index - factor); }, roundValueIfNeeded({ context, prop }) { if (prop("allowHalf")) return; context.set("value", Math.round(context.get("value"))); }, dispatchChangeEvent({ context, scope }) { dom.dispatchChangeEvent(scope, context.get("value")); } } } }); // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { machine });