UNPKG

@wordpress/interactivity

Version:

Package that provides a standard and simple way to handle the frontend interactivity of Gutenberg blocks.

242 lines (241 loc) 5.93 kB
// packages/interactivity/src/utils.ts import { useMemo as _useMemo, useCallback as _useCallback, useEffect as _useEffect, useLayoutEffect as _useLayoutEffect } from "preact/hooks"; import { effect, signal } from "@preact/signals"; import { getScope, setScope, resetScope } from "./scopes"; import { getNamespace, setNamespace, resetNamespace } from "./namespaces"; var afterNextFrame = (callback) => { return new Promise((resolve) => { const done = () => { clearTimeout(timeout); window.cancelAnimationFrame(raf); setTimeout(() => { callback(); resolve(); }); }; const timeout = setTimeout(done, 100); const raf = window.requestAnimationFrame(done); }); }; var splitTask = typeof window.scheduler?.yield === "function" ? window.scheduler.yield.bind(window.scheduler) : () => { return new Promise((resolve) => { setTimeout(resolve, 0); }); }; function createFlusher(compute, notify) { let flush = () => void 0; const dispose = effect(function() { flush = this.c.bind(this); this.x = compute; this.c = notify; return compute(); }); return { flush, dispose }; } function useSignalEffect(callback) { _useEffect(() => { let eff = null; let isExecuting = false; const notify = async () => { if (eff && !isExecuting) { isExecuting = true; await afterNextFrame(eff.flush); isExecuting = false; } }; eff = createFlusher(callback, notify); return eff.dispose; }, []); } function withScope(func) { const scope = getScope(); const ns = getNamespace(); let wrapped; if (func?.constructor?.name === "GeneratorFunction") { wrapped = async (...args) => { const gen = func(...args); let value; let it; let error; while (true) { setNamespace(ns); setScope(scope); try { it = error ? gen.throw(error) : gen.next(value); error = void 0; } catch (e) { throw e; } finally { resetScope(); resetNamespace(); } try { value = await it.value; } catch (e) { error = e; } if (it.done) { if (error) { throw error; } else { break; } } } return value; }; } else { wrapped = (...args) => { setNamespace(ns); setScope(scope); try { return func(...args); } finally { resetNamespace(); resetScope(); } }; } const syncAware = func; if (syncAware.sync) { const syncAwareWrapped = wrapped; syncAwareWrapped.sync = true; return syncAwareWrapped; } return wrapped; } function useWatch(callback) { useSignalEffect(withScope(callback)); } function useInit(callback) { _useEffect(withScope(callback), []); } function useEffect(callback, inputs) { _useEffect(withScope(callback), inputs); } function useLayoutEffect(callback, inputs) { _useLayoutEffect(withScope(callback), inputs); } function useCallback(callback, inputs) { return _useCallback(withScope(callback), inputs); } function useMemo(factory, inputs) { return _useMemo(withScope(factory), inputs); } var createRootFragment = (parent, replaceNode) => { replaceNode = [].concat(replaceNode); const sibling = replaceNode[replaceNode.length - 1].nextSibling; function insert(child, root) { parent.insertBefore(child, root || sibling); } return parent.__k = { nodeType: 1, parentNode: parent, firstChild: replaceNode[0], childNodes: replaceNode, insertBefore: insert, appendChild: insert, removeChild(c) { parent.removeChild(c); }, contains(c) { parent.contains(c); } }; }; function kebabToCamelCase(str) { return str.replace(/^-+|-+$/g, "").toLowerCase().replace(/-([a-z])/g, function(_match, group1) { return group1.toUpperCase(); }); } var logged = /* @__PURE__ */ new Set(); var warn = (message) => { if (globalThis.SCRIPT_DEBUG) { if (logged.has(message)) { return; } console.warn(message); try { throw Error(message); } catch (e) { } logged.add(message); } }; var isPlainObject = (candidate) => Boolean( candidate && typeof candidate === "object" && candidate.constructor === Object ); function withSyncEvent(callback) { const syncAware = callback; syncAware.sync = true; return syncAware; } var readOnlyMap = /* @__PURE__ */ new WeakMap(); var createDeepReadOnlyHandlers = (errorMessage) => { const handleError = () => { if (globalThis.SCRIPT_DEBUG) { warn(errorMessage); } return false; }; return { get(target, prop) { const value = target[prop]; if (value && typeof value === "object") { return deepReadOnly(value, { errorMessage }); } return value; }, set: handleError, deleteProperty: handleError, defineProperty: handleError }; }; function deepReadOnly(obj, options) { const errorMessage = options?.errorMessage ?? "Cannot modify read-only object"; if (!readOnlyMap.has(obj)) { const handlers = createDeepReadOnlyHandlers(errorMessage); readOnlyMap.set(obj, new Proxy(obj, handlers)); } return readOnlyMap.get(obj); } var navigationSignal = signal(0); function deepClone(source) { if (isPlainObject(source)) { return Object.fromEntries( Object.entries(source).map(([key, value]) => [ key, deepClone(value) ]) ); } if (Array.isArray(source)) { return source.map((i) => deepClone(i)); } return source; } export { createRootFragment, deepClone, deepReadOnly, isPlainObject, kebabToCamelCase, navigationSignal, splitTask, useCallback, useEffect, useInit, useLayoutEffect, useMemo, useSignalEffect, useWatch, warn, withScope, withSyncEvent }; //# sourceMappingURL=utils.js.map