UNPKG

@zag-js/preact

Version:

The preact wrapper for zag

283 lines (281 loc) 9.44 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; 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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/machine.ts var machine_exports = {}; __export(machine_exports, { useMachine: () => useMachine }); module.exports = __toCommonJS(machine_exports); var import_core = require("@zag-js/core"); var import_utils = require("@zag-js/utils"); var import_compat = require("preact/compat"); var import_hooks = require("preact/hooks"); var import_bindable = require("./bindable.js"); var import_refs = require("./refs.js"); var import_track = require("./track.js"); function useMachine(machine, userProps = {}) { const scope = (0, import_hooks.useMemo)(() => { const { id, ids, getRootNode } = userProps; return (0, import_core.createScope)({ id, ids, getRootNode }); }, [userProps]); const debug = (...args) => { if (machine.debug) console.log(...args); }; const props = machine.props?.({ props: userProps, scope }) ?? userProps; const prop = useProp(props); const context = machine.context?.({ prop, bindable: import_bindable.useBindable, scope, flush, getContext() { return ctx; }, getComputed() { return computed; }, getRefs() { return refs; }, getEvent() { return getEvent(); } }); const contextRef = useLiveRef(context); const ctx = { get(key) { return contextRef.current?.[key].get(); }, set(key, value) { contextRef.current?.[key].set(value); }, initial(key) { return contextRef.current?.[key].initial; }, hash(key) { const current = contextRef.current?.[key].get(); return contextRef.current?.[key].hash(current); } }; const effects = (0, import_hooks.useRef)(/* @__PURE__ */ new Map()); const transitionRef = (0, import_hooks.useRef)(null); const previousEventRef = (0, import_hooks.useRef)(null); const eventRef = (0, import_hooks.useRef)({ type: "" }); const refs = (0, import_refs.useRefs)(machine.refs?.({ prop, context: ctx }) ?? {}); const getEvent = () => ({ ...eventRef.current, current() { return eventRef.current; }, previous() { return previousEventRef.current; } }); const getState = () => ({ ...state, hasTag(tag) { const currentState = state.get(); return (0, import_core.hasTag)(machine, currentState, tag); }, matches(...values) { const currentState = state.get(); return values.some((value) => (0, import_core.matchesState)(currentState, value)); } }); const getParams = () => ({ state: getState(), context: ctx, event: getEvent(), prop, send, action, guard, track: import_track.useTrack, refs, computed, flush, scope, choose }); const action = (keys) => { const strs = (0, import_utils.isFunction)(keys) ? keys(getParams()) : keys; if (!strs) return; const fns = strs.map((s) => { const fn = machine.implementations?.actions?.[s]; if (!fn) (0, import_utils.warn)(`[zag-js] No implementation found for action "${JSON.stringify(s)}"`); return fn; }); for (const fn of fns) { fn?.(getParams()); } }; const guard = (str) => { if ((0, import_utils.isFunction)(str)) return str(getParams()); const fn = machine.implementations?.guards?.[str]; if (!fn) (0, import_utils.warn)(`[zag-js] No implementation found for guard "${JSON.stringify(str)}"`); return fn?.(getParams()); }; const effect = (keys) => { const strs = (0, import_utils.isFunction)(keys) ? keys(getParams()) : keys; if (!strs) return; const fns = strs.map((s) => { const fn = machine.implementations?.effects?.[s]; if (!fn) (0, import_utils.warn)(`[zag-js] No implementation found for effect "${JSON.stringify(s)}"`); return fn; }); const cleanups = []; for (const fn of fns) { const cleanup = fn?.(getParams()); if (cleanup) cleanups.push(cleanup); } return () => cleanups.forEach((fn) => fn?.()); }; const choose = (transitions) => { return (0, import_utils.toArray)(transitions).find((t) => { let result = !t.guard; if ((0, import_utils.isString)(t.guard)) result = !!guard(t.guard); else if ((0, import_utils.isFunction)(t.guard)) result = t.guard(getParams()); return result; }); }; const computed = (key) => { (0, import_utils.ensure)(machine.computed, () => `[zag-js] No computed object found on machine`); const fn = machine.computed[key]; return fn({ context: ctx, event: getEvent(), prop, refs, scope, computed }); }; const state = (0, import_bindable.useBindable)(() => ({ defaultValue: (0, import_core.resolveStateValue)(machine, machine.initialState({ prop })), onChange(nextState, prevState) { currentStateRef.current = nextState; const { exiting, entering } = (0, import_core.getExitEnterStates)(machine, prevState, nextState, transitionRef.current?.reenter); exiting.forEach((item) => { const exitEffects = effects.current.get(item.path); exitEffects?.(); effects.current.delete(item.path); }); exiting.forEach((item) => { action(item.state?.exit); }); action(transitionRef.current?.actions); entering.forEach((item) => { const cleanup = effect(item.state?.effects); if (cleanup) { const existing = effects.current.get(item.path); effects.current.set(item.path, existing ? (0, import_utils.callAll)(existing, cleanup) : cleanup); } }); if (prevState === import_core.INIT_STATE) { action(machine.entry); const cleanup = effect(machine.effects); if (cleanup) { const existing = effects.current.get(import_core.INIT_STATE); effects.current.set(import_core.INIT_STATE, existing ? (0, import_utils.callAll)(existing, cleanup) : cleanup); } } entering.forEach((item) => { action(item.state?.entry); }); } })); const currentStateRef = (0, import_hooks.useRef)(state.initial); const hydratedStateRef = (0, import_hooks.useRef)(void 0); const statusRef = (0, import_hooks.useRef)(import_core.MachineStatus.NotStarted); (0, import_hooks.useLayoutEffect)(() => { const started = statusRef.current === import_core.MachineStatus.Started; statusRef.current = import_core.MachineStatus.Started; debug(started ? "rehydrating..." : "initializing..."); const initialState = hydratedStateRef.current ?? state.initial; state.invoke(initialState, started ? state.get() : import_core.INIT_STATE); const fns = effects.current; return () => { const currentState = getCurrentState(); debug("unmounting..."); hydratedStateRef.current = currentState; statusRef.current = import_core.MachineStatus.Stopped; fns.forEach((fn) => fn?.()); effects.current = /* @__PURE__ */ new Map(); transitionRef.current = null; action(machine.exit); }; }, []); const getCurrentState = () => { return currentStateRef.current; }; const send = (event) => { queueMicrotask(() => { if (statusRef.current !== import_core.MachineStatus.Started) return; previousEventRef.current = eventRef.current; eventRef.current = event; let currentState = getCurrentState(); const { transitions, source } = (0, import_core.findTransition)(machine, currentState, event.type); const transition = choose(transitions); if (!transition) return; transitionRef.current = transition; const target = (0, import_core.resolveStateValue)(machine, transition.target ?? currentState, source); const changed = target !== currentState; if (changed) { currentStateRef.current = target; (0, import_compat.flushSync)(() => state.set(target)); } else if (transition.reenter) { state.invoke(currentState, currentState); } else { action(transition.actions ?? []); } }); }; machine.watch?.(getParams()); return { state: getState(), send, context: ctx, prop, scope, refs, computed, event: getEvent(), getStatus: () => statusRef.current }; } function useLiveRef(value) { const ref = (0, import_hooks.useRef)(value); ref.current = value; return ref; } function useProp(value) { const ref = useLiveRef(value); return function get(key) { return ref.current[key]; }; } function flush(fn) { queueMicrotask(() => { (0, import_compat.flushSync)(() => fn()); }); } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { useMachine });