UNPKG

@zag-js/presence

Version:

Core logic for the presence widget implemented as a state machine

197 lines (195 loc) • 6.62 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/presence.machine.ts var presence_machine_exports = {}; __export(presence_machine_exports, { machine: () => machine }); module.exports = __toCommonJS(presence_machine_exports); var import_core = require("@zag-js/core"); var import_dom_query = require("@zag-js/dom-query"); var machine = (0, import_core.createMachine)({ props({ props }) { return { ...props, present: !!props.present }; }, initialState({ prop }) { return prop("present") ? "mounted" : "unmounted"; }, refs() { return { node: null, styles: null }; }, context({ bindable }) { return { unmountAnimationName: bindable(() => ({ defaultValue: null })), prevAnimationName: bindable(() => ({ defaultValue: null })), present: bindable(() => ({ defaultValue: false })), initial: bindable(() => ({ sync: true, defaultValue: false })) }; }, exit: ["cleanupNode"], watch({ track, prop, send }) { track([() => prop("present")], () => { send({ type: "PRESENCE.CHANGED" }); }); }, on: { "NODE.SET": { actions: ["setupNode"] }, "PRESENCE.CHANGED": { actions: ["setInitial", "syncPresence"] } }, states: { mounted: { on: { UNMOUNT: { target: "unmounted", actions: ["clearPrevAnimationName", "invokeOnExitComplete"] }, "UNMOUNT.SUSPEND": { target: "unmountSuspended" } } }, unmountSuspended: { effects: ["trackAnimationEvents"], on: { MOUNT: { target: "mounted", actions: ["setPrevAnimationName"] }, UNMOUNT: { target: "unmounted", actions: ["clearPrevAnimationName", "invokeOnExitComplete"] } } }, unmounted: { on: { MOUNT: { target: "mounted", actions: ["setPrevAnimationName"] } } } }, implementations: { actions: { setInitial: ({ context }) => { if (context.get("initial")) return; queueMicrotask(() => { context.set("initial", true); }); }, invokeOnExitComplete: ({ prop, refs }) => { prop("onExitComplete")?.(); const node = refs.get("node"); if (!node) return; const win = (0, import_dom_query.getWindow)(node); const event = new win.CustomEvent("exitcomplete", { bubbles: false }); node.dispatchEvent(event); }, setupNode: ({ refs, event }) => { if (refs.get("node") === event.node) return; refs.set("node", event.node); refs.set("styles", (0, import_dom_query.getComputedStyle)(event.node)); }, cleanupNode: ({ refs }) => { refs.set("node", null); refs.set("styles", null); }, syncPresence: ({ context, refs, send, prop }) => { const presentProp = prop("present"); if (presentProp) { return send({ type: "MOUNT", src: "presence.changed" }); } const node = refs.get("node"); if (!presentProp && node?.ownerDocument.visibilityState === "hidden") { return send({ type: "UNMOUNT", src: "visibilitychange" }); } (0, import_dom_query.raf)(() => { const animationName = getAnimationName(refs.get("styles")); context.set("unmountAnimationName", animationName); if (animationName === "none" || animationName === context.get("prevAnimationName") || refs.get("styles")?.display === "none" || refs.get("styles")?.animationDuration === "0s") { send({ type: "UNMOUNT", src: "presence.changed" }); } else { send({ type: "UNMOUNT.SUSPEND" }); } }); }, setPrevAnimationName: ({ context, refs }) => { (0, import_dom_query.raf)(() => { context.set("prevAnimationName", getAnimationName(refs.get("styles"))); }); }, clearPrevAnimationName: ({ context }) => { context.set("prevAnimationName", null); } }, effects: { trackAnimationEvents: ({ context, refs, send, prop }) => { const node = refs.get("node"); if (!node) return; const onStart = (event) => { const target = event.composedPath?.()?.[0] ?? event.target; if (target === node) { context.set("prevAnimationName", getAnimationName(refs.get("styles"))); } }; const onEnd = (event) => { const animationName = getAnimationName(refs.get("styles")); const target = (0, import_dom_query.getEventTarget)(event); if (target === node && animationName === context.get("unmountAnimationName") && !prop("present")) { send({ type: "UNMOUNT", src: "animationend" }); } }; const onCancel = (event) => { const target = (0, import_dom_query.getEventTarget)(event); if (target === node && !prop("present")) { send({ type: "UNMOUNT", src: "animationcancel" }); } }; node.addEventListener("animationstart", onStart); node.addEventListener("animationcancel", onCancel); node.addEventListener("animationend", onEnd); const cleanupStyles = (0, import_dom_query.setStyle)(node, { animationFillMode: "forwards" }); return () => { node.removeEventListener("animationstart", onStart); node.removeEventListener("animationcancel", onCancel); node.removeEventListener("animationend", onEnd); (0, import_dom_query.nextTick)(() => cleanupStyles()); }; } } } }); function getAnimationName(styles) { return styles?.animationName || "none"; } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { machine });