UNPKG

vidstack

Version:

Build awesome media experiences on the web.

1,584 lines (1,552 loc) 46.3 kB
// src/symbols.ts var SCOPE = Symbol("SCOPE" ); // src/core.ts var scheduledEffects = false; var runningEffects = false; var currentScope = null; var currentObserver = null; var currentObservers = null; var currentObserversIndex = 0; var effects = []; var NOOP = () => { }; var STATE_CLEAN = 0; var STATE_CHECK = 1; var STATE_DIRTY = 2; var STATE_DISPOSED = 3; function flushEffects() { scheduledEffects = true; queueMicrotask(runEffects); } function runEffects() { if (!effects.length) { scheduledEffects = false; return; } runningEffects = true; for (let i = 0; i < effects.length; i++) { if (effects[i]._state !== STATE_CLEAN) runTop(effects[i]); } effects = []; scheduledEffects = false; runningEffects = false; } function runTop(node) { let ancestors = [node]; while (node = node[SCOPE]) { if (node._effect && node._state !== STATE_CLEAN) ancestors.push(node); } for (let i = ancestors.length - 1; i >= 0; i--) { updateCheck(ancestors[i]); } } function root(init) { const scope = createScope(); return compute(scope, !init.length ? init : init.bind(null, dispose.bind(scope)), null); } function peek(fn) { return compute(currentScope, fn, null); } function untrack(fn) { return compute(null, fn, null); } function tick() { if (!runningEffects) runEffects(); } function getScope() { return currentScope; } function scoped(run, scope) { try { return compute(scope, run, null); } catch (error) { handleError(scope, error); return; } } function getContext(key, scope = currentScope) { return scope?._context[key]; } function setContext(key, value, scope = currentScope) { if (scope) scope._context = { ...scope._context, [key]: value }; } function onDispose(disposable) { if (!disposable || !currentScope) return disposable || NOOP; const node = currentScope; if (!node._disposal) { node._disposal = disposable; } else if (Array.isArray(node._disposal)) { node._disposal.push(disposable); } else { node._disposal = [node._disposal, disposable]; } return function removeDispose() { if (node._state === STATE_DISPOSED) return; disposable.call(null); if (isFunction$1(node._disposal)) { node._disposal = null; } else if (Array.isArray(node._disposal)) { node._disposal.splice(node._disposal.indexOf(disposable), 1); } }; } function dispose(self = true) { if (this._state === STATE_DISPOSED) return; let head = self ? this._prevSibling ?? this[SCOPE] : this, current = this._nextSibling; while (current && current[SCOPE] === this) { dispose.call(current, true); disposeNode(current); current = current._nextSibling; } if (self) disposeNode(this); if (current) current._prevSibling = !self ? this : this._prevSibling; if (head) head._nextSibling = current; } function disposeNode(node) { node._state = STATE_DISPOSED; if (node._disposal) emptyDisposal(node); if (node._sources) removeSourceObservers(node, 0); if (node._prevSibling) node._prevSibling._nextSibling = null; node[SCOPE] = null; node._sources = null; node._observers = null; node._prevSibling = null; node._context = null; node._handlers = null; } function emptyDisposal(scope) { try { if (Array.isArray(scope._disposal)) { for (let i = 0; i < scope._disposal.length; i++) { const callable = scope._disposal[i]; callable.call(callable); } } else { scope._disposal.call(scope._disposal); } scope._disposal = null; } catch (error) { handleError(scope, error); } } function compute(scope, compute2, observer) { const prevScope = currentScope, prevObserver = currentObserver; currentScope = scope; currentObserver = observer; try { return compute2.call(scope); } finally { currentScope = prevScope; currentObserver = prevObserver; } } function handleError(scope, error) { if (!scope) throw error; let i = 0, len = scope._handlers.length, coercedError = coerceError(error); for (i = 0; i < len; i++) { try { scope._handlers[i](coercedError); break; } catch (error2) { coercedError = coerceError(error2); } } if (i === len) throw coercedError; } function coerceError(error) { return error instanceof Error ? error : Error(JSON.stringify(error)); } function read() { if (this._state === STATE_DISPOSED) return this._value; if (currentObserver && !this._effect) { if (!currentObservers && currentObserver._sources && currentObserver._sources[currentObserversIndex] == this) { currentObserversIndex++; } else if (!currentObservers) currentObservers = [this]; else currentObservers.push(this); } if (this._compute) updateCheck(this); return this._value; } function write(newValue) { const value = isFunction$1(newValue) ? newValue(this._value) : newValue; if (this._changed(this._value, value)) { this._value = value; if (this._observers) { for (let i = 0; i < this._observers.length; i++) { notify(this._observers[i], STATE_DIRTY); } } } return this._value; } var ScopeNode = function Scope() { this[SCOPE] = null; this._nextSibling = null; this._prevSibling = null; if (currentScope) currentScope.append(this); }; var ScopeProto = ScopeNode.prototype; ScopeProto._context = {}; ScopeProto._handlers = []; ScopeProto._compute = null; ScopeProto._disposal = null; ScopeProto.append = function appendScope(scope) { scope[SCOPE] = this; scope._prevSibling = this; scope._context = this._context; scope._handlers = this._handlers; if (this._nextSibling) this._nextSibling._prevSibling = scope; scope._nextSibling = this._nextSibling; this._nextSibling = scope; }; function createScope() { return new ScopeNode(); } var ComputeNode = function Computation(initialValue, compute2, options) { ScopeNode.call(this); this._state = compute2 ? STATE_DIRTY : STATE_CLEAN; this._init = false; this._effect = false; this._sources = null; this._observers = null; this._value = initialValue; this.id = options?.id ?? (this._compute ? "computed" : "signal"); if (compute2) this._compute = compute2; if (options && options.dirty) this._changed = options.dirty; }; var ComputeProto = ComputeNode.prototype; Object.setPrototypeOf(ComputeProto, ScopeProto); ComputeProto._changed = isNotEqual; ComputeProto.call = read; function createComputation(initialValue, compute2, options) { return new ComputeNode(initialValue, compute2, options); } function isNotEqual(a, b) { return a !== b; } function isFunction$1(value) { return typeof value === "function"; } function updateCheck(node) { if (node._state === STATE_CHECK) { for (let i = 0; i < node._sources.length; i++) { updateCheck(node._sources[i]); if (node._state === STATE_DIRTY) { break; } } } if (node._state === STATE_DIRTY) update(node); else node._state = STATE_CLEAN; } function cleanup(node) { if (node._nextSibling && node._nextSibling[SCOPE] === node) dispose.call(node, false); if (node._disposal) emptyDisposal(node); node._handlers = node[SCOPE]?._handlers || []; } function update(node) { let prevObservers = currentObservers, prevObserversIndex = currentObserversIndex; currentObservers = null; currentObserversIndex = 0; try { cleanup(node); const result = compute(node, node._compute, node); if (currentObservers) { if (node._sources) removeSourceObservers(node, currentObserversIndex); if (node._sources && currentObserversIndex > 0) { node._sources.length = currentObserversIndex + currentObservers.length; for (let i = 0; i < currentObservers.length; i++) { node._sources[currentObserversIndex + i] = currentObservers[i]; } } else { node._sources = currentObservers; } let source; for (let i = currentObserversIndex; i < node._sources.length; i++) { source = node._sources[i]; if (!source._observers) source._observers = [node]; else source._observers.push(node); } } else if (node._sources && currentObserversIndex < node._sources.length) { removeSourceObservers(node, currentObserversIndex); node._sources.length = currentObserversIndex; } if (!node._effect && node._init) { write.call(node, result); } else { node._value = result; node._init = true; } } catch (error) { if (!node._init && typeof node._value === "undefined") { console.error( `computed \`${node.id}\` threw error during first run, this can be fatal. Solutions: 1. Set the \`initial\` option to silence this error`, "\n2. Or, use an `effect` if the return value is not being used", "\n\n", error ); } handleError(node, error); if (node._state === STATE_DIRTY) { cleanup(node); if (node._sources) removeSourceObservers(node, 0); } return; } currentObservers = prevObservers; currentObserversIndex = prevObserversIndex; node._state = STATE_CLEAN; } function notify(node, state) { if (node._state >= state) return; if (node._effect && node._state === STATE_CLEAN) { effects.push(node); if (!scheduledEffects) flushEffects(); } node._state = state; if (node._observers) { for (let i = 0; i < node._observers.length; i++) { notify(node._observers[i], STATE_CHECK); } } } function removeSourceObservers(node, index) { let source, swap; for (let i = index; i < node._sources.length; i++) { source = node._sources[i]; if (source._observers) { swap = source._observers.indexOf(node); source._observers[swap] = source._observers[source._observers.length - 1]; source._observers.pop(); } } } // src/signals.ts function signal(initialValue, options) { const node = createComputation(initialValue, null, options), signal2 = read.bind(node); signal2.node = node; signal2.set = write.bind(node); return signal2; } function isReadSignal(fn) { return isFunction$1(fn); } function computed(compute2, options) { { const node = createComputation( options?.initial, compute2, options ); const signal2 = read.bind(node); signal2.node = node; return signal2; } } function effect$1(effect2, options) { const signal2 = createComputation( null, function runEffect() { let effectResult = effect2(); isFunction$1(effectResult) && onDispose(effectResult); return null; }, { id: options?.id ?? "effect" } ); signal2._effect = true; update(signal2); { return function stopEffect() { dispose.call(signal2, true); }; } } function isWriteSignal(fn) { return isReadSignal(fn) && "set" in fn; } // src/std/unit.ts function noop(...args) { } function isNull(value) { return value === null; } function isUndefined(value) { return typeof value === "undefined"; } function isNil(value) { return isNull(value) || isUndefined(value); } function isObject(value) { return value?.constructor === Object; } function isNumber(value) { return typeof value === "number" && !Number.isNaN(value); } function isString(value) { return typeof value === "string"; } function isBoolean(value) { return typeof value === "boolean"; } function isFunction(value) { return typeof value === "function"; } function isArray(value) { return Array.isArray(value); } function createContext(provide) { return { id: Symbol(), provide }; } function provideContext(context, value, scope = getScope()) { if (!scope) { throw Error("[maverick] attempting to provide context outside `root` or `setup` function"); } const hasProvidedValue = !isUndefined(value); if (!hasProvidedValue && !context.provide) { throw Error("[maverick] context can not be provided without a value or `provide` function"); } setContext(context.id, hasProvidedValue ? value : context.provide?.(), scope); } function useContext(context) { const value = getContext(context.id); if (isUndefined(value)) { throw Error("[maverick] attempting to use context without providing first"); } return value; } function hasProvidedContext(context) { return !isUndefined(getContext(context.id)); } // src/runtime/store.ts var StoreFactory = class { constructor(record) { this.id = Symbol("STORE" ); this.record = record; this._descriptors = Object.getOwnPropertyDescriptors(record); } create() { const store = {}, state = new Proxy(store, { get: (_, prop) => store[prop]() }); for (const name of Object.keys(this.record)) { const getter = this._descriptors[name].get; store[name] = getter ? computed(getter.bind(state)) : signal(this.record[name]); } return store; } reset(record, filter) { for (const name of Object.keys(record)) { if (!this._descriptors[name].get && (!filter || filter(name))) { record[name].set(this.record[name]); } } } }; function useStore(store) { return useContext(store); } // src/runtime/reactivity.ts var effect = effect$1; function unwrapDeep(fn) { let value = fn; while (typeof value === "function") value = value(); return value; } // src/std/event.ts var EVENT = Event; var DOM_EVENT = Symbol("DOM_EVENT"); var _a$1; var DOMEvent = class extends EVENT { constructor(type, ...init) { super(type, init[0]); this[_a$1] = true; this.detail = init[0]?.detail; this.trigger = init[0]?.trigger; } /** * Walks up the event chain (following each `trigger`) and returns the origin event * that started the chain. */ get originEvent() { return getOriginEvent(this) ?? this; } /** * Walks up the event chain (following each `trigger`) and determines whether the initial * event was triggered by the end user (ie: check whether `isTrusted` on the `originEvent` `true`). */ get isOriginTrusted() { return getOriginEvent(this)?.isTrusted ?? false; } }; _a$1 = DOM_EVENT; function isDOMEvent(event) { return !!event?.[DOM_EVENT]; } function getOriginEvent(event) { let trigger = event.trigger; while (trigger && trigger.trigger) { trigger = trigger.trigger; } return trigger; } function appendTriggerEvent(event, trigger) { const origin = getOriginEvent(event) ?? event; if (origin === trigger) { throw Error( "[maverick] attemping to append event as a trigger on itself (cyclic)" ); } if (typeof origin.trigger !== "undefined") { console.warn( `[maverick] overwriting existing trigger event: \`${origin.trigger.type}\` -> \`${trigger?.type}\` `, "Event:\n", event, "Origin Event:\n", origin, "Trigger Event:\n", trigger ); } Object.defineProperty(origin, "trigger", { configurable: true, enumerable: true, get: () => trigger }); } var EventsTarget = class extends EventTarget { addEventListener(type, callback, options) { return super.addEventListener(type, callback, options); } removeEventListener(type, callback, options) { return super.removeEventListener(type, callback, options); } }; function listenEvent(target, type, handler, options) { target.addEventListener(type, handler, options); return onDispose(() => target.removeEventListener(type, handler, options)); } function isPointerEvent(event) { return !!event?.type.startsWith("pointer"); } function isTouchEvent(event) { return !!event?.type.startsWith("touch"); } function isMouseEvent(event) { return /^(click|mouse)/.test(event?.type ?? ""); } function isKeyboardEvent(event) { return !!event?.type.startsWith("key"); } function isKeyboardClick(event) { return isKeyboardEvent(event) && (event.key === "Enter" || event.key === " "); } // src/std/dom.ts function isDOMNode(node) { return node instanceof Node; } function isDOMElement(node) { return isDOMNode(node) && node.nodeType === 1; } function createFragment() { return document.createDocumentFragment(); } function createComment(data) { return document.createComment(data); } function setAttribute(host, name, value) { if (!value && value !== "" && value !== 0) { host.removeAttribute(name); } else { const attrValue = value + ""; if (host.getAttribute(name) !== attrValue) { host.setAttribute(name, attrValue); } } } function setStyle(host, property, value) { if (!value && value !== 0) { host.style.removeProperty(property); } else { host.style.setProperty(property, value + ""); } } function runAll(fns, arg) { for (const fn of fns) fn(arg); } // src/std/string.ts function camelToKebabCase(str) { return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase(); } function kebabToCamelCase(str) { return str.replace(/-./g, (x) => x[1].toUpperCase()); } function uppercaseFirstChar(str) { return str.charAt(0).toUpperCase() + str.slice(1); } // src/std/array.ts function flattenArray(array) { const flat = []; for (let i = 0; i < array.length; i++) { if (isArray(array[i])) { flat.push(...flattenArray(array[i])); } else if (array[i] || array[i] === 0) { flat.push(array[i]); } } return flat; } // src/element/controller.ts var ComponentController = class { /** * The custom element this component is attached to. This is safe to use server-side with the * limited API listed below. * * **Important:** Only specific DOM APIs are safe to call server-side. This includes: * * - Attributes: `getAttribute`, `setAttribute`, `removeAttribute`, and `hasAttribute` * - Classes: `classList` API * - Styles: `style` API * - Events (noop): `addEventListener`, `removeEventListener`, and `dispatchEvent` */ get el() { return this.instance._el; } /** * Reactive component properties. */ get $props() { return this.instance._props; } /** * Reactive component store. */ get $store() { return this.instance._store; } constructor(instance) { this.instance = instance; if (this.onAttach) instance._attachCallbacks.push(this.onAttach.bind(this)); if (this.onConnect) instance._connectCallbacks.push(this.onConnect.bind(this)); if (this.onDisconnect) instance._disconnectCallbacks.push(this.onDisconnect.bind(this)); if (this.onDestroy) instance._destroyCallbacks.push(this.onDestroy.bind(this)); } /** * This method can be used to specify attributes that should be set on the host element. Any * attributes that are assigned to a function will be considered a signal and updated accordingly. */ setAttributes(attributes) { if (this.instance._attrs) Object.assign(this.instance._attrs, attributes); } /** * This method can be used to specify styles that should set be set on the host element. Any * styles that are assigned to a function will be considered a signal and updated accordingly. */ setStyles(styles) { if (this.instance._styles) Object.assign(this.instance._styles, styles); } /** * This method is used to satisfy the CSS variables contract specified on the current * custom element definition. Other CSS variables can be set via the `setStyles` method. */ setCSSVars(vars) { this.setStyles(vars); } /** * Type-safe utility for creating component DOM events. */ createEvent(type, ...init) { return new DOMEvent(type, init[0]); } /** * Creates a `DOMEvent` and dispatches it from the host element. This method is typed to * match all component events. */ dispatch(type, ...init) { if (!this.el) return; const event = new DOMEvent(type, init[0]); this.el.dispatchEvent(event); } /** * Adds an event listener for the given `type` and returns a function which can be invoked to * remove the event listener. * * - The listener is removed if the current scope is disposed. * - This method is safe to use on the server (noop). */ listen(type, handler, options) { if (!this.el) return noop; return listenEvent(this.el, type, handler, options); } }; // src/element/component.ts var Component = class extends ComponentController { constructor(instance) { super(instance); if (this.render && !instance._innerHTML && !instance._renderer) { instance._renderer = this.render.bind(this); } } destroy() { this.instance._destroy(); } }; // src/runtime/dom/walker.ts var createMarkerWalker = (root4) => ( // @ts-expect-error - filter accepts `boolean` but not typed. document.createTreeWalker(root4, NodeFilter.SHOW_COMMENT, (node) => node.nodeValue === "$") ); // src/runtime/dom/render.ts var hydration = null; function runHydration(run, options) { const prev = hydration; hydration = { w: createMarkerWalker(options.target) }; const result = run(); hydration = prev; return result; } // src/runtime/dom/insert-lite.ts var CONNECTED = /* @__PURE__ */ Symbol("CONNECTED" ); var INSERT_MARKER_NODE = createComment("$$"); var END_MARKER = /* @__PURE__ */ Symbol("END_MARKER" ); var END_MARKER_NODE = /* @__PURE__ */ createComment("/$"); var ARRAY_END_MARKER_VALUE = "/[]"; function insertLite(parent, value, before) { let isSignal = isFunction(value); if (isSignal && value[$$CHILDREN]) { value = value(); isSignal = isFunction(value); } if (isSignal) { insertEffect(parent, value, before); } else if (!hydration && (value || value === 0)) { addChild( parent, isArray(value) ? resolveArray2(value) : isDOMNode(value) ? value : document.createTextNode(value + ""), before ); } } function addChild(parent, node, before) { if (!node) return; if (before) parent.insertBefore(node, before); else parent.appendChild(node); } function insertEffect(parent, value, before) { const marker = before && before.nodeType === 8 ? before : INSERT_MARKER_NODE.cloneNode(); if (marker !== before) addChild(parent, marker, before); effect(() => void insertExpression2(marker, unwrapDeep(value))); } function insertExpression2(start, value) { const end = start[END_MARKER]; if (isArray(value)) { if (hydration) { start[END_MARKER] = findArrayEndMarker(start); } else { if (end) removeOldNodes(start, end); const fragment = resolveArray2(value); if (!fragment) return; if (!end) fragment.appendChild(createEndMarker(start)); start.after(fragment); } } else if (isDOMNode(value)) { if (end) removeOldNodes(start, end); if (!hydration) start.after(value); if (!end) value.after(createEndMarker(start)); } else if (isString(value) || isNumber(value)) { if (start[CONNECTED]) { start.nextSibling.data = value + ""; return; } if (end) removeOldNodes(start, end); let text; if (!hydration) { text = document.createTextNode(value + ""); start.after(text); } else { text = start.nextSibling; } start[CONNECTED] = true; if (!end) text.after(createEndMarker(start)); } else if (end) { removeOldNodes(start, end); } } function createEndMarker(start) { return start[END_MARKER] = END_MARKER_NODE.cloneNode(); } function findArrayEndMarker(node) { while (node) { if (node.nodeType === 8 && node.nodeValue === ARRAY_END_MARKER_VALUE) return node; node = node.nextSibling; } } function removeOldNodes(start, end) { while (start.nextSibling !== end) start.nextSibling.remove(); start[CONNECTED] = false; } function resolveArray2(value) { const flattened = flattenArray(value); if (!flattened.length) return null; const fragment = createFragment(); for (let i = 0; i < flattened.length; i++) { const child = flattened[i]; if (isFunction(child)) { insertEffect(fragment, child); } else { fragment.append(child); } } return fragment; } function registerLiteCustomElement(Component2) { register(Component2, { insert: insertLite }); } var DOM_ELEMENT_REGISTRY = Symbol("MAVERICK_REGISTRY" ); function register(Component2, init) { const tagName = Component2.el.tagName; if (!window.customElements.get(tagName)) { if (!window[DOM_ELEMENT_REGISTRY]) window[DOM_ELEMENT_REGISTRY] = /* @__PURE__ */ new Map(); window[DOM_ELEMENT_REGISTRY].set(tagName, Component2); window.customElements.define(tagName, createHTMLElement(Component2, init)); } } // src/runtime/dom/internal.ts function $$_create_template(html) { const template = document.createElement("template"); template.innerHTML = html; return template.content; } function $$_setup_custom_element(host, props) { const Component2 = window[DOM_ELEMENT_REGISTRY]?.get(host.localName); if (!Component2) { throw Error( `[maverick] custom element not registered: ${host.localName}` ); } const component = createComponent(Component2, { props }); host.attachComponent(component); return component.instance._scope; } function $$_clone(fragment) { const clone = document.importNode(fragment, true); return clone.firstElementChild; } function $$_create_element(tagName) { return document.createElement(tagName); } var $$_insert_lite = insertLite; function $$_create_component(component, props = {}) { return peek(() => component(props)); } var $$CHILDREN = /* @__PURE__ */ Symbol("$$CHILDREN" ); function $$_ref(element, ref) { if (isArray(ref)) { ref.filter(isFunction).forEach((ref2) => ref2(element)); } else if (isFunction(ref)) { ref(element); } } var $$_attr = setAttribute; function $$_listen(target, type, handler, capture = false) { if (isFunction(handler)) { listenEvent(target, type, handler, { capture }); } } var $$_peek = peek; var $$_scoped = scoped; var $$_effect = effect; // src/element/internal.ts var CONNECT = /* @__PURE__ */ Symbol("CONNECT" ); var PROPS = /* @__PURE__ */ Symbol("PROPS" ); var METHODS = /* @__PURE__ */ Symbol("METHODS" ); function createComponent(Component2, init) { const instance = new ComponentInstance(Component2, init); return scoped(() => new Component2(instance), instance._scope); } var ComponentInstance = class { constructor(Component2, init = {}) { this._el = null; this._renderer = null; this._innerHTML = false; this._destroyed = false; this._attrs = {}; this._styles = {}; this._props = {}; // these props cause type issues - don't type them. this._state = null; this._store = null; this._attachCallbacks = []; this._connectCallbacks = []; this._disconnectCallbacks = []; this._destroyCallbacks = []; root((dispose) => { this._scope = getScope(); this._dispose = dispose; if (init.scope) init.scope.append(this._scope); const store = Component2.el.store; if (store) { this._store = store.create(); this._state = new Proxy(this._store, { get: (_, prop2) => this._store[prop2]() }); provideContext(store, this._store); } const props = Component2.el.props; if (props) { this._props = createInstanceProps(props); if (init.props) { for (const prop2 of Object.keys(init.props)) { if (prop2 in props) { const value = init.props[prop2]; if (isFunction$1(value)) { effect$1(() => void this._props[prop2].set(value())); } else { this._props[prop2].set(value); } } } } } if (init.props?.innerHTML) { this._innerHTML = true; } onDispose(this._destroy.bind(this)); }); } _destroy() { if (this._destroyed) return; this._destroyed = true; for (const destroy of this._destroyCallbacks) { scoped(() => destroy(this._el), this._scope); } this._el?.destroy(); this._attachCallbacks.length = 0; this._connectCallbacks.length = 0; this._disconnectCallbacks.length = 0; this._destroyCallbacks.length = 0; tick(); this._dispose(); this._el = null; this._renderer = null; } }; function createInstanceProps(defs) { const props = {}; for (const name of Object.keys(defs)) { const def = defs[name]; props[name] = signal(def.value, def); } return props; } // src/element/setup.ts async function setup(host) { const parent = findParent(host); const hostCtor = host.constructor, componentCtor = hostCtor._component; if (parent) { await customElements.whenDefined(parent.localName); parent[CONNECT] === true || await new Promise((res) => parent[CONNECT].push(res)); } if (host.isConnected) { if (parent?.keepAlive) host.keepAlive = true; host.attachComponent( createComponent(componentCtor, { scope: parent?.component?.instance._scope }) ); } } function resolvePropsFromAttrs(host) { const hostCtor = host.constructor, componentCtor = hostCtor._component, props = {}; if (!hostCtor._attrs) return props; for (const attr of host.attributes) { let propName = hostCtor._attrs.get(attr.name), convert = propName && componentCtor.el.props[propName].type?.from; if (convert) { let attrValue = host.getAttribute(attr.name); props[propName] = convert(attrValue); } } return props; } function findParent(host) { let hostCtor = host.constructor, componentCtor = hostCtor._component, node = host.parentNode, prefix = componentCtor.el.tagName.split("-", 1)[0] + "-"; while (node) { if (node.nodeType === 1 && node.localName.startsWith(prefix)) { return node; } node = node.parentNode; } return null; } // src/element/create-html-element.ts function createHTMLElement(Component2, init) { const _register = Component2.register; if (Component2.register) { const result = isArray(_register) ? _register : _register?.(); if (isArray(result)) for (const Component3 of result) register(Component3, init); } let attrs; if (Component2.el.props) { attrs = /* @__PURE__ */ new Map(); for (const propName of Object.keys(Component2.el.props)) { const def = Component2.el.props[propName]; const attr = def.attribute; if (attr !== false) { const attrName = attr ?? camelToKebabCase(propName); attrs.set(attrName, propName); } } } class MaverickElement extends HTMLCustomElement { static get _component() { return Component2; } } MaverickElement._init = init; MaverickElement._attrs = attrs; const proto = MaverickElement.prototype, componentProto = Component2.prototype; if (Component2.el.props) { for (const prop2 of Object.keys(Component2.el.props)) { Object.defineProperty(proto, prop2, { enumerable: true, configurable: true, get() { return !this.component ? Component2.el.props[prop2].value : this.component.instance._props[prop2](); }, set(value) { const fn = () => this.component.instance._props[prop2].set(value); if (!this.component) { this._queuedActions.delete(prop2); this._queuedActions.set(prop2, fn); return; } fn(); } }); } } if (componentProto[PROPS]) { for (const name of componentProto[PROPS]) { Object.defineProperty(proto, name, { enumerable: true, configurable: true, get() { return this.component ? this.component[name] : void 0; }, set(value) { if (!this.component) { this._queuedActions.delete(name); this._queuedActions.set(name, () => { this.component[name] = value; }); return; } this.component[name] = value; } }); } } if (componentProto[METHODS]) { for (const name of componentProto[METHODS]) { proto[name] = function(...args) { if (!this.component) this._throwAttachError([`el.${name}(...)`]); return this.component[name](...args); }; } } return MaverickElement; } var HTML_ELEMENT = HTMLElement; var _a; var HTMLCustomElement = class extends HTML_ELEMENT { constructor() { super(...arguments); this._connected = false; this._destroyed = false; this._component = null; this._connectScope = null; this._attachCallbacks = /* @__PURE__ */ new Set(); this._disconnectCallbacks = []; this._queuedActions = /* @__PURE__ */ new Map(); this.keepAlive = false; /** @internal */ this[_a] = []; this._pendingSetup = false; } get _delegate() { return this.hasAttribute("mk-d"); } get component() { return this._component; } get $store() { return this._component?.instance._store; } get state() { if (!this._component) { this._throwAttachError(["el.state.foo"]); } return this._component.instance._state; } static get observedAttributes() { return this._attrs ? Array.from(this._attrs.keys()) : []; } attributeChangedCallback(name, _, newValue) { const ctor = this.constructor; if (!this._component || !ctor._attrs) return; const propName = ctor._attrs.get(name); const from = ctor._component.el.props[propName]?.type?.from; if (from) this._component.instance._props[propName].set(from(newValue)); } connectedCallback() { const instance = this._component?.instance; if (!this._delegate && !instance) return this._setup(); if (!instance || !this.isConnected || this._connected) return; if (this._destroyed) { { throw Error( "[maverick] attempting to connect an element that has been destroyed" ); } } if (this.hasAttribute("keep-alive")) this.keepAlive = true; this._connected = true; if (instance._connectCallbacks.length) { scoped(() => { root((dispose) => { this._connectScope = getScope(); for (const connectCallback of instance._connectCallbacks) { scoped(() => { const disconnectCallback = connectCallback(this); if (isFunction$1(disconnectCallback)) { this._disconnectCallbacks.push(disconnectCallback); } }, this._connectScope); } this._disconnectCallbacks.push(dispose); }); }, instance._scope); } if (isArray(this[CONNECT])) { runAll(this[CONNECT], this); this[CONNECT] = true; } return; } disconnectedCallback() { const instance = this._component?.instance; if (!this._connected || this._destroyed) return; this._connected = false; for (const callback of this._disconnectCallbacks) { scoped(callback, this._connectScope); } if (instance?._disconnectCallbacks.length) { for (const callback of instance._disconnectCallbacks) { scoped(() => callback(this), instance._scope); } } this._connectScope = null; if (!this._delegate && !this.keepAlive) { requestAnimationFrame(() => { if (!this.isConnected) { instance?._destroy(); this._destroyed = true; } }); } } attachComponent(component) { const instance = component.instance, ctor = this.constructor, def = ctor._component.el, init = ctor._init; if (this._component) { console.warn(`[maverick] element \`${def.tagName}\` already has attached component`); } if (this._destroyed) { console.warn(`[maverick] attempted attaching to destroyed element \`${def.tagName}\``); } if (this._component || this._destroyed) return; scoped(() => { this._root = instance._renderer ? def.shadowRoot ? this.shadowRoot ?? this.attachShadow(isBoolean(def.shadowRoot) ? { mode: "open" } : def.shadowRoot) : resolveShadowRootElement(this) : null; if (def.css && !init?.adoptCSS) { console.warn( `[maverick] \`css\` was provided for \`${def.tagName}\` but element registration doesn't support adopting stylesheets. Resolve this by registering element with \`registerElement\` instead of lite or headless.` ); } if (!hydration && def.shadowRoot && def.css && init?.adoptCSS) { init.adoptCSS(this._root, def.css); } instance._el = this; this._component = component; const attrValues = resolvePropsFromAttrs(this); for (const name of Object.keys(attrValues)) { instance._props[name].set(attrValues[name]); } if (this._queuedActions?.size) { for (const action of this._queuedActions.values()) action(); } this._queuedActions = null; for (const callback of [...instance._attachCallbacks, ...this._attachCallbacks]) { scoped(() => callback(this), instance._scope); } instance._attachCallbacks.length = 0; this._attachCallbacks = null; const $attrs = instance._attrs, $styles = instance._styles; if ($attrs) { for (const name of Object.keys($attrs)) { if (isFunction$1($attrs[name])) { effect$1(() => setAttribute(this, name, $attrs[name]())); } else { setAttribute(this, name, $attrs[name]); } } } if ($styles) { for (const name of Object.keys($styles)) { if (isFunction$1($styles[name])) { effect$1(() => setStyle(this, name, $styles[name]())); } else { setStyle(this, name, $styles[name]); } } } this.dispatchEvent(new Event("attached")); if (this._root && init && instance._renderer) { const insert2 = () => init.insert(this._root, instance._renderer); if (this.hasAttribute("mk-h") && !ctor._component.el.nohydrate) { runHydration(insert2, { target: this._root }); } else { insert2(); } } this.connectedCallback(); }, instance._scope); } subscribe(callback) { if (!this._component) { this._throwAttachError(["el.subscribe(({ foo, bar }) => {", " // ...", "});"]); } if (!this._component?.instance._state) { const ctor = this.constructor; const tagName = ctor._component.el.tagName; throw Error(`[maverick] \`${tagName}\` element does not have a store to subscribe to`); } return scoped(() => { return effect$1(() => callback(this._component.instance._state)); }, this._component.instance._scope); } onAttach(callback) { if (this._component) { callback(this); return noop; } else { this._attachCallbacks.add(callback); return () => this._attachCallbacks?.delete(callback); } } onEventDispatch(callback) { const ctor = this.constructor; if (ctor._dispatchedEvents) for (const eventType of ctor._dispatchedEvents) callback(eventType); this._onEventDispatch = callback; } destroy() { this.disconnectedCallback(); this._component?.destroy(); this._component = null; this._destroyed = true; } dispatchEvent(event) { if (this._delegate) { const ctor = this.constructor; if (!ctor._dispatchedEvents) ctor._dispatchedEvents = /* @__PURE__ */ new Set(); if (!ctor._dispatchedEvents.has(event.type)) { this._onEventDispatch?.(event.type); ctor._dispatchedEvents.add(event.type); } } return untrack(() => super.dispatchEvent(event)); } async _setup() { if (this._pendingSetup) return; this._pendingSetup = true; await setup(this); this._pendingSetup = false; } _throwAttachError(code) { { const ctor = this.constructor; const tagName = ctor._component.el.tagName; throw Error( `[maverick] component instance has not attached yet, wait for event like so: const el = document.querySelector('${tagName}'); el.addEventListener('attached', () => { ` + (" " + code.join("\n ")) + ` }, { once: true }); ` ); } } }; _a = CONNECT; function resolveShadowRootElement(root4) { if (root4.firstChild && isDOMElement(root4.firstChild) && root4.firstChild.localName === "shadow-root") { return root4.firstChild; } else { const shadowRoot = $$_create_element("shadow-root"); root4.prepend(shadowRoot); return shadowRoot; } } // src/element/props.ts var PROP_DEF = Symbol("PROP_DEF" ); function defineProp(definition) { return { [PROP_DEF]: true, ...definition }; } // src/element/define.ts function defineElement(declaration) { if ("props" in declaration) { const props = declaration.props; for (const name of Object.keys(props)) { const def = props[name]?.[PROP_DEF] ? props[name] : { [PROP_DEF]: true, value: props[name] }; if (def.attribute !== false && !def.type) def.type = inferAttributeType(def.value); props[name] = def; } } return declaration; } var STRING = { from: (v) => v === null ? "" : v + "" }; var NUMBER = { from: (v) => v === null ? 0 : Number(v) }; var BOOLEAN = { from: (v) => v !== null, to: (v) => v ? "" : null }; var FUNCTION = { from: false, to: () => null }; var ARRAY = { from: (v) => v === null ? [] : JSON.parse(v), to: (v) => JSON.stringify(v) }; var OBJECT = { from: (v) => v === null ? {} : JSON.parse(v), to: (v) => JSON.stringify(v) }; function inferAttributeType(value) { if (value === null) return STRING; switch (typeof value) { case "undefined": return STRING; case "string": return STRING; case "boolean": return BOOLEAN; case "number": return NUMBER; case "function": return FUNCTION; case "object": return isArray(value) ? ARRAY : OBJECT; default: return STRING; } } // src/element/decorators.ts function prop(target, propertyKey, descriptor) { if (!target[PROPS]) target[PROPS] = /* @__PURE__ */ new Set(); target[PROPS].add(propertyKey); } function method(target, propertyKey, descriptor) { if (!target[METHODS]) target[METHODS] = /* @__PURE__ */ new Set(); target[METHODS].add(propertyKey); } // src/std/aria.ts function ariaBool(value) { return value ? "true" : "false"; } // src/std/disposal.ts function createDisposalBin() { const disposal = /* @__PURE__ */ new Set(); return { add(...callbacks) { for (const callback of callbacks) disposal.add(callback); }, empty() { for (const callback of disposal) callback(); disposal.clear(); } }; } function useDisposalBin() { const disposal = createDisposalBin(); onDispose(disposal.empty); return disposal; } // src/std/promise.ts function deferredPromise() { let resolve, reject; const promise = new Promise((res, rej) => { resolve = res; reject = rej; }); return { promise, resolve, reject }; } // src/std/timing.ts function waitTimeout(delay) { return new Promise((resolve) => setTimeout(resolve, delay)); } function animationFrameThrottle(func) { let id = -1, lastArgs; function throttle(...args) { lastArgs = args; if (id >= 0) return; id = window.requestAnimationFrame(() => { func.apply(this, lastArgs); id = -1; lastArgs = void 0; }); } return throttle; } var requestIdleCallback = "requestIdleCallback" in window ? window.requestIdleCallback : (cb) => window.requestAnimationFrame(cb); function waitIdlePeriod(callback, options) { return new Promise((resolve) => { requestIdleCallback((deadline) => { callback?.(deadline); resolve(); }, options); }); } var std = /*#__PURE__*/Object.freeze({ __proto__: null, DOMEvent: DOMEvent, EventsTarget: EventsTarget, animationFrameThrottle: animationFrameThrottle, appendTriggerEvent: appendTriggerEvent, ariaBool: ariaBool, camelToKebabCase: camelToKebabCase, createComment: createComment, createDisposalBin: createDisposalBin, createFragment: createFragment, deferredPromise: deferredPromise, flattenArray: flattenArray, getOriginEvent: getOriginEvent, isArray: isArray, isBoolean: isBoolean, isDOMElement: isDOMElement, isDOMEvent: isDOMEvent, isDOMNode: isDOMNode, isFunction: isFunction, isKeyboardClick: isKeyboardClick, isKeyboardEvent: isKeyboardEvent, isMouseEvent: isMouseEvent, isNil: isNil, isNull: isNull, isNumber: isNumber, isObject: isObject, isPointerEvent: isPointerEvent, isString: isString, isTouchEvent: isTouchEvent, isUndefined: isUndefined, kebabToCamelCase: kebabToCamelCase, listenEvent: listenEvent, noop: noop, runAll: runAll, setAttribute: setAttribute, setStyle: setStyle, unwrapDeep: unwrapDeep, uppercaseFirstChar: uppercaseFirstChar, useDisposalBin: useDisposalBin, waitIdlePeriod: waitIdlePeriod, waitTimeout: waitTimeout }); export { $$_clone as $, createContext as A, tick as B, Component as C, DOMEvent as D, EventsTarget as E, isArray as F, animationFrameThrottle as G, setStyle as H, ComponentController as I, isObject as J, defineProp as K, isKeyboardEvent as L, appendTriggerEvent as M, noop as N, provideContext as O, uppercaseFirstChar as P, prop as Q, method as R, StoreFactory as S, isWriteSignal as T, ariaBool as U, $$_create_component as V, createDisposalBin as W, isPointerEvent as X, isMouseEvent as Y, isTouchEvent as Z, kebabToCamelCase as _, isFunction as a, $$_insert_lite as a0, isDOMElement as a1, hasProvidedContext as a2, $$_listen as a3, $$_scoped as a4, $$_setup_custom_element as a5, useStore as a6, isDOMEvent as a7, $$_peek as a8, registerLiteCustomElement as a9, std as aa, isNull as b, isString as c, deferredPromise as d, $$_effect as e, $$_attr as f, $$_ref as g, $$_create_template as h, isUndefined as i, computed as j, isNumber as k, effect as l, listenEvent as m, isNil as n, onDispose as o, peek as p, camelToKebabCase as q, defineElement as r, setAttribute as s, signal as t, useDisposalBin as u, isKeyboardClick as v, waitTimeout as w, scoped as x, getScope as y, useContext as z };