@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
JavaScript
// 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