alien-dom
Version:
Next-generation JSX client renderer with observable data primitives, immediate DOM references, and more.
1,791 lines (1,740 loc) • 83.4 kB
JavaScript
import { animate, createAlienElementList } from './chunk-YVHVFFPI.mjs';
export { AlienElement, addClass, animate, hasClass, hasEveryClass, hasSomeClass, matchClass, mixColor, observeAs, parseColor, patchAttributes, removeClass, removeMatchingClass, toggleClass } from './chunk-YVHVFFPI.mjs';
import { definePrivateSymbol, setPrivate, kAlienStateless, ref, expectCurrentComponent, useConstructor, depsHaveChanged, isFragment, isElement, isArray, isFunction, observeArrayOperations, isPlainObject, useDepsArray, peek, createState, arrayRef, onceMounted, kAlienUnmountHandler, unmount, setElementKey, isNode, forwardContext, currentNodeStore, keys, getShadowRoot, onMount, isDocument, computed, getPrivate, kAlienInitialContext, defineContext, toArray, isNumber, at, defineProperty, getHostProps, createDisposable, createElementProxy, onceElementExists, createGuid, isString, noop, observe, attachRef, isBoolean, useRef, updateStyle, kAlienNodeType, kShadowRootNodeType, resolveChildren, getFragmentNodes, getElementKey, isTextNode, isShadowRoot, createFragment, isDeferredNode, morphFragment, evaluateDeferredNode, isPromiseLike, jsx, Fragment, isRef, isWhat, flattenClassProp, AlienComponent, findFirstElement, findLastElement, fragmentToChildNodes, isClass, canMatch, attachDisposer, isDisposable, hasForEach, isIterable, morphRootNode, endOfFragment, mountAfterNode, getElementTags, observeNewDescendants, observeRemovedDescendants, getAnimatedKeys, isElementProxy, kTemplateNodeType, createEffect } from './chunk-JH7ONIU5.mjs';
export { AlienEffects, AlienMountEffects, ArrayObserver, ArrayRef, ComputedRef, ContextStore, Fragment, LensRef, Observer, ReadonlyArrayRef, ReadonlyRef, Ref, RefMap, SVGNamespace, arrayRef, attachDisposer, attachRef, collectAccessedRefs, computed, computedEvery, computedSome, createAsyncEffect, createDisposable, createEffect, createElement, createElementProxy, createFragment, createOnceEffect, defineContext, defineEffectType, depsHaveChanged, evaluateInput, getCurrentEffect, getElementIdentity, guardRef, isArrayRef, isChildrenFragment, isComment, isDisposable, isDocument, isElement, isElementProxy, isFragment, isJSXChild, isNode, isReadonlyRef, isRef, isShadowRoot, isTemplateNode, isTextNode, lens, matchDescendants, mergeDisposables, morphRootNode, mount, mountAfterNode, mountBeforeNode, mountFirstChild, mountLastChild, mountReplacementNode, observe, observeArrayOperations, observeNewChildren, observeNewDescendants, observeRemovedChildren, observeRemovedDescendants, onMount, onUnmount, peek, ref, refMap, setObservableHooks, unmount, unref, useChildren, useDepsArray, useRef, when } from './chunk-JH7ONIU5.mjs';
// hooks/useEffect.ts
function useEffect(effect, deps) {
const component = expectCurrentComponent();
const hook = useConstructor(UseEffect);
if (depsHaveChanged(deps, hook.deps)) {
component.newEffects.run(() => {
hook.component = component;
hook.effect = effect;
hook.deps = deps;
hook.run();
});
}
}
var UseEffect = class {
deps = void 0;
component = null;
effect = void 0;
dispose = void 0;
rerun = void 0;
runs = 0;
run() {
this.dispose?.();
this.dispose = this.effect ? (0, this.effect)(this) : void 0;
this.runs++;
}
get isFirstRun() {
return this.runs === 0;
}
/**
* Access the root node of the current component. If a fragment or primitive
* is returned by the component, this will be a `Comment` node. If the root
* node changes, your effect will rerun.
*/
get rootNode() {
this.component.rootNodeCallbacks ||= /* @__PURE__ */ new Set();
this.component.rootNodeCallbacks.add(this.rerun ||= this.run.bind(this));
const node = this.component.rootNode;
return isFragment(node) ? node.firstChild : node;
}
/**
* Type cast the root node of the current component as a`JSX.Element` object.
* If the root node changes, your effect will rerun.
*
* Note: This accessor is unsafe if the component ever returns a fragment or
* primitive. In those cases, you'll want to either use `rootNode` instead or
* check for null.
*/
get rootElement() {
const { rootNode } = this;
return isElement(rootNode) ? rootNode : null;
}
/**
* Access the parent node of the current component.
*
* Caveat: If the parent node changes, your effect will not rerun.
*/
get parentNode() {
return this.component.rootNode.parentNode;
}
};
function useWrappedEffect(effect, wrapper, deps) {
useEffect(effect && ((context) => wrapper(() => effect(context))), deps);
}
// hooks/useUpdateEffect.ts
function useUpdateEffect(effect, deps) {
useEffect(
effect && ((ctx) => {
if (!ctx.isFirstRun) {
return effect(ctx);
}
}),
deps
);
}
// hooks/useAbortController.ts
function useAbortController(arg, deps) {
const signal = isArray(arg) ? (deps = arg, void 0) : arg;
const hook = useConstructor(UseAbortController);
useEffect(() => {
hook.setSignal(signal);
}, [signal]);
useUpdateEffect(() => {
hook.ctrl.abort();
hook.ctrl = new AbortController();
}, deps ?? []);
return hook.ctrl;
}
var UseAbortController = class {
ctrl = new AbortController();
signal = void 0;
abort = void 0;
setSignal(signal) {
if (this.signal) {
this.signal.removeEventListener("abort", this.abort);
}
this.signal = signal;
this.abort = signal ? () => this.ctrl.abort() : void 0;
signal?.addEventListener("abort", this.abort);
}
dispose() {
this.ctrl.abort();
this.setSignal(void 0);
}
};
// hooks/useHookOffset.ts
function useHookOffset(offset) {
const component = expectCurrentComponent();
for (let i = 0; i < offset; i++) {
const index = component.nextHookIndex + i;
const hook = component.hooks[index];
if (hook && isFunction(hook.dispose)) {
hook.dispose();
}
component.hooks[index] = void 0;
}
component.nextHookIndex += offset;
}
// hooks/internal/useApply.ts
function useApply(apply) {
const component = expectCurrentComponent();
component.newEffects.run(apply);
}
// hooks/useStableCallback.ts
function useStableCallback(callback) {
const state = useConstructor(UseStableCallback);
useApply(() => {
state.callback = callback;
});
return state.wrapper;
}
var UseStableCallback = class {
callback = false;
wrapper = (...args) => {
if (this.callback) {
return this.callback.apply(this, args);
}
};
};
// hooks/useArrayObserver.ts
function useArrayObserver(arrayRef2, handler) {
if (!arrayRef2) {
useHookOffset(2);
return;
}
handler = useStableCallback(handler);
useEffect(() => {
return observeArrayOperations(arrayRef2, handler).destructor;
}, [arrayRef2]);
}
// functions/objectToDeps.ts
function objectToDeps(object) {
const deps = [];
for (const key of Object.keys(object).sort()) {
if (object[key] !== void 0) {
deps.push(key, object[key]);
}
}
return deps;
}
// hooks/useConst.ts
function useConst(init, ...params) {
const component = expectCurrentComponent();
const index = component.nextHookIndex++;
const deps = params.length === 1 && isPlainObject(params[0]) ? objectToDeps(params[0]) : params;
const oldState = component.hooks[index];
if (useDepsArray(deps)) {
if (oldState && isFunction(oldState.dispose))
useApply(() => {
oldState.dispose();
});
return component.hooks[index] = peek(createState, init, params);
}
return oldState;
}
// hooks/useArrayRef.ts
var useArrayRef = /* @__PURE__ */ useConst.bind(null, arrayRef);
// hooks/useMemo.ts
function useMemo(arg, deps = []) {
const state = useConstructor(UseMemo);
if (depsHaveChanged(deps, state.deps)) {
const value = isFunction(arg) ? peek(arg) : arg;
useApply(() => {
state.value = value;
state.deps = deps;
});
return value;
}
return state.value;
}
var UseMemo = class {
value = void 0;
deps = void 0;
dispose = true;
};
// hooks/useView.tsx
var nextViewId = 1;
function useView(view, deps) {
const node = useMemo(initViewNode, deps);
useEffect(() => {
let disposeMountEffect = onceMounted(node, function mountEffect() {
const dispose = view(node.parentNode);
setPrivate(node, kAlienUnmountHandler, () => {
dispose();
disposeMountEffect = onceMounted(node, mountEffect);
});
});
return () => {
unmount(node);
disposeMountEffect?.();
};
}, [node]);
return node;
}
function initViewNode() {
const node = document.createTextNode("");
setElementKey(node, "useView#" + nextViewId++);
return node;
}
// hooks/useArrayView.tsx
function useArrayView(array, arg2, arg3, deps) {
if (!array) {
useHookOffset(5);
return null;
}
const component = expectCurrentComponent();
const view = useMemo(initArrayViewState, [array]);
view.context = component.context;
const options = arg2 && !isFunction(arg2) ? arg2 : {};
let render = isFunction(arg2) ? arg2 : arg3;
if (isArray(arg3)) {
deps = arg3;
}
useEffect(() => {
if (view.mounted) {
const items = array.peek();
renderArrayView(view, { type: "update", items }, render, options);
}
}, deps || [render]);
render = useStableCallback(render);
view.head = useView(() => {
const items = array.peek();
renderArrayView(view, { type: "mount", items }, render, options);
view.mounted = true;
const observer = observeArrayOperations(array, (operations) => {
for (const operation of operations) {
view[operation.type](operation, render, options);
}
for (const node of view.itemsToUnmount.values()) {
unmount(isNode(node) ? node : node.rootNode);
}
view.itemsToUnmount.clear();
});
observer.isObservablyPure = () => true;
return () => {
observer.dispose();
for (const node of view.itemNodes) {
unmount(isNode(node) ? node : node.rootNode);
}
view.itemNodes.length = 0;
view.itemKeys.length = 0;
view.nextItemKey = 1;
view.mounted = false;
};
}, [view]);
return view.head;
}
var nextViewKey = 1;
var ArrayViewState = class {
key = nextViewKey++;
head;
context;
mounted = false;
itemKeys = [];
itemNodes = [];
itemUpdates = /* @__PURE__ */ new Map();
itemsToUnmount = /* @__PURE__ */ new Map();
nextItemKey = 1;
/**
* The `view` method is called once per item. It calls the `children` prop
* to render the item, and keeps track of the item's key and node.
*/
mountItem(item, index, render, options) {
const getItemKey = options?.getItemKey;
const itemKey = getArrayViewItemKey(
this,
getItemKey ? getItemKey(item) : this.nextItemKey++
);
let rootNode;
let itemNode;
const reusedItemNode = getItemKey && this.itemsToUnmount.get(itemKey);
if (reusedItemNode) {
this.itemsToUnmount.delete(itemKey);
itemNode = reusedItemNode;
rootNode = isNode(itemNode) ? itemNode : itemNode.rootNode;
if (isFragment(rootNode)) {
const childNodes = fragmentToChildNodes(rootNode);
rootNode = document.createDocumentFragment();
for (const childNode of childNodes) {
rootNode.append(childNode);
}
}
} else {
const itemResult = render(item, itemKey);
rootNode = morphRootNode(null, itemResult, void 0);
validateArrayViewItemResult(rootNode, itemResult, itemKey);
itemNode = getArrayViewItemNode(rootNode);
}
const { itemNodes, itemKeys } = this;
itemNodes[index] = itemNode;
itemKeys[index] = itemKey;
let prevSibling;
for (let i = index; i >= 0; i--) {
prevSibling = i > 0 ? itemNodes[i - 1] : this.head;
if (prevSibling) {
break;
}
}
if (isNode(prevSibling)) {
if (isFragment(prevSibling)) {
const lastChild = endOfFragment(prevSibling);
mountAfterNode(lastChild, rootNode);
} else {
mountAfterNode(prevSibling, rootNode);
}
} else {
mountAfterNode(prevSibling.lastChild, rootNode);
}
}
updateItem(item, index, render) {
const itemKey = this.itemKeys[index];
const itemResult = render(item, itemKey);
let rootNode = this.itemNodes[index];
rootNode = morphRootNode(
isNode(rootNode) ? rootNode : rootNode.rootNode,
itemResult,
itemKey,
this.context,
this
);
validateArrayViewItemResult(rootNode, itemResult, itemKey);
const itemNode = getArrayViewItemNode(rootNode);
this.itemNodes[index] = itemNode;
}
add(operation, render, options) {
const slots = Array(operation.count);
this.itemNodes.splice(operation.index, 0, ...slots);
this.itemKeys.splice(operation.index, 0, ...slots);
renderArrayView(this, operation, render, options);
}
remove(operation) {
for (let offset = 0; offset < operation.count; offset++) {
const index = operation.index + offset;
const node = this.itemNodes[index];
if (node) {
this.itemsToUnmount.set(this.itemKeys[index], node);
}
}
this.itemNodes.splice(operation.index, operation.count);
this.itemKeys.splice(operation.index, operation.count);
}
replace(operation, render, options) {
const node = this.itemNodes[operation.index];
if (node) {
this.itemsToUnmount.set(this.itemKeys[operation.index], node);
}
renderArrayView(this, operation, render, options);
}
// TODO: Try to reuse old item nodes if possible?
rebase(operation, render, options) {
this.itemNodes.forEach((node, index) => {
this.itemsToUnmount.set(this.itemKeys[index], node);
});
this.itemNodes.length = 0;
this.itemKeys.length = 0;
renderArrayView(this, operation, render, options);
}
//
// NodeStore interface
//
getNodeForKey(key) {
const itemIndex = this.itemKeys.indexOf(key);
if (itemIndex !== -1) {
const itemNode = this.itemNodes[itemIndex];
return isNode(itemNode) ? itemNode : itemNode.rootNode;
}
}
setNodeForKey(_key, _node) {
}
getNodeUpdateForKey(key) {
return this.itemUpdates.get(key);
}
setNodeUpdateForKey(key, update) {
this.itemUpdates.set(key, update);
}
};
function initArrayViewState() {
return new ArrayViewState();
}
function getArrayViewItemKey(view, itemKey) {
return `${view.key}@${itemKey}`;
}
function getArrayViewItemNode(rootNode) {
const tags = getElementTags(rootNode);
return tags && Array.from(tags.values()).pop() || rootNode;
}
function validateArrayViewItemResult(rootNode, itemResult, itemKey) {
const rootKey = getElementKey(rootNode);
if (!itemResult) {
setElementKey(rootNode, itemKey);
} else if (rootKey !== itemKey) {
throw Error(
`ArrayView item key mismatch. Expected ${JSON.stringify(
itemKey
)}, but got ${JSON.stringify(rootKey)}.`
);
}
}
function renderArrayView(view, props, render, options) {
const restoreContext = forwardContext(view.context);
currentNodeStore.push(view);
try {
switch (props.type) {
case "mount":
return props.items.forEach((item, i) => {
view.mountItem(item, i, render, options);
});
case "update":
return props.items.forEach((item, i) => {
view.updateItem(item, i, render);
});
case "add":
for (let offset = 0; offset < props.count; offset++) {
const index = props.index + offset;
if (props.newArray.hasOwnProperty(index)) {
view.mountItem(props.newArray[index], index, render, options);
}
}
break;
case "replace":
return view.mountItem(props.newValue, props.index, render, options);
case "rebase":
return props.newArray.forEach((item, i) => {
view.mountItem(item, i, render, options);
});
}
} finally {
currentNodeStore.pop();
restoreContext();
}
}
// hooks/useAssign.ts
function useAssign(target, source) {
const initial = useConst(clone, target);
useApply(() => {
const update = Object.assign({}, initial, source);
for (const key of keys(update)) {
if (!(key in initial)) {
throw Error(
`useAssign: The property "${key}" does not exist in the target object. Did you misspell the property or forget to initialize it?`
);
}
target[key] = update[key] === void 0 ? initial[key] : update[key];
}
});
return initial;
}
function clone(target) {
return Object.assign({}, target);
}
// hooks/useAsync.ts
function useAsync(get, deps = []) {
const instance = useConstructor(UseAsync);
if (!get) {
instance.stale = true;
instance.abort();
} else {
const reset = depsHaveChanged(deps, instance.deps);
instance.deps = deps;
if (reset && instance.numAttempts > 0) {
instance.abort();
}
if (reset || instance.stale) {
let promise;
try {
instance.markAttempt();
promise = resolveAsyncResults(get(instance));
} catch (error) {
promise = Promise.reject(error);
}
instance.setPromise(promise);
}
}
return instance;
}
var UseAsync = class {
state = ref(null);
abortCtrl = new AbortController();
timestamp = null;
promise = null;
effects = [];
numAttempts = 0;
stale = true;
deps = void 0;
/** @observable */
get status() {
const state = this.state.value;
return state ? state.aborted ? "idle" : state.retries ? "loading" : keys(state)[0] : this.promise ? "loading" : "idle";
}
/** @observable */
get result() {
return this.state.value?.loaded;
}
/** @observable */
get error() {
return this.state.value?.error;
}
get abort() {
return () => {
const state = this.state.peek();
if (state && !state.aborted) {
this.abortCtrl.abort();
this.promise = null;
this.numAttempts = 0;
this.state.value = { aborted: true };
this.effects.forEach((effect) => effect.dispose());
}
};
}
get aborted() {
return this.abortCtrl.signal.aborted;
}
get abortSignal() {
return this.abortCtrl.signal;
}
get retry() {
return (reset) => {
if (reset) {
this.abort();
}
const state = this.state.peek();
if (state && !("value" in state)) {
this.stale = true;
this.state.value = {
retries: (state.retries || 0) + 1
};
}
};
}
markAttempt() {
this.stale = false;
this.timestamp = Date.now();
this.numAttempts++;
if (this.aborted) {
this.abortCtrl = new AbortController();
}
}
setPromise(promise) {
this.promise = promise = promise.then(
(loaded) => {
if (promise === this.promise) {
this.promise = null;
this.state.value = { loaded };
}
},
(error) => {
if (promise === this.promise) {
this.promise = null;
this.state.value = { error };
}
}
);
}
/**
* Track a disposable effect that will be disposed when the component unmounts
* or the async getter is called again.
*/
get track() {
const { abortSignal, effects } = this;
function track(effect) {
if (isFunction(effect)) {
effect = { dispose: effect };
}
if (abortSignal.aborted) {
effect.dispose();
abortSignal.throwIfAborted();
}
effects.push(effect);
return effect;
}
return track;
}
};
function resolveAsyncResults(results) {
if (!isPromiseLike2(results)) {
if (Array.isArray(results)) {
return Promise.all(results.map(resolveAsyncResults));
}
if (isPlainObject(results)) {
const promises = [];
for (const key in results) {
const value = results[key];
if (isPromiseLike2(value)) {
promises.push(
value.then((result) => {
results[key] = result;
})
);
}
}
return Promise.all(promises).then(() => results);
}
}
return Promise.resolve(results);
}
function isPromiseLike2(value) {
return !!value && typeof value.then === "function";
}
// hooks/useAutoBind.ts
function useAutoBind(target) {
return new Proxy(target, traps);
}
var traps = {
get(target, key) {
const value = Reflect.get(target, key);
if (typeof value === "function") {
return value.bind(target);
}
return value;
}
};
// hooks/useEventTarget.ts
function useEventTarget(effect) {
const self = useConst(initEventTarget, effect);
const component = expectCurrentComponent();
useEffect(() => {
if (!self.enabled) {
return self.enable(component.ownerDocument);
}
}, []);
return self;
}
var initEventTarget = (enable) => ({
setElement(element) {
if (this.enabled) {
this.dispose?.();
this.dispose = void 0;
}
this.enabled = element != null;
if (element) {
if (element.isConnected) {
const result = this.enable(element);
if (result) {
this.dispose = result;
}
} else {
const shadowRoot = getShadowRoot();
this.dispose = onMount(
element,
() => this.setElement(element),
shadowRoot
).dispose;
}
}
},
enabled: false,
enable,
dispose: void 0
});
// hooks/useClickOutside.ts
function useClickOutside(handler) {
handler = useStableCallback(handler);
return useEventTarget((target) => {
if (isDocument(target)) {
return;
}
const onClick = (event) => {
if (!target.contains(event.target)) {
handler();
}
};
document.addEventListener("click", onClick, {
capture: true,
passive: true
});
return () => {
document.removeEventListener("click", onClick);
};
});
}
// hooks/useComputed.ts
function useComputed(get, deps = [], debugId) {
const state = useConstructor(UseComputed);
if (depsHaveChanged(deps, state.deps)) {
state.ref = computed(get, debugId);
state.deps = deps;
}
return state.ref;
}
var UseComputed = class {
ref = void 0;
deps = void 0;
dispose = true;
};
// hooks/useContext.ts
function useContext(context) {
const component = expectCurrentComponent();
if (context) {
const current = component.context.get(context);
return current ? current.value : getPrivate(context, kAlienInitialContext);
}
const index = component.nextHookIndex++;
return component.hooks[index] ||= defineContext(component.context);
}
// hooks/useDelayedEffect.ts
function useDelayedEffect(delay, effect, deps = []) {
useWrappedEffect(
effect,
(effect2) => {
let result;
let numPending = 0;
const finishOne = () => {
if (--numPending == 0) {
try {
result = effect2();
} catch (error) {
console.error(error);
}
}
};
const disposers = toArray(delay).map((delay2) => {
numPending++;
if (isNumber(delay2)) {
const timerId = setTimeout(finishOne, delay2);
return () => clearTimeout(timerId);
}
delay2.then(finishOne);
return () => delay2.dispose();
});
return () => {
if (isFunction(result)) {
try {
result();
} catch (error) {
console.error(error);
}
} else {
disposers.forEach((dispose) => dispose());
}
};
},
deps
);
}
// addons/elementRef.ts
var ElementRef = class {
element = null;
dispose = void 0;
setElement(element) {
if (this.element) {
if (element === this.element) {
return;
}
this.detach?.(this.element);
this.dispose?.();
}
this.element = element;
if (element) {
this.dispose = this.attach?.(element);
}
}
};
var DelegatedElementRef = class extends ElementRef {
constructor(delegate, key) {
super();
this.delegate = delegate;
this.key = key;
}
attach(element) {
return this.delegate.attach?.(element, this);
}
detach(element) {
this.delegate.detach?.(element, this);
}
};
// hooks/useElementArray.ts
var useElementArray = (deps) => useMemo(ElementArray, deps);
var ElementArray = class extends Array {
/**
* Get the element at the given index. Negative indices are allowed.
*/
get(index) {
const ref2 = at(this, index);
return ref2?.element ?? null;
}
/**
* Get an element ref to hold an element for the given index. Pass the result
* as the `ref` prop of the JSX element whose DOM node you need a reference
* to.
*/
bind(index) {
return this[index] || new DelegatedElementRef(this, index);
}
attach(_element, ref2) {
this[ref2.key] = ref2;
}
detach(_element, ref2) {
this[ref2.key] = void 0;
this.length = lastDefinedIndex(this) + 1;
}
};
function lastDefinedIndex(array) {
for (let i = array.length - 1; i >= 0; i--) {
if (array[i] !== void 0) {
return i;
}
}
return -1;
}
// addons/elementBounds.ts
var ElementBounds = class {
constructor(element) {
if (element)
this.setElement(element);
}
rectRef = ref(null);
resizeEffect;
observedElement = null;
observer = null;
locked = false;
topRef;
rightRef;
bottomRef;
leftRef;
widthRef;
heightRef;
get x() {
return this.left;
}
get y() {
return this.top;
}
get top() {
return this.observe("top").value;
}
get right() {
return this.observe("right").value;
}
get bottom() {
return this.observe("bottom").value;
}
get left() {
return this.observe("left").value;
}
get width() {
return this.observe("width").value;
}
get height() {
return this.observe("height").value;
}
toJSON() {
return this.rectRef.value?.toJSON();
}
observe(key) {
const propertyName = `${key}Ref`;
let ref2 = this[propertyName];
if (!ref2)
defineProperty(this, propertyName, {
enumerable: true,
value: ref2 = computed(() => this.rectRef.value?.[key] ?? NaN)
});
return ref2;
}
setElement(element) {
if (this.locked)
return;
if (this.observedElement = element) {
this.setupResizeEffect(element);
} else {
this.resizeEffect?.dispose();
this.resizeEffect = void 0;
}
}
/**
* While locked, the current bounds won't be updated when the target element
* changes.
*/
lock(flag) {
if (flag === this.locked)
return;
this.locked = flag;
if (this.locked) {
this.resizeEffect?.dispose();
this.resizeEffect = void 0;
} else if (this.observedElement) {
this.setupResizeEffect(this.observedElement);
}
}
dispose() {
this.setElement(null);
}
setupResizeEffect(element) {
this.rectRef.value = element.getBoundingClientRect();
const observer = new ResizeObserver(() => {
if (!this.locked) {
this.rectRef.value = element.getBoundingClientRect();
}
});
observer.observe(element);
const hostProps = getHostProps(element);
this.resizeEffect = hostProps.addEffect(
createDisposable([], observer.disconnect, observer)
);
}
};
// hooks/useElementBounds.ts
function useElementBounds(deps) {
return useMemo(ElementBounds, deps);
}
// hooks/useElementMap.ts
var useElementMap = (deps) => useMemo(ElementMap, deps);
var ElementMap = class {
constructor(delegate) {
this.delegate = delegate;
}
map = /* @__PURE__ */ new Map();
/**
* Get the element at the given key. Returns `null` if the key is not found.
*/
get(key) {
const ref2 = this.map.get(key);
return ref2?.element ?? null;
}
/**
* Get an element ref to hold an element for the given key. Pass the result as
* the `ref` prop of the JSX element whose DOM node you need a reference to.
*/
bind(key) {
return this.map.get(key) || new DelegatedElementRef(this, key);
}
/** @internal */
attach(element, ref2) {
this.map.set(ref2.key, ref2);
return this.delegate?.attach?.(element, ref2);
}
/** @internal */
detach(element, ref2) {
this.map.delete(ref2.key);
this.delegate?.detach?.(element, ref2);
}
[Symbol.iterator]() {
const entries = this.map.entries();
return {
next() {
const result = entries.next();
if (result.done) {
return result;
}
return {
done: false,
value: [result.value[0], result.value[1].element]
};
}
};
}
};
// hooks/useElementProxy.ts
function useElementProxy(effect, deps) {
const proxy = useConst(createElementProxy);
if (effect) {
useEffect(() => {
return onceElementExists(proxy, effect);
}, deps);
} else {
useHookOffset(1);
}
return proxy;
}
// hooks/useForceUpdate.ts
function useForceUpdate() {
const component = expectCurrentComponent();
const index = component.nextHookIndex++;
return component.hooks[index] ||= component.update.bind(component);
}
// hooks/useGlobalId.ts
function useGlobalId(reset, generateId) {
const component = expectCurrentComponent();
return createGuid(
component.hooks,
component.nextHookIndex++,
reset,
generateId
);
}
// hooks/useInterval.ts
var useInterval = (callback, delay, deps = []) => useEffect(() => {
if (delay === null)
return;
const id = setInterval(callback, delay);
return () => clearInterval(id);
}, [delay, ...deps]);
// hooks/useKeyBinding.ts
function useKeyBinding(combo, onKeyDown, options) {
const component = expectCurrentComponent();
const binding = useConst(initKeyBinding);
useApply(() => {
binding.combo = prepareCombo(combo);
binding.options = options;
binding.onKeyDown = onKeyDown;
});
useEffect(() => {
if (!binding.effect) {
return binding.enable(component.ownerDocument, options);
}
}, []);
return binding;
}
var initKeyBinding = () => {
const isActiveRef = ref(false);
const comboRef = ref();
return {
get isActive() {
return isActiveRef.value;
},
set isActive(value) {
isActiveRef.value = value;
},
effect: null,
get combo() {
return comboRef.value;
},
set combo(newCombo) {
const oldCombo = comboRef.peek();
if (!oldCombo || !setsEqual(newCombo, oldCombo)) {
comboRef.value = newCombo;
}
},
options: void 0,
onKeyDown: void 0,
onKeyUp: void 0,
enable(target, options) {
enableKeyBinding(target, this, options);
return () => {
disableKeyBinding(target, this, options);
};
},
setElement(element) {
if (!element) {
this.effect?.dispose();
return;
}
const hostProps = getHostProps(element);
enableKeyBinding(element, this, this.options);
this.effect = hostProps.addEffect(
createDisposable([element, this, this.options], disableKeyBinding)
);
}
};
};
function makeContextKey(options) {
if (!options) {
return "{}";
}
const sortedOptions = Object.keys(options).filter((key) => options[key]).sort().reduce((obj, key) => {
obj[key] = options[key];
return obj;
}, {});
return JSON.stringify(sortedOptions);
}
function enableKeyBinding(target, binding, options) {
const contexts = contextsByTarget.get(target) || /* @__PURE__ */ new Map();
contextsByTarget.set(target, contexts);
const contextKey = makeContextKey(options);
const context = contexts.get(contextKey) || new KeyBindingContext(target, options);
contexts.set(contextKey, context);
context.addBinding(binding);
}
function disableKeyBinding(target, binding, options) {
const contexts = contextsByTarget.get(target);
if (contexts) {
const contextKey = makeContextKey(options);
const context = contexts.get(contextKey);
context?.removeBinding(binding);
}
}
var isWindows = /* @__PURE__ */ navigator.platform.includes("Win");
function prepareCombo(combo, keys2 = /* @__PURE__ */ new Set()) {
for (let key of toArray(combo)) {
if (!key)
continue;
if (isString(key)) {
if (isWindows && key === "Meta") {
key = "Control";
}
keys2.add(key.toLowerCase());
} else {
prepareCombo(key, keys2);
}
}
return keys2;
}
var shiftedKeys = '~!@#$%^&*()_+{}|:"<>?';
var unshiftedKeys = "`1234567890-=[]\\;',./";
var modifierKeys = ["shift", "control", "alt", "meta", "fn", "hyper", "super"];
var contextsByTarget = /* @__PURE__ */ new Map();
var KeyBindingContext = class {
constructor(target, options) {
this.target = target;
this.options = options;
if (!isDocument(target) && !supportsKeyDown(target)) {
target.setAttribute("tabindex", "0");
}
const { activeKeys } = this;
const onKeyChange = (event) => {
let stopPropagation = false;
const isModifierChange = event && modifierKeys.includes(event.key.toLowerCase());
for (const binding of this.bindings) {
if (event && comboMatches(binding.combo, activeKeys)) {
if (stopPropagation || isModifierChange)
continue;
if (event.type === "keydown") {
binding.isActive = true;
if (binding.onKeyDown) {
binding.onKeyUp = binding.onKeyDown({
target,
repeat: event?.repeat ?? false,
preventDefault: event?.preventDefault.bind(event) ?? noop,
stopPropagation() {
stopPropagation = true;
}
});
}
}
} else if (binding.isActive) {
binding.isActive = false;
if (binding.onKeyUp) {
binding.onKeyUp();
binding.onKeyUp = void 0;
}
}
}
if (stopPropagation) {
event?.stopPropagation();
}
if (activeKeys.size === 0 && this.newBindings.length > 0) {
for (const binding of this.newBindings) {
this.bindings.push(binding);
}
this.newBindings.length = 0;
}
};
const onKeyDown = (event) => {
const key = event.key.toLowerCase();
activeKeys.add(key);
onKeyChange(event);
};
const onKeyUp = (event) => {
const key = event.key.toLowerCase();
activeKeys.delete(key);
if (modifierKeys.includes(key)) {
activeKeys.forEach((key2) => {
if (!modifierKeys.includes(key2)) {
activeKeys.delete(key2);
}
});
}
onKeyChange(event);
};
const clear = () => {
if (activeKeys.size > 0) {
activeKeys.clear();
onKeyChange();
}
};
target.addEventListener("keydown", onKeyDown, options);
target.addEventListener("keyup", onKeyUp, options);
target.addEventListener("paste", clear, options);
window.addEventListener("blur", clear, options);
this.dispose = () => {
const contextKey = makeContextKey(options);
const contexts = contextsByTarget.get(target);
if (contexts.delete(contextKey) && contexts.size === 0) {
contextsByTarget.delete(target);
}
target.removeEventListener("keydown", onKeyDown, options);
target.removeEventListener("keyup", onKeyUp, options);
target.removeEventListener("paste", clear, options);
window.removeEventListener("blur", clear, options);
};
}
bindings = [];
newBindings = [];
activeKeys = /* @__PURE__ */ new Set();
dispose;
addBinding(binding) {
if (this.activeKeys.size === 0) {
this.bindings.push(binding);
} else {
this.newBindings.push(binding);
}
}
removeBinding(binding) {
let index = this.newBindings.indexOf(binding);
if (index != -1) {
this.newBindings.splice(index, 1);
} else {
index = this.bindings.indexOf(binding);
if (index != -1) {
this.bindings.splice(index, 1);
}
}
const bindingCount = this.bindings.length + this.newBindings.length;
if (bindingCount === 0) {
this.dispose();
}
}
};
function supportsKeyDown(element) {
return element.matches(
"input, textarea, summary, [contenteditable], [tabindex]"
);
}
function comboMatches(combo, activeKeys) {
if (combo.size === 0) {
return false;
}
let shiftExpected = combo.has("shift");
let shiftImplied = false;
for (let key of combo) {
if (key.length === 1) {
if (shiftExpected) {
const index = unshiftedKeys.indexOf(key);
if (index >= 0) {
key = shiftedKeys[index];
}
} else if (shiftedKeys.includes(key)) {
shiftImplied = true;
}
}
if (!activeKeys.has(key)) {
return false;
}
}
if (shiftImplied && activeKeys.has("shift")) {
return combo.size === activeKeys.size - 1;
}
return combo.size === activeKeys.size;
}
function setsEqual(a, b) {
if (a.size !== b.size) {
return false;
}
for (const item of a) {
if (!b.has(item)) {
return false;
}
}
return true;
}
// addons/machine.ts
function defineMachine(setup) {
return class extends Machine {
constructor(params, onChange = noop) {
const stateRef = ref(void 0);
super(params, stateRef);
stateRef.value = setup(
params,
(arg1, arg2) => {
const newState = isString(arg1) ? { ...arg2, value: arg1 } : arg1;
stateRef.value = newState;
onChange(newState, this);
return newState;
},
this
);
}
};
}
var Machine = class {
constructor(params, stateRef) {
this.params = params;
this.stateRef = stateRef;
}
get state() {
return this.stateRef.value;
}
get value() {
return this.state.value;
}
peek() {
return this.stateRef.peek().value;
}
is(value) {
return isArray(value) ? value.includes(this.value) : this.value === value;
}
has(key) {
return key in this.state;
}
assert(value) {
if (this.value === value) {
return this.state;
}
throw Error(`Expected "${value}", got "${this.value}"`);
}
};
function toMachineProxy(machine) {
return new Proxy(machine, {
get(machine2, prop) {
if (prop in machine2) {
return machine2[prop];
}
if (prop in machine2.state) {
return machine2.state[prop];
}
if (prop === "dispose") {
return;
}
throw ReferenceError(
`Illegal property access: "${String(prop)}" in "${machine2.value}" state`
);
}
});
}
// hooks/useMachineProxy.ts
function useMachineProxy(constructor, params, onChange) {
if (isFunction(params)) {
onChange = params;
params = void 0;
}
const onChangeRef = useStableCallback(onChange);
const component = expectCurrentComponent();
const index = component.nextHookIndex++;
return component.hooks[index] ||= toMachineProxy(
new constructor(params, onChangeRef)
);
}
// hooks/useObjectMemo.ts
function useObjectMemo(object) {
return useMemo(object, objectToDeps(object));
}
// hooks/useObserver.ts
function useObserver(arg1, arg2) {
if (isFunction(arg1)) {
const effect = arg1, deps = arg2;
useHookOffset(1);
useWrappedEffect(effect, (effect2) => observe(effect2).destructor, deps);
} else if (arg1) {
const ref2 = arg1, onChange = useStableCallback(arg2);
useEffect(() => {
const initialValue = ref2.value;
onChange(initialValue, initialValue);
return observe(ref2, onChange).destructor;
}, [ref2]);
} else {
useHookOffset(2);
}
}
// hooks/usePrevious.ts
function usePrevious(value, deps) {
const state = useConstructor(UsePrevious);
useApply(() => {
state.prev = value;
state.deps = deps;
});
if (deps && depsHaveChanged(deps, state.deps)) {
return void 0;
}
return state.prev;
}
var UsePrevious = class {
prev = void 0;
deps = void 0;
dispose = true;
};
// functions/makeObjectObservable.ts
function makeObjectObservable(object) {
const boundRefs = {};
setPrivate(object, kBoundRefs, boundRefs);
for (const key of keys(object)) {
attachRef(object, key, boundRefs[key] = ref(object[key]));
}
defineProperty(object, "bind", { value: getBoundRef });
return object;
}
var kBoundRefs = definePrivateSymbol("boundRefs");
function getBoundRef(key) {
return getPrivate(this, kBoundRefs)[key];
}
// hooks/useRefs.ts
function useRefs(init, deps = []) {
const component = expectCurrentComponent();
const index = component.nextHookIndex++;
if (useDepsArray(deps)) {
const object = isFunction(init) ? peek(init) : init;
if (!isPlainObject(object)) {
throw Error("useRefs only accepts plain objects");
}
return component.hooks[index] = makeObjectObservable(object);
}
return component.hooks[index];
}
// hooks/useResetId.ts
function useResetId(reset, deps) {
if (reset === void 0) {
useHookOffset(4);
return false;
}
if (isString(reset)) {
useHookOffset(4);
return reset;
}
const state = useConst(UseResetId, isFunction(reset));
const container = state.ref || state;
if (isFunction(reset)) {
const compute = reset;
useObserver(() => {
const reset2 = compute();
const force2 = resolveReset(reset2, state.prevReset, false);
state.prevReset = isBoolean(reset2) ? void 0 : reset2;
container.value = createGuid(container, "value", force2);
}, deps);
return state.ref;
}
const force = resolveReset(reset, state.prevReset);
const result = createGuid(container, "value", force);
useApply(() => {
state.prevReset = isBoolean(reset) ? void 0 : reset;
container.value = result;
});
useHookOffset(2);
return result;
}
var UseResetId = class {
ref;
value = void 0;
prevReset = void 0;
constructor(isCompute) {
this.ref = isCompute ? ref(createGuid()) : void 0;
}
};
function resolveReset(reset, prevReset, defaultReset = true) {
if (isBoolean(reset)) {
return reset;
}
if (prevReset) {
return depsHaveChanged(reset, prevReset);
}
return defaultReset;
}
// hooks/useScrollStart.ts
function useScrollStart(handler) {
handler = useStableCallback(handler);
return useEventTarget((target) => {
if (isDocument(target)) {
target = target.scrollingElement;
} else {
target = findScrollingElement(target);
}
let started = false;
let stopTimer = -1;
const onScroll = (event) => {
if (started) {
clearTimeout(stopTimer);
stopTimer = setTimeout(() => {
started = false;
}, 100);
} else {
started = true;
handler(event);
}
};
target.addEventListener("scroll", onScroll, { passive: true });
return () => {
target.removeEventListener("scroll", onScroll);
};
});
}
function findScrollingElement(elem) {
const root = document.scrollingElement;
while (elem && elem !== root) {
const style = getComputedStyle(elem);
const overflow = style.overflow + style.overflowY + style.overflowX;
if (overflow.includes("scroll")) {
return elem;
}
elem = elem.parentElement;
}
return root;
}
// hooks/useSelector.ts
function observeDescendants(context, selector, onMatch) {
const onAdded = observeNewDescendants(context, (node) => {
if (isElement(node) && node.matches(selector)) {
onMatch(node, true);
}
});
const onRemoved = observeRemovedDescendants(context, (node) => {
if (isElement(node) && node.matches(selector)) {
onMatch(node, false);
}
});
return () => {
onAdded.dispose();
onRemoved.dispose();
};
}
function useQuerySelector(selector, context = document.body) {
const match = useRef(null);
useEffect(() => {
match.value = context.querySelector(selector);
return observeDescendants(context, selector, () => {
match.value = context.querySelector(selector);
});
}, [context, selector]);
return match;
}
function useQuerySelectorAll(selector, context = document.body) {
const matches = useRef();
matches.value ||= /* @__PURE__ */ new Set();
useEffect(() => {
context.querySelectorAll(selector).forEach((element) => {
matches.value.add(element);
});
return observeDescendants(
context,
selector,
(element, isConnected) => {
matches.value = new Set(matches.value);
if (isConnected) {
matches.value.add(element);
} else {
matches.value.delete(element);
}
}
);
}, [context, selector]);
return matches;
}
// internal/shallowEquals.ts
function shallowEquals(a, b) {
if (a === b) {
return true;
}
if (isPlainObject(a) && isPlainObject(b)) {
const keys2 = keysEqual(Object.keys(a), Object.keys(b));
return keys2 && keys2.every((key) => a[key] === b[key]);
}
return false;
}
function keysEqual(left, right) {
return left.every((key) => right.includes(key)) && right.every((key) => left.includes(key)) && left;
}
// hooks/useSpring.ts
function useSpring(element, animations, shouldRun) {
const { to, from } = Array.isArray(animations) ? mergeAnimations(animations) : animations;
const state = useConstructor(UseSpring);
if (shouldRun == null) {
shouldRun = !!to && !shallowEquals(state.to, to);
}
if (shouldRun) {
useApply(() => {
state.to = to;
state.from = from;
const animatedKeys = getAnimatedKeys(element);
for (const animation of toArray(animations)) {
let from2 = animation.from;
if (animation.to) {
from2 ||= {};
for (const key in animation.to) {
from2[key] ??= animation.to[key];
}
}
if (from2 && animatedKeys) {
for (const key of animatedKeys) {
delete from2[key];
}
}
}
animate(element, animations);
});
}
}
var UseSpring = class {
to = void 0;
from = void 0;
};
function mergeAnimations(animations) {
let to;
let from;
for (const animation of animations) {
for (const key in animation.to) {
to ||= {};
to[key] = animation.to[key];
}
for (const key in animation.from) {
from ||= {};
from[key] = animation.from[key];
}
}
return { to, from };
}
// hooks/useState.ts
var useState = (init) => [...useRef(init)];
// hooks/useStyle.ts
function useStyle(element, style, deps) {
const elements = toArray(element);
if (!isFunction(style)) {
deps = deps ? [...elements, ...deps] : elements;
const prevDeps = usePrevious(deps);
if (style && depsHaveChanged(deps, prevDeps))
for (const element2 of elements) {
updateStyle(element2, style);
}
} else if (deps) {
const state = useConstructor(UseStyle);
if (state.dispose && depsHaveChanged(deps, state.deps)) {
state.dispose();
state.dispose = void 0;
state.deps = deps;
}
const getStyle = style;
state.dispose ||= observe(() => {
const style2 = getStyle();
if (!style2)
return;
for (const element2 of elements) {
updateStyle(element2, style2, 2 /* NonAnimated */);
}
}).destructor;
} else {
useHookOffset(1);
}
}
var UseStyle = class {
deps = void 0;
dispose = void 0;
};
// hooks/useVisibility.ts
function useVisibility(target, options = {}, deps = []) {
const visibleRef = useRef(false);
useEffect(() => {
function observe2(target2) {
const init = {
...options,
root: isElementProxy(options.root) ? options.root.toElement() : options.root
};
const observer = new IntersectionObserver((entries) => {
const visible = entries[0].isIntersecting;
if (visible !== visibleRef.value) {
visibleRef.value = visible;
options.onChange?.(visible, entries[0]);
}
}, init);
observer.observe(target2);
return () => observer.disconnect();
}
return isElement(target) ? observe2(target) : target.onceElementExists(observe2).dispose;
}, [target, ...deps]);
return visibleRef;
}
// hooks/useYieldingEffect.ts
function useYieldingEffect(effect, deps) {
useEffect(() => {
const generator = effect();
let disposed = false;
const disposers = [];
const next = () => {
if (disposed)
return;
const result = generator.next();
if (result.done) {
if (isFunction(result.value)) {
disposers.push(result.value);
}
} else if (isFunction(result.value)) {
disposers.push(result.value);
} else if (result.value instanceof Promise) {
const promise = result.value;
promise.then(next, console.error);
if ("dispose" in promise) {
disposers.push(promise.dispose);
}
} else {
next();
}
};
next();
return () => {
disposed = true;
disposers.forEach((dispose) => dispose());
};
}, deps);
}
// core/template.ts
function template(template2) {
const node = { [kAlienNodeType]: kTemplateNodeType, template: template2 };
return node;
}
// components/ArrayView.tsx
function ArrayView({
array,
children: render,
deps,
...options
}) {
return useArrayView(array, options, render, deps);
}
// components/ShadowRoot.ts
function ShadowRoot({ children, ...props }) {
return {
[kAlienNodeType]: kShadowRootNodeType,
props,
children: resolveChildren(children)
};
}
setPrivate(ShadowRoot, kAlienStateless, true);
// functions/restoreNodeReferences.ts
function restoreNodeReferences(node) {
const component = expectCurrentComponent();
component.nodes ??= /* @__PURE__ */ new Map();
if (isFragment(node)) {
const childNodes = getFragmentNodes(node);
childNodes?.forEach((childNode) => {
if (childNode) {
restoreComponentRef(childNode, component);
}
});
} else {
restoreComponentRef(node, component);
}
}
function restoreComponentRef(node, component) {
const key = getElementKey(node);
if (key != null) {
component.nodes.set(key, node);
}
if (!isTextNode(nod