diff --git a/component.d.ts b/component.d.ts deleted file mode 100644 index 5d840f4..0000000 --- a/component.d.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { GenericRenderer, RenderFunction } from './core'; -interface Renderer

extends GenericRenderer { - (this: Component

, host: Component

): unknown | void; - observedAttributes?: (keyof P)[]; -} -declare type Component

= HTMLElement & P; -declare type Constructor

= new (...args: unknown[]) => Component

; -interface Creator { -

(renderer: Renderer

): Constructor

; -

(renderer: Renderer

, options: Options

): Constructor

; -

(renderer: Renderer

, baseElement: Constructor<{}>, options: Omit, 'baseElement'>): Constructor

; -} -interface Options

{ - baseElement?: Constructor<{}>; - observedAttributes?: (keyof P)[]; - useShadowDOM?: boolean; - shadowRootInit?: ShadowRootInit; -} -declare function makeComponent(render: RenderFunction): Creator; -export { makeComponent, Component, Constructor as ComponentConstructor, Creator as ComponentCreator }; diff --git a/component.js b/component.js deleted file mode 100644 index 2884f55..0000000 --- a/component.js +++ /dev/null @@ -1,106 +0,0 @@ -import { BaseScheduler } from './scheduler'; -const toCamelCase = (val = '') => val.replace(/-+([a-z])?/g, (_, char) => char ? char.toUpperCase() : ''); -function makeComponent(render) { - class Scheduler extends BaseScheduler { - frag; - constructor(renderer, frag, host) { - super(renderer, (host || frag)); - this.frag = frag; - } - commit(result) { - render(result, this.frag); - } - } - function component(renderer, baseElementOrOptions, options) { - const BaseElement = (options || baseElementOrOptions || {}).baseElement || HTMLElement; - const { observedAttributes = [], useShadowDOM = true, shadowRootInit = {} } = options || baseElementOrOptions || {}; - class Element extends BaseElement { - _scheduler; - static get observedAttributes() { - return renderer.observedAttributes || observedAttributes || []; - } - constructor() { - super(); - if (useShadowDOM === false) { - this._scheduler = new Scheduler(renderer, this); - } - else { - this.attachShadow({ mode: 'open', ...shadowRootInit }); - this._scheduler = new Scheduler(renderer, this.shadowRoot, this); - } - } - connectedCallback() { - this._scheduler.update(); - } - disconnectedCallback() { - this._scheduler.teardown(); - } - attributeChangedCallback(name, oldValue, newValue) { - if (oldValue === newValue) { - return; - } - let val = newValue === '' ? true : newValue; - Reflect.set(this, toCamelCase(name), val); - } - } - ; - function reflectiveProp(initialValue) { - let value = initialValue; - let isSetup = false; - return Object.freeze({ - enumerable: true, - configurable: true, - get() { - return value; - }, - set(newValue) { - // Avoid scheduling update when prop value hasn't changed - if (isSetup && value === newValue) - return; - isSetup = true; - value = newValue; - if (this._scheduler) { - this._scheduler.update(); - } - } - }); - } - const proto = new Proxy(BaseElement.prototype, { - getPrototypeOf(target) { - return target; - }, - set(target, key, value, receiver) { - let desc; - if (key in target) { - desc = Object.getOwnPropertyDescriptor(target, key); - if (desc && desc.set) { - desc.set.call(receiver, value); - return true; - } - Reflect.set(target, key, value, receiver); - return true; - } - if (typeof key === 'symbol' || key[0] === '_') { - desc = { - enumerable: true, - configurable: true, - writable: true, - value - }; - } - else { - desc = reflectiveProp(value); - } - Object.defineProperty(receiver, key, desc); - if (desc.set) { - desc.set.call(receiver, value); - } - return true; - } - }); - Object.setPrototypeOf(Element.prototype, proto); - return Element; - } - return component; -} -export { makeComponent }; diff --git a/core.d.ts b/core.d.ts deleted file mode 100644 index 1c5cd2c..0000000 --- a/core.d.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { ComponentCreator } from './component'; -import { ContextCreator } from './create-context'; -import { ChildPart } from 'lit/html'; -declare type Component

= HTMLElement & P; -declare type ComponentOrVirtualComponent = T extends HTMLElement ? Component

: ChildPart; -declare type GenericRenderer = (this: ComponentOrVirtualComponent, ...args: any[]) => unknown | void; -declare type RenderFunction = (result: unknown, container: DocumentFragment | HTMLElement) => void; -interface Options { - render: RenderFunction; -} -declare function haunted({ render }: Options): { - component: ComponentCreator; - createContext: ContextCreator; -}; -export { haunted as default, Options, GenericRenderer, RenderFunction, ComponentOrVirtualComponent }; -export { useCallback } from './use-callback'; -export { useController } from './use-controller'; -export { useEffect } from './use-effect'; -export { useLayoutEffect } from './use-layout-effect'; -export { useState } from './use-state'; -export { useReducer } from './use-reducer'; -export { useMemo } from './use-memo'; -export { useContext } from './use-context'; -export { useRef } from './use-ref'; -export { hook, Hook } from './hook'; -export { BaseScheduler } from './scheduler'; -export { State } from './state'; diff --git a/core.js b/core.js deleted file mode 100644 index 088e24f..0000000 --- a/core.js +++ /dev/null @@ -1,20 +0,0 @@ -import { makeComponent } from './component'; -import { makeContext } from './create-context'; -function haunted({ render }) { - const component = makeComponent(render); - const createContext = makeContext(component); - return { component, createContext }; -} -export { haunted as default }; -export { useCallback } from './use-callback'; -export { useController } from './use-controller'; -export { useEffect } from './use-effect'; -export { useLayoutEffect } from './use-layout-effect'; -export { useState } from './use-state'; -export { useReducer } from './use-reducer'; -export { useMemo } from './use-memo'; -export { useContext } from './use-context'; -export { useRef } from './use-ref'; -export { hook, Hook } from './hook'; -export { BaseScheduler } from './scheduler'; -export { State } from './state'; diff --git a/create-context.d.ts b/create-context.d.ts deleted file mode 100644 index a37dc6b..0000000 --- a/create-context.d.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { ComponentConstructor, ComponentCreator } from './component'; -interface ConsumerProps { - render: (value: T) => unknown; -} -interface Creator { - (defaultValue: T): Context; -} -interface Context { - Provider: ComponentConstructor<{}>; - Consumer: ComponentConstructor>; - defaultValue: T; -} -interface ContextDetail { - Context: Context; - callback: (value: T) => void; - value: T; - unsubscribe?: (this: Context) => void; -} -declare function makeContext(component: ComponentCreator): Creator; -export { makeContext, Creator as ContextCreator, Context, ContextDetail }; diff --git a/create-context.js b/create-context.js deleted file mode 100644 index 47c8cde..0000000 --- a/create-context.js +++ /dev/null @@ -1,48 +0,0 @@ -import { contextEvent } from './symbols'; -import { useContext } from './use-context'; -function makeContext(component) { - return (defaultValue) => { - const Context = { - Provider: class extends HTMLElement { - listeners; - _value; - constructor() { - super(); - this.listeners = new Set(); - this.addEventListener(contextEvent, this); - } - disconnectedCallback() { - this.removeEventListener(contextEvent, this); - } - handleEvent(event) { - const { detail } = event; - if (detail.Context === Context) { - detail.value = this.value; - detail.unsubscribe = this.unsubscribe.bind(this, detail.callback); - this.listeners.add(detail.callback); - event.stopPropagation(); - } - } - unsubscribe(callback) { - this.listeners.delete(callback); - } - set value(value) { - this._value = value; - for (let callback of this.listeners) { - callback(value); - } - } - get value() { - return this._value; - } - }, - Consumer: component(function ({ render }) { - const context = useContext(Context); - return render(context); - }), - defaultValue, - }; - return Context; - }; -} -export { makeContext }; diff --git a/create-effect.d.ts b/create-effect.d.ts deleted file mode 100644 index b9c44a9..0000000 --- a/create-effect.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { State, Callable } from './state'; -declare type Effect = (this: State) => void | VoidFunction | Promise; -declare function createEffect(setEffects: (state: State, cb: Callable) => void): (callback: Effect, values?: unknown[] | undefined) => void; -export { createEffect }; diff --git a/create-effect.js b/create-effect.js deleted file mode 100644 index df474ed..0000000 --- a/create-effect.js +++ /dev/null @@ -1,36 +0,0 @@ -import { Hook, hook } from './hook'; -function createEffect(setEffects) { - return hook(class extends Hook { - callback; - lastValues; - values; - _teardown; - constructor(id, state, ignored1, ignored2) { - super(id, state); - setEffects(state, this); - } - update(callback, values) { - this.callback = callback; - this.values = values; - } - call() { - if (!this.values || this.hasChanged()) { - this.run(); - } - this.lastValues = this.values; - } - run() { - this.teardown(); - this._teardown = this.callback.call(this.state); - } - teardown() { - if (typeof this._teardown === 'function') { - this._teardown(); - } - } - hasChanged() { - return !this.lastValues || this.values.some((value, i) => this.lastValues[i] !== value); - } - }); -} -export { createEffect }; diff --git a/haunted.d.ts b/haunted.d.ts deleted file mode 100644 index 9154692..0000000 --- a/haunted.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { html, render, component, createContext, virtual } from './lit-haunted'; -export * from './core'; -export { default } from './core'; diff --git a/hook.d.ts b/hook.d.ts deleted file mode 100644 index f4193a5..0000000 --- a/hook.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { State } from './state'; -declare abstract class Hook

{ - id: number; - state: State; - constructor(id: number, state: State); - abstract update(...args: P): R; - teardown?(): void; -} -interface CustomHook

{ - new (id: number, state: State, ...args: P): Hook; -} -declare function hook

(Hook: CustomHook): (...args: P) => R; -export { hook, Hook }; diff --git a/hook.js b/hook.js deleted file mode 100644 index f884ebb..0000000 --- a/hook.js +++ /dev/null @@ -1,24 +0,0 @@ -import { current, notify } from './interface'; -import { hookSymbol } from './symbols'; -class Hook { - id; - state; - constructor(id, state) { - this.id = id; - this.state = state; - } -} -function use(Hook, ...args) { - let id = notify(); - let hooks = current[hookSymbol]; - let hook = hooks.get(id); - if (!hook) { - hook = new Hook(id, current, ...args); - hooks.set(id, hook); - } - return hook.update(...args); -} -function hook(Hook) { - return use.bind(null, Hook); -} -export { hook, Hook }; diff --git a/interface.d.ts b/interface.d.ts deleted file mode 100644 index fc81263..0000000 --- a/interface.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { State } from './state'; -declare let current: State | null; -declare function setCurrent(state: State): void; -declare function clear(): void; -declare function notify(): number; -export { clear, current, setCurrent, notify }; diff --git a/interface.js b/interface.js deleted file mode 100644 index 0d6b75f..0000000 --- a/interface.js +++ /dev/null @@ -1,13 +0,0 @@ -let current; -let currentId = 0; -function setCurrent(state) { - current = state; -} -function clear() { - current = null; - currentId = 0; -} -function notify() { - return currentId++; -} -export { clear, current, setCurrent, notify }; diff --git a/lit-haunted.d.ts b/lit-haunted.d.ts deleted file mode 100644 index cd1fe1f..0000000 --- a/lit-haunted.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { html, render } from 'lit'; -declare const component: import("./component").ComponentCreator, createContext: import("./create-context").ContextCreator; -declare const virtual: any; -export { component, createContext, virtual, html, render }; diff --git a/lit-haunted.js b/lit-haunted.js deleted file mode 100644 index ff3729a..0000000 --- a/lit-haunted.js +++ /dev/null @@ -1,6 +0,0 @@ -import { html, render } from 'lit'; -import haunted from './core'; -import { makeVirtual } from './virtual'; -const { component, createContext } = haunted({ render }); -const virtual = makeVirtual(); -export { component, createContext, virtual, html, render }; diff --git a/scheduler.d.ts b/scheduler.d.ts deleted file mode 100644 index 8317087..0000000 --- a/scheduler.d.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { State } from './state'; -import { commitSymbol, phaseSymbol, updateSymbol, effectsSymbol, Phase, EffectsSymbols } from './symbols'; -import { GenericRenderer, ComponentOrVirtualComponent } from './core'; -import { ChildPart } from 'lit/html'; -declare abstract class BaseScheduler

, H extends ComponentOrVirtualComponent> { - renderer: R; - host: H; - state: State; - [phaseSymbol]: Phase | null; - _updateQueued: boolean; - constructor(renderer: R, host: H); - update(): void; - handlePhase(phase: typeof commitSymbol, arg: unknown): void; - handlePhase(phase: typeof updateSymbol): unknown; - handlePhase(phase: typeof effectsSymbol): void; - render(): unknown; - runEffects(phase: EffectsSymbols): void; - abstract commit(result: unknown): void; - teardown(): void; -} -export { BaseScheduler }; diff --git a/scheduler.js b/scheduler.js deleted file mode 100644 index 253dadb..0000000 --- a/scheduler.js +++ /dev/null @@ -1,73 +0,0 @@ -import { State } from './state'; -import { commitSymbol, phaseSymbol, updateSymbol, effectsSymbol, layoutEffectsSymbol } from './symbols'; -const defer = Promise.resolve().then.bind(Promise.resolve()); -function runner() { - let tasks = []; - let id; - function runTasks() { - id = null; - let t = tasks; - tasks = []; - for (var i = 0, len = t.length; i < len; i++) { - t[i](); - } - } - return function (task) { - tasks.push(task); - if (id == null) { - id = defer(runTasks); - } - }; -} -const read = runner(); -const write = runner(); -class BaseScheduler { - renderer; - host; - state; - [phaseSymbol]; - _updateQueued; - constructor(renderer, host) { - this.renderer = renderer; - this.host = host; - this.state = new State(this.update.bind(this), host); - this[phaseSymbol] = null; - this._updateQueued = false; - } - update() { - if (this._updateQueued) - return; - read(() => { - let result = this.handlePhase(updateSymbol); - write(() => { - this.handlePhase(commitSymbol, result); - write(() => { - this.handlePhase(effectsSymbol); - }); - }); - this._updateQueued = false; - }); - this._updateQueued = true; - } - handlePhase(phase, arg) { - this[phaseSymbol] = phase; - switch (phase) { - case commitSymbol: - this.commit(arg); - this.runEffects(layoutEffectsSymbol); - return; - case updateSymbol: return this.render(); - case effectsSymbol: return this.runEffects(effectsSymbol); - } - } - render() { - return this.state.run(() => this.renderer.call(this.host, this.host)); - } - runEffects(phase) { - this.state._runEffects(phase); - } - teardown() { - this.state.teardown(); - } -} -export { BaseScheduler }; diff --git a/state.d.ts b/state.d.ts deleted file mode 100644 index cbc0abf..0000000 --- a/state.d.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Hook } from './hook'; -import { hookSymbol, effectsSymbol, layoutEffectsSymbol, EffectsSymbols } from './symbols'; -interface Callable { - call: (state: State) => void; -} -declare class State { - update: VoidFunction; - host: H; - virtual?: boolean; - [hookSymbol]: Map; - [effectsSymbol]: Callable[]; - [layoutEffectsSymbol]: Callable[]; - constructor(update: VoidFunction, host: H); - run(cb: () => T): T; - _runEffects(phase: EffectsSymbols): void; - runEffects(): void; - runLayoutEffects(): void; - teardown(): void; -} -export { State, Callable }; diff --git a/state.js b/state.js deleted file mode 100644 index f722eda..0000000 --- a/state.js +++ /dev/null @@ -1,46 +0,0 @@ -import { setCurrent, clear } from './interface'; -import { hookSymbol, effectsSymbol, layoutEffectsSymbol } from './symbols'; -class State { - update; - host; - virtual; - [hookSymbol]; - [effectsSymbol]; - [layoutEffectsSymbol]; - constructor(update, host) { - this.update = update; - this.host = host; - this[hookSymbol] = new Map(); - this[effectsSymbol] = []; - this[layoutEffectsSymbol] = []; - } - run(cb) { - setCurrent(this); - let res = cb(); - clear(); - return res; - } - _runEffects(phase) { - let effects = this[phase]; - setCurrent(this); - for (let effect of effects) { - effect.call(this); - } - clear(); - } - runEffects() { - this._runEffects(effectsSymbol); - } - runLayoutEffects() { - this._runEffects(layoutEffectsSymbol); - } - teardown() { - let hooks = this[hookSymbol]; - hooks.forEach(hook => { - if (typeof hook.teardown === 'function') { - hook.teardown(); - } - }); - } -} -export { State }; diff --git a/symbols.d.ts b/symbols.d.ts deleted file mode 100644 index f403868..0000000 --- a/symbols.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -declare const phaseSymbol: unique symbol; -declare const hookSymbol: unique symbol; -declare const updateSymbol: unique symbol; -declare const commitSymbol: unique symbol; -declare const effectsSymbol: unique symbol; -declare const layoutEffectsSymbol: unique symbol; -declare type EffectsSymbols = typeof effectsSymbol | typeof layoutEffectsSymbol; -declare type Phase = typeof updateSymbol | typeof commitSymbol | typeof effectsSymbol; -declare const contextEvent = "haunted.context"; -export { phaseSymbol, hookSymbol, updateSymbol, commitSymbol, effectsSymbol, layoutEffectsSymbol, contextEvent, Phase, EffectsSymbols, }; diff --git a/symbols.js b/symbols.js deleted file mode 100644 index dc8b14f..0000000 --- a/symbols.js +++ /dev/null @@ -1,8 +0,0 @@ -const phaseSymbol = Symbol('haunted.phase'); -const hookSymbol = Symbol('haunted.hook'); -const updateSymbol = Symbol('haunted.update'); -const commitSymbol = Symbol('haunted.commit'); -const effectsSymbol = Symbol('haunted.effects'); -const layoutEffectsSymbol = Symbol('haunted.layoutEffects'); -const contextEvent = 'haunted.context'; -export { phaseSymbol, hookSymbol, updateSymbol, commitSymbol, effectsSymbol, layoutEffectsSymbol, contextEvent, }; diff --git a/use-callback.d.ts b/use-callback.d.ts deleted file mode 100644 index ae557f9..0000000 --- a/use-callback.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * @function - * @template {Function} T - * @param {T} fn - callback to memoize - * @param {unknown[]} inputs - dependencies to callback memoization - * @return {T} - */ -declare const useCallback: (fn: T, inputs: unknown[]) => T; -export { useCallback }; diff --git a/use-callback.js b/use-callback.js deleted file mode 100644 index b33d8ad..0000000 --- a/use-callback.js +++ /dev/null @@ -1,10 +0,0 @@ -import { useMemo } from './use-memo'; -/** - * @function - * @template {Function} T - * @param {T} fn - callback to memoize - * @param {unknown[]} inputs - dependencies to callback memoization - * @return {T} - */ -const useCallback = (fn, inputs) => useMemo(() => fn, inputs); -export { useCallback }; diff --git a/use-context.d.ts b/use-context.d.ts deleted file mode 100644 index c298e20..0000000 --- a/use-context.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Context } from './create-context'; -/** - * @function - * @template T - * @param {Context} context - * @return {T} - */ -declare const useContext: (Context: Context) => T; -export { useContext }; diff --git a/use-context.js b/use-context.js deleted file mode 100644 index 2c56b07..0000000 --- a/use-context.js +++ /dev/null @@ -1,63 +0,0 @@ -import { hook, Hook } from './hook'; -import { contextEvent } from './symbols'; -import { setEffects } from './use-effect'; -/** - * @function - * @template T - * @param {Context} context - * @return {T} - */ -const useContext = hook(class extends Hook { - Context; - value; - _ranEffect; - _unsubscribe; - constructor(id, state, _) { - super(id, state); - this._updater = this._updater.bind(this); - this._ranEffect = false; - this._unsubscribe = null; - setEffects(state, this); - } - update(Context) { - if (this.state.virtual) { - throw new Error('can\'t be used with virtual components'); - } - if (this.Context !== Context) { - this._subscribe(Context); - this.Context = Context; - } - return this.value; - } - call() { - if (!this._ranEffect) { - this._ranEffect = true; - if (this._unsubscribe) - this._unsubscribe(); - this._subscribe(this.Context); - this.state.update(); - } - } - _updater(value) { - this.value = value; - this.state.update(); - } - _subscribe(Context) { - const detail = { Context, callback: this._updater }; - this.state.host.dispatchEvent(new CustomEvent(contextEvent, { - detail, - bubbles: true, - cancelable: true, - composed: true, // to pass ShadowDOM boundaries - })); - const { unsubscribe = null, value } = detail; - this.value = unsubscribe ? value : Context.defaultValue; - this._unsubscribe = unsubscribe; - } - teardown() { - if (this._unsubscribe) { - this._unsubscribe(); - } - } -}); -export { useContext }; diff --git a/use-controller.d.ts b/use-controller.d.ts deleted file mode 100644 index 08a3fec..0000000 --- a/use-controller.d.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * @license - * Portions Copyright 2021 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -import { ReactiveController, ReactiveControllerHost } from '@lit/reactive-element'; -/** - * Creates and stores a stateful ReactiveController instance and provides it - * with a ReactiveControllerHost that drives the controller lifecycle. - * - * Use this hook to convert a ReactiveController into a Haunted hook. - * - * @param {(host: ReactiveControllerHost) => C} createController A function that creates a controller instance. - * This function is given a HauntedControllerHost to pass to the controller. The - * create function is only called once per component. - * @return {ReactiveController} the controller instance - */ -export declare function useController(createController: (host: ReactiveControllerHost) => C): C; diff --git a/use-controller.js b/use-controller.js deleted file mode 100644 index e93f1e6..0000000 --- a/use-controller.js +++ /dev/null @@ -1,94 +0,0 @@ -/** - * @license - * Portions Copyright 2021 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -import { useLayoutEffect } from './use-layout-effect'; -import { useState } from './use-state'; -const microtask = Promise.resolve(); -/** - * An implementation of ReactiveControllerHost that is driven by Haunted hooks - * and `useController()`. - */ -class HauntedControllerHost { - count; - kick; - _controllers = []; - _updatePending = true; - _updateCompletePromise; - _resolveUpdate; - constructor(count, kick) { - this.count = count; - this.kick = kick; - this._updateCompletePromise = new Promise(res => { - this._resolveUpdate = res; - }); - } - addController(controller) { - this._controllers.push(controller); - } - removeController(controller) { - // Note, if the indexOf is -1, the >>> will flip the sign which makes the - // splice do nothing. - this._controllers && this._controllers.splice(this._controllers.indexOf(controller) >>> 0, 1); - } - requestUpdate() { - if (!this._updatePending) { - this._updatePending = true; - microtask.then(() => this.kick(this.count + 1)); - } - } - get updateComplete() { - return this._updateCompletePromise; - } - connected() { - this._controllers.forEach(c => c.hostConnected && c.hostConnected()); - } - disconnected() { - this._controllers.forEach(c => c.hostDisconnected && c.hostDisconnected()); - } - update() { - this._controllers.forEach(c => c.hostUpdate && c.hostUpdate()); - } - updated() { - this._updatePending = false; - const resolve = this._resolveUpdate; - // Create a new updateComplete Promise for the next update, - // before resolving the current one. - this._updateCompletePromise = new Promise(res => { - this._resolveUpdate = res; - }); - this._controllers.forEach(c => c.hostUpdated && c.hostUpdated()); - resolve(this._updatePending); - } -} -/** - * Creates and stores a stateful ReactiveController instance and provides it - * with a ReactiveControllerHost that drives the controller lifecycle. - * - * Use this hook to convert a ReactiveController into a Haunted hook. - * - * @param {(host: ReactiveControllerHost) => C} createController A function that creates a controller instance. - * This function is given a HauntedControllerHost to pass to the controller. The - * create function is only called once per component. - * @return {ReactiveController} the controller instance - */ -export function useController(createController) { - const [count, kick] = useState(0); - const [host] = useState(() => { - const host = new HauntedControllerHost(count, kick); - const controller = createController(host); - host.primaryController = controller; - host.connected(); - return host; - }); - // We use useLayoutEffect because we need updated() called synchronously - // after rendering. - useLayoutEffect(() => host.updated()); - // Returning a cleanup function simulates hostDisconnected timing. An empty - // deps array tells Haunted to only call this once: on mount with the cleanup - // called on unmount. - useLayoutEffect(() => () => host.disconnected(), []); - host.update(); - return host.primaryController; -} diff --git a/use-effect.d.ts b/use-effect.d.ts deleted file mode 100644 index 27e8877..0000000 --- a/use-effect.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { State, Callable } from './state'; -declare function setEffects(state: State, cb: Callable): void; -/** - * @function - * @param {() => void} effect - callback function that runs each time dependencies change - * @param {unknown[]} [dependencies] - list of dependencies to the effect - * @return {void} - */ -declare const useEffect: (callback: (this: State) => void | Promise | VoidFunction, values?: unknown[] | undefined) => void; -export { setEffects, useEffect }; diff --git a/use-effect.js b/use-effect.js deleted file mode 100644 index 535929b..0000000 --- a/use-effect.js +++ /dev/null @@ -1,13 +0,0 @@ -import { effectsSymbol } from './symbols'; -import { createEffect } from './create-effect'; -function setEffects(state, cb) { - state[effectsSymbol].push(cb); -} -/** - * @function - * @param {() => void} effect - callback function that runs each time dependencies change - * @param {unknown[]} [dependencies] - list of dependencies to the effect - * @return {void} - */ -const useEffect = createEffect(setEffects); -export { setEffects, useEffect }; diff --git a/use-layout-effect.d.ts b/use-layout-effect.d.ts deleted file mode 100644 index 4c313f2..0000000 --- a/use-layout-effect.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { State } from './state'; -/** - * @function - * @param {Effect} callback effecting callback - * @param {unknown[]} [values] dependencies to the effect - * @return {void} - */ -declare const useLayoutEffect: (callback: (this: State) => void | Promise | VoidFunction, values?: unknown[] | undefined) => void; -export { useLayoutEffect }; diff --git a/use-layout-effect.js b/use-layout-effect.js deleted file mode 100644 index 5c45cb4..0000000 --- a/use-layout-effect.js +++ /dev/null @@ -1,13 +0,0 @@ -import { layoutEffectsSymbol } from './symbols'; -import { createEffect } from './create-effect'; -function setLayoutEffects(state, cb) { - state[layoutEffectsSymbol].push(cb); -} -/** - * @function - * @param {Effect} callback effecting callback - * @param {unknown[]} [values] dependencies to the effect - * @return {void} - */ -const useLayoutEffect = createEffect(setLayoutEffects); -export { useLayoutEffect }; diff --git a/use-memo.d.ts b/use-memo.d.ts deleted file mode 100644 index 14e9dc0..0000000 --- a/use-memo.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * @function - * @template T - * @param {() => T} fn function to memoize - * @param {unknown[]} values dependencies to the memoized computation - * @return {T} The next computed value - */ -declare const useMemo: (fn: () => T, values: unknown[]) => T; -export { useMemo }; diff --git a/use-memo.js b/use-memo.js deleted file mode 100644 index 890ab31..0000000 --- a/use-memo.js +++ /dev/null @@ -1,28 +0,0 @@ -import { hook, Hook } from './hook'; -/** - * @function - * @template T - * @param {() => T} fn function to memoize - * @param {unknown[]} values dependencies to the memoized computation - * @return {T} The next computed value - */ -const useMemo = hook(class extends Hook { - value; - values; - constructor(id, state, fn, values) { - super(id, state); - this.value = fn(); - this.values = values; - } - update(fn, values) { - if (this.hasChanged(values)) { - this.values = values; - this.value = fn(); - } - return this.value; - } - hasChanged(values = []) { - return values.some((value, i) => this.values[i] !== value); - } -}); -export { useMemo }; diff --git a/use-reducer.d.ts b/use-reducer.d.ts deleted file mode 100644 index ef18ae9..0000000 --- a/use-reducer.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -declare type Reducer = (state: S, action: A) => S; -/** - * Given a reducer function, initial state, and optional state initializer function, returns a tuple of state and dispatch function. - * @function - * @template S State - * @template I Initial State - * @template A Action - * @param {Reducer} reducer - reducer function to compute the next state given the previous state and the action - * @param {I} initialState - the initial state of the reducer - * @param {(init: I) => S} [init=undefined] - Optional initializer function, called on initialState if provided - * @return {readonly [S, (action: A) => void]} - */ -declare const useReducer: (_: Reducer, initialState: I, init?: ((_: I) => S) | undefined) => readonly [S, (action: A) => void]; -export { useReducer }; diff --git a/use-reducer.js b/use-reducer.js deleted file mode 100644 index 9981a2b..0000000 --- a/use-reducer.js +++ /dev/null @@ -1,30 +0,0 @@ -import { hook, Hook } from './hook'; -/** - * Given a reducer function, initial state, and optional state initializer function, returns a tuple of state and dispatch function. - * @function - * @template S State - * @template I Initial State - * @template A Action - * @param {Reducer} reducer - reducer function to compute the next state given the previous state and the action - * @param {I} initialState - the initial state of the reducer - * @param {(init: I) => S} [init=undefined] - Optional initializer function, called on initialState if provided - * @return {readonly [S, (action: A) => void]} - */ -const useReducer = hook(class extends Hook { - reducer; - currentState; - constructor(id, state, _, initialState, init) { - super(id, state); - this.dispatch = this.dispatch.bind(this); - this.currentState = init !== undefined ? init(initialState) : initialState; - } - update(reducer) { - this.reducer = reducer; - return [this.currentState, this.dispatch]; - } - dispatch(action) { - this.currentState = this.reducer(this.currentState, action); - this.state.update(); - } -}); -export { useReducer }; diff --git a/use-ref.d.ts b/use-ref.d.ts deleted file mode 100644 index 2df8ec6..0000000 --- a/use-ref.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * @function - * @template T - * @param {T} initialValue - * @return {{ current: T }} Ref - */ -declare const useRef: (initialValue: T) => { - current: T; -}; -export { useRef }; diff --git a/use-ref.js b/use-ref.js deleted file mode 100644 index 252227d..0000000 --- a/use-ref.js +++ /dev/null @@ -1,11 +0,0 @@ -import { useMemo } from './use-memo'; -/** - * @function - * @template T - * @param {T} initialValue - * @return {{ current: T }} Ref - */ -const useRef = (initialValue) => useMemo(() => ({ - current: initialValue -}), []); -export { useRef }; diff --git a/use-state.d.ts b/use-state.d.ts deleted file mode 100644 index 6ce904e..0000000 --- a/use-state.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -declare type NewState = T | ((previousState?: T) => T); -declare type StateUpdater = (value: NewState) => void; -/** - * @function - * @template {*} T - * @param {T} [initialState] - Optional initial state - * @return {readonly [state: T, updaterFn: StateUpdater]} stateTuple - Tuple of current state and state updater function - */ -declare const useState: (initialValue?: T | undefined) => readonly [T extends (...args: any[]) => infer R ? R : T, StateUpdater infer S ? S : T>]; -export { useState }; diff --git a/use-state.js b/use-state.js deleted file mode 100644 index 99a5fff..0000000 --- a/use-state.js +++ /dev/null @@ -1,35 +0,0 @@ -import { hook, Hook } from './hook'; -/** - * @function - * @template {*} T - * @param {T} [initialState] - Optional initial state - * @return {readonly [state: T, updaterFn: StateUpdater]} stateTuple - Tuple of current state and state updater function - */ -const useState = hook(class extends Hook { - args; - constructor(id, state, initialValue) { - super(id, state); - this.updater = this.updater.bind(this); - if (typeof initialValue === 'function') { - initialValue = initialValue(); - } - this.makeArgs(initialValue); - } - update() { - return this.args; - } - updater(value) { - if (typeof value === 'function') { - const updaterFn = value; - const [previousValue] = this.args; - value = updaterFn(previousValue); - } - this.makeArgs(value); - this.state.update(); - } - makeArgs(value) { - this.args = Object.freeze([value, this.updater]); - } -}); -; -export { useState }; diff --git a/virtual.d.ts b/virtual.d.ts deleted file mode 100644 index 9196e60..0000000 --- a/virtual.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { ChildPart } from 'lit/directive.js'; -import { GenericRenderer } from './core'; -interface Renderer extends GenericRenderer { - (this: ChildPart, ...args: unknown[]): unknown | void; -} -declare function makeVirtual(): any; -export { makeVirtual, Renderer as VirtualRenderer }; diff --git a/virtual.js b/virtual.js deleted file mode 100644 index b770638..0000000 --- a/virtual.js +++ /dev/null @@ -1,79 +0,0 @@ -import { directive } from 'lit/directive.js'; -import { noChange } from 'lit'; -import { AsyncDirective } from 'lit/async-directive.js'; -import { BaseScheduler } from './scheduler'; -const includes = Array.prototype.includes; -const partToScheduler = new WeakMap(); -const schedulerToPart = new WeakMap(); -class Scheduler extends BaseScheduler { - args; - setValue; - constructor(renderer, part, setValue) { - super(renderer, part); - this.state.virtual = true; - this.setValue = setValue; - } - render() { - return this.state.run(() => this.renderer.apply(this.host, this.args)); - } - commit(result) { - this.setValue(result); - } - teardown() { - super.teardown(); - let part = schedulerToPart.get(this); - partToScheduler.delete(part); - } -} -function makeVirtual() { - function virtual(renderer) { - class VirtualDirective extends AsyncDirective { - cont; - constructor(partInfo) { - super(partInfo); - this.cont = undefined; - } - update(part, args) { - this.cont = partToScheduler.get(part); - if (!this.cont || this.cont.renderer !== renderer) { - this.cont = new Scheduler(renderer, part, (r) => { this.setValue(r); }); - partToScheduler.set(part, this.cont); - schedulerToPart.set(this.cont, part); - teardownOnRemove(this.cont, part); - } - this.cont.args = args; - this.cont.update(); - return this.render(args); - } - render(args) { - return noChange; - } - } - return directive(VirtualDirective); - } - return virtual; -} -function teardownOnRemove(cont, part, node = part.startNode) { - let frag = node.parentNode; - let mo = new MutationObserver(mutations => { - for (let mutation of mutations) { - if (includes.call(mutation.removedNodes, node)) { - mo.disconnect(); - if (node.parentNode instanceof ShadowRoot) { - teardownOnRemove(cont, part); - } - else { - cont.teardown(); - } - break; - } - else if (includes.call(mutation.addedNodes, node.nextSibling)) { - mo.disconnect(); - teardownOnRemove(cont, part, node.nextSibling || undefined); - break; - } - } - }); - mo.observe(frag, { childList: true }); -} -export { makeVirtual };