UNPKG

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
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