@zag-js/preact
Version:
The preact wrapper for zag
283 lines (281 loc) • 9.44 kB
JavaScript
;
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
});