@llui/dom 0.0.20 → 0.0.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -14
- package/dist/addressed.d.ts +1 -1
- package/dist/addressed.d.ts.map +1 -1
- package/dist/addressed.js.map +1 -1
- package/dist/binding.d.ts +2 -2
- package/dist/binding.d.ts.map +1 -1
- package/dist/binding.js +2 -2
- package/dist/binding.js.map +1 -1
- package/dist/devtools.d.ts +3 -3
- package/dist/devtools.d.ts.map +1 -1
- package/dist/devtools.js +14 -12
- package/dist/devtools.js.map +1 -1
- package/dist/el-split.js +3 -3
- package/dist/el-split.js.map +1 -1
- package/dist/el-template.js +3 -3
- package/dist/el-template.js.map +1 -1
- package/dist/elements.js +1 -1
- package/dist/elements.js.map +1 -1
- package/dist/hmr.d.ts +1 -1
- package/dist/hmr.d.ts.map +1 -1
- package/dist/hmr.js +6 -6
- package/dist/hmr.js.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +1 -1
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +1 -1
- package/dist/internal.js.map +1 -1
- package/dist/lifetime.d.ts +39 -0
- package/dist/lifetime.d.ts.map +1 -0
- package/dist/lifetime.js +250 -0
- package/dist/lifetime.js.map +1 -0
- package/dist/mathml-elements.js +1 -1
- package/dist/mathml-elements.js.map +1 -1
- package/dist/mount.d.ts +11 -9
- package/dist/mount.d.ts.map +1 -1
- package/dist/mount.js +15 -31
- package/dist/mount.js.map +1 -1
- package/dist/primitives/branch.d.ts +1 -1
- package/dist/primitives/branch.d.ts.map +1 -1
- package/dist/primitives/branch.js +30 -20
- package/dist/primitives/branch.js.map +1 -1
- package/dist/primitives/child.js +12 -12
- package/dist/primitives/child.js.map +1 -1
- package/dist/primitives/context.js +6 -6
- package/dist/primitives/context.js.map +1 -1
- package/dist/primitives/each.d.ts.map +1 -1
- package/dist/primitives/each.js +37 -32
- package/dist/primitives/each.js.map +1 -1
- package/dist/primitives/error-boundary.js +7 -7
- package/dist/primitives/error-boundary.js.map +1 -1
- package/dist/primitives/foreign.js +3 -3
- package/dist/primitives/foreign.js.map +1 -1
- package/dist/primitives/lazy.js +11 -11
- package/dist/primitives/lazy.js.map +1 -1
- package/dist/primitives/on-mount.js +2 -2
- package/dist/primitives/on-mount.js.map +1 -1
- package/dist/primitives/portal.js +4 -4
- package/dist/primitives/portal.js.map +1 -1
- package/dist/primitives/sample.d.ts +18 -0
- package/dist/primitives/sample.d.ts.map +1 -0
- package/dist/primitives/sample.js +22 -0
- package/dist/primitives/sample.js.map +1 -0
- package/dist/primitives/scope.d.ts +25 -0
- package/dist/primitives/scope.d.ts.map +1 -0
- package/dist/primitives/scope.js +36 -0
- package/dist/primitives/scope.js.map +1 -0
- package/dist/primitives/selector.js +4 -4
- package/dist/primitives/selector.js.map +1 -1
- package/dist/primitives/show.d.ts.map +1 -1
- package/dist/primitives/show.js +4 -1
- package/dist/primitives/show.js.map +1 -1
- package/dist/primitives/slice.d.ts.map +1 -1
- package/dist/primitives/slice.js +18 -2
- package/dist/primitives/slice.js.map +1 -1
- package/dist/primitives/text.js +3 -3
- package/dist/primitives/text.js.map +1 -1
- package/dist/primitives/virtual-each.d.ts.map +1 -1
- package/dist/primitives/virtual-each.js +9 -9
- package/dist/primitives/virtual-each.js.map +1 -1
- package/dist/render-context.d.ts +2 -2
- package/dist/render-context.d.ts.map +1 -1
- package/dist/render-context.js.map +1 -1
- package/dist/ssr.d.ts +4 -4
- package/dist/ssr.d.ts.map +1 -1
- package/dist/ssr.js +3 -3
- package/dist/ssr.js.map +1 -1
- package/dist/svg-elements.js +1 -1
- package/dist/svg-elements.js.map +1 -1
- package/dist/tracking/disposer-log.d.ts +4 -4
- package/dist/tracking/disposer-log.d.ts.map +1 -1
- package/dist/tracking/disposer-log.js.map +1 -1
- package/dist/types.d.ts +79 -10
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/update-loop.d.ts +3 -3
- package/dist/update-loop.d.ts.map +1 -1
- package/dist/update-loop.js +12 -8
- package/dist/update-loop.js.map +1 -1
- package/dist/view-helpers.d.ts +10 -2
- package/dist/view-helpers.d.ts.map +1 -1
- package/dist/view-helpers.js +4 -0
- package/dist/view-helpers.js.map +1 -1
- package/package.json +1 -1
package/dist/primitives/child.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getRenderContext, setRenderContext, clearRenderContext } from '../render-context.js';
|
|
2
|
-
import {
|
|
2
|
+
import { createLifetime, disposeLifetime, addDisposer } from '../lifetime.js';
|
|
3
3
|
import { createComponentInstance, flushInstance } from '../update-loop.js';
|
|
4
4
|
import { createBinding, setFlatBindings } from '../binding.js';
|
|
5
5
|
import { registerChild, unregisterChild } from '../addressed.js';
|
|
@@ -13,15 +13,15 @@ export function child(opts) {
|
|
|
13
13
|
`and never update.`);
|
|
14
14
|
}
|
|
15
15
|
const parentCtx = getRenderContext('child');
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
// Tag eagerly:
|
|
16
|
+
const parentLifetime = parentCtx.rootLifetime;
|
|
17
|
+
const childLifetime = createLifetime(parentLifetime);
|
|
18
|
+
childLifetime._kind = 'child';
|
|
19
|
+
// Tag eagerly: childLifetime lives as long as the child component is mounted,
|
|
20
20
|
// so disposing it IS the child-unmount event. Setting the cause up front
|
|
21
21
|
// (instead of inside the disposer closure below) ensures the parent's
|
|
22
|
-
// _disposerLog sees it when `
|
|
23
|
-
// `childInst.
|
|
24
|
-
|
|
22
|
+
// _disposerLog sees it when `disposeLifetime` walks up the parent chain —
|
|
23
|
+
// `childInst.rootLifetime` is an orphan (parent = null) and cannot emit.
|
|
24
|
+
childLifetime.disposalCause = 'child-unmount';
|
|
25
25
|
const parentSend = parentCtx.send;
|
|
26
26
|
const childDef = opts.def;
|
|
27
27
|
const initialProps = opts.props(parentCtx.state);
|
|
@@ -47,7 +47,7 @@ export function child(opts) {
|
|
|
47
47
|
// accessor purely to fire the diff + propsMsg dispatch below. There is no
|
|
48
48
|
// DOM output — the comment node is a detached anchor kept only so the
|
|
49
49
|
// Binding shape stays uniform with other kinds.
|
|
50
|
-
createBinding(
|
|
50
|
+
createBinding(childLifetime, {
|
|
51
51
|
mask: FULL_MASK,
|
|
52
52
|
accessor: ((parentState) => {
|
|
53
53
|
const newProps = opts.props(parentState);
|
|
@@ -88,10 +88,10 @@ export function child(opts) {
|
|
|
88
88
|
// Register in component registry for addressed effects
|
|
89
89
|
registerChild(opts.key, { send: childInst.send });
|
|
90
90
|
// Cleanup: dispose child instance when parent scope disposes
|
|
91
|
-
addDisposer(
|
|
91
|
+
addDisposer(childLifetime, () => {
|
|
92
92
|
unregisterChild(opts.key);
|
|
93
|
-
childInst.
|
|
94
|
-
|
|
93
|
+
childInst.rootLifetime.disposalCause = 'child-unmount';
|
|
94
|
+
disposeLifetime(childInst.rootLifetime);
|
|
95
95
|
});
|
|
96
96
|
return nodes;
|
|
97
97
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"child.js","sourceRoot":"","sources":["../../src/primitives/child.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAC7F,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"child.js","sourceRoot":"","sources":["../../src/primitives/child.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ChildOptions, ComponentDef } from '../types.js'\nimport { getRenderContext, setRenderContext, clearRenderContext } from '../render-context.js'\nimport { createLifetime, disposeLifetime, addDisposer } from '../lifetime.js'\nimport { createComponentInstance, flushInstance, type ComponentInstance } from '../update-loop.js'\nimport { createBinding, setFlatBindings } from '../binding.js'\nimport { registerChild, unregisterChild } from '../addressed.js'\nimport { createView } from '../view-helpers.js'\n\nconst FULL_MASK = 0xffffffff\n\nexport function child<S, ChildM>(opts: ChildOptions<S, ChildM>): Node[] {\n // Dev-mode guard: props must be a function, not a static object\n if (typeof opts.props !== 'function') {\n throw new Error(\n `child(\"${String(opts.key)}\"): props must be a reactive accessor function ` +\n `(s => ({ ... })), not a static object. Static props are captured once at mount ` +\n `and never update.`,\n )\n }\n\n const parentCtx = getRenderContext('child')\n const parentLifetime = parentCtx.rootLifetime\n const childLifetime = createLifetime(parentLifetime)\n childLifetime._kind = 'child'\n // Tag eagerly: childLifetime lives as long as the child component is mounted,\n // so disposing it IS the child-unmount event. Setting the cause up front\n // (instead of inside the disposer closure below) ensures the parent's\n // _disposerLog sees it when `disposeLifetime` walks up the parent chain —\n // `childInst.rootLifetime` is an orphan (parent = null) and cannot emit.\n childLifetime.disposalCause = 'child-unmount'\n const parentSend = parentCtx.send\n\n const childDef = opts.def as ComponentDef<unknown, ChildM, unknown, Record<string, unknown>>\n const initialProps = opts.props(parentCtx.state as S)\n const childInst = createComponentInstance(childDef, initialProps)\n\n // Wrap child's send to intercept messages for onMsg → parent\n const originalSend = childInst.send\n childInst.send = (msg: ChildM) => {\n originalSend(msg)\n if (opts.onMsg && parentSend) {\n // Defer to after the child processes — use microtask\n queueMicrotask(() => {\n const parentMsg = opts.onMsg!(msg)\n if (parentMsg != null) {\n parentSend(parentMsg)\n }\n })\n }\n }\n\n // Track props for shallow-diff\n let prevProps: Record<string, unknown> = { ...initialProps }\n\n // Register a binding on the child scope that watches parent props changes.\n // This is a side-effect-only (`kind: 'effect'`) binding: Phase 2 runs the\n // accessor purely to fire the diff + propsMsg dispatch below. There is no\n // DOM output — the comment node is a detached anchor kept only so the\n // Binding shape stays uniform with other kinds.\n createBinding(childLifetime, {\n mask: FULL_MASK,\n accessor: ((parentState: S) => {\n const newProps = opts.props(parentState)\n\n let changed = false\n for (const key of Object.keys(newProps)) {\n if (!Object.is(newProps[key], prevProps[key])) {\n changed = true\n break\n }\n }\n\n if (changed && childDef.propsMsg) {\n const msg = childDef.propsMsg(newProps)\n // Dispatch via `originalSend` so framework-synthesized propsMsg\n // traffic bypasses the `onMsg` wrapper below. Otherwise a naive\n // `onMsg: m => forward(m)` echoes props/set back to the parent,\n // which mutates parent state, re-fires this accessor, and loops\n // forever.\n originalSend(msg)\n flushInstance(childInst)\n }\n prevProps = { ...newProps }\n }) as (state: never) => unknown,\n kind: 'effect',\n node: document.createComment('child:' + opts.key),\n perItem: false,\n })\n\n // Run the child's view within the child's render context\n setFlatBindings(childInst.allBindings)\n setRenderContext({\n ...childInst,\n send: childInst.send as (msg: unknown) => void,\n instance: childInst as ComponentInstance,\n })\n const nodes = childDef.view(createView(childInst.send))\n clearRenderContext()\n setFlatBindings(parentCtx.allBindings)\n setRenderContext(parentCtx)\n\n // Register in component registry for addressed effects\n registerChild(opts.key, { send: childInst.send as (msg: unknown) => void })\n\n // Cleanup: dispose child instance when parent scope disposes\n addDisposer(childLifetime, () => {\n unregisterChild(opts.key)\n childInst.rootLifetime.disposalCause = 'child-unmount'\n disposeLifetime(childInst.rootLifetime)\n })\n\n return nodes\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getRenderContext, setRenderContext, clearRenderContext } from '../render-context.js';
|
|
2
|
-
import {
|
|
2
|
+
import { createLifetime } from '../lifetime.js';
|
|
3
3
|
/**
|
|
4
4
|
* Per-scope storage: scope → (context-id → accessor).
|
|
5
5
|
* WeakMap so disposed scopes are GC'd.
|
|
@@ -35,18 +35,18 @@ export function createContext(defaultValue, name) {
|
|
|
35
35
|
*/
|
|
36
36
|
export function provide(ctx, accessor, children) {
|
|
37
37
|
const renderCtx = getRenderContext('provide');
|
|
38
|
-
const
|
|
38
|
+
const parentLifetime = renderCtx.rootLifetime;
|
|
39
39
|
// Create a sub-scope so the context is attached to THIS provider alone.
|
|
40
40
|
// Descendants (including those mounted later via show/branch/each) walk
|
|
41
41
|
// up to this scope via their own parent chain and find the accessor.
|
|
42
42
|
// Nested providers create their own sub-scope, shadowing outer values.
|
|
43
|
-
const providerScope =
|
|
43
|
+
const providerScope = createLifetime(parentLifetime);
|
|
44
44
|
const map = new Map();
|
|
45
45
|
map.set(ctx._id, accessor);
|
|
46
46
|
contextMap.set(providerScope, map);
|
|
47
|
-
// Render children with the provider scope as the new
|
|
47
|
+
// Render children with the provider scope as the new rootLifetime so any
|
|
48
48
|
// primitives (bindings, structural blocks, nested providers) attach here.
|
|
49
|
-
setRenderContext({ ...renderCtx,
|
|
49
|
+
setRenderContext({ ...renderCtx, rootLifetime: providerScope });
|
|
50
50
|
try {
|
|
51
51
|
return children();
|
|
52
52
|
}
|
|
@@ -70,7 +70,7 @@ export function provide(ctx, accessor, children) {
|
|
|
70
70
|
export function useContext(ctx) {
|
|
71
71
|
let scope = null;
|
|
72
72
|
try {
|
|
73
|
-
scope = getRenderContext('useContext').
|
|
73
|
+
scope = getRenderContext('useContext').rootLifetime;
|
|
74
74
|
}
|
|
75
75
|
catch {
|
|
76
76
|
// No render context (e.g. called from connect() in a unit test).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/primitives/context.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAC7F,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/primitives/context.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Lifetime } from '../types.js'\nimport { getRenderContext, setRenderContext, clearRenderContext } from '../render-context.js'\nimport { createLifetime } from '../lifetime.js'\n\n/**\n * Per-scope storage: scope → (context-id → accessor).\n * WeakMap so disposed scopes are GC'd.\n */\nconst contextMap = new WeakMap<Lifetime, Map<symbol, (s: unknown) => unknown>>()\n\nexport interface Context<T> {\n readonly _id: symbol\n readonly _default: T | undefined\n readonly _name: string | undefined\n}\n\n/**\n * Create a typed context key. Pass a default value to make consumers without a\n * provider resolve to it; omit to make unprovided consumption throw.\n *\n * ```ts\n * const ThemeContext = createContext<'light' | 'dark'>('light')\n * ```\n */\nexport function createContext<T>(defaultValue?: T, name?: string): Context<T> {\n return { _id: Symbol(name ?? 'llui-ctx'), _default: defaultValue, _name: name }\n}\n\n/**\n * Provide a reactive value for `ctx` to every descendant rendered inside `children`.\n * The accessor `(s: S) => T` is evaluated lazily at binding read time, so providers\n * can thread state slices down without prop drilling.\n *\n * ```ts\n * view: ({ send }) => [\n * provide(ThemeContext, (s: State) => s.theme, () => [\n * header(send),\n * main(send),\n * ]),\n * ]\n * ```\n *\n * Nested providers shadow outer ones within their subtree. The outer value is\n * restored after `children()` returns, so sibling subtrees aren't affected.\n */\nexport function provide<S, T>(\n ctx: Context<T>,\n accessor: (s: S) => T,\n children: () => Node[],\n): Node[] {\n const renderCtx = getRenderContext('provide')\n const parentLifetime = renderCtx.rootLifetime\n // Create a sub-scope so the context is attached to THIS provider alone.\n // Descendants (including those mounted later via show/branch/each) walk\n // up to this scope via their own parent chain and find the accessor.\n // Nested providers create their own sub-scope, shadowing outer values.\n const providerScope = createLifetime(parentLifetime)\n const map = new Map<symbol, (s: unknown) => unknown>()\n map.set(ctx._id, accessor as (s: unknown) => unknown)\n contextMap.set(providerScope, map)\n // Render children with the provider scope as the new rootLifetime so any\n // primitives (bindings, structural blocks, nested providers) attach here.\n setRenderContext({ ...renderCtx, rootLifetime: providerScope })\n try {\n return children()\n } finally {\n clearRenderContext()\n setRenderContext(renderCtx)\n }\n}\n\n/**\n * Read a context accessor within a view or view-function. Walks the scope chain\n * from the current render point to find the nearest provider. Returns an\n * `(s: S) => T` accessor that can be passed to bindings (text/class/etc.).\n *\n * ```ts\n * export function themedCard(): Node[] {\n * const theme = useContext(ThemeContext)\n * return div({ class: (s) => `card theme-${theme(s)}` }, [...])\n * }\n * ```\n */\nexport function useContext<S, T>(ctx: Context<T>): (s: S) => T {\n let scope: Lifetime | null = null\n try {\n scope = getRenderContext('useContext').rootLifetime\n } catch {\n // No render context (e.g. called from connect() in a unit test).\n // Fall through to default resolution below.\n }\n while (scope) {\n const map = contextMap.get(scope)\n if (map?.has(ctx._id)) {\n const accessor = map.get(ctx._id)!\n return accessor as (s: S) => T\n }\n scope = scope.parent\n }\n if (ctx._default !== undefined) {\n const d = ctx._default\n return () => d\n }\n const label = ctx._name ?? ctx._id.description ?? 'unknown'\n throw new Error(\n `[LLui] useContext(${label}): no provider found and no default value. ` +\n `Wrap a parent element with provide(${label}, accessor, () => [...]) ` +\n `or pass a default to createContext().`,\n )\n}\n\n/**\n * Provide a state-independent value to every descendant. Companion to\n * `provide()` for the common case of publishing a stable dispatcher\n * bag, callback record, or DI container — anything that doesn't depend\n * on the parent's state.\n *\n * ```ts\n * provideValue(ToastContext, { show: (m) => send({ type: 'toast', m }) }, () => [\n * main([pageSlot()]),\n * ])\n * ```\n *\n * Equivalent to `provide(ctx, () => value, children)`, but exists so\n * the call site reads as \"provide this value\" rather than \"provide an\n * accessor that ignores its state argument and returns a value.\" Pair\n * with `useContextValue` for symmetric ergonomics on the consumer side.\n *\n * Internally still uses the accessor mechanism: the value is wrapped\n * in a constant lambda. Consumers that read via the reactive\n * `useContext` form will get an `(s) => T` whose accessor ignores `s`.\n */\nexport function provideValue<T>(ctx: Context<T>, value: T, children: () => Node[]): Node[] {\n return provide(ctx, () => value, children)\n}\n\n/**\n * Read a state-independent value from the nearest provider. Companion\n * to `useContext()` for the common case of consuming a stable\n * dispatcher bag, callback record, or DI container.\n *\n * ```ts\n * const toast = useContextValue(ToastContext)\n * button({ onClick: () => toast.show('Saved') }, [text('Save')])\n * ```\n *\n * Equivalent to calling the accessor returned by `useContext` with\n * `undefined`, but reads as a single function call instead of a\n * three-step \"look up the accessor, ignore the state arg, get the\n * value\" dance.\n *\n * ## Value capture contract\n *\n * **The returned value is captured once, at view-construction time.**\n * Any reference you store from `useContextValue(ctx)` into a closure\n * — for example, by assigning it to a local `const` inside `view(...)`\n * and reading it from an event handler — sees the value as it was\n * when the view ran. The closure does NOT re-read the context on each\n * event dispatch.\n *\n * That's fine, and usually what you want, for stable dispatcher bags:\n * the bag's methods close over the layout's `send`, and `send` itself\n * is stable across the layout's lifetime, so the methods work\n * correctly regardless of when the handler fires. Pages can stash\n * `const toast = useContextValue(ToastContext)` at the top of their\n * `view()` and call `toast.show(...)` from any event handler below.\n *\n * **Do NOT use `useContextValue` when the consumer needs to see\n * updates to the context value.** If a parent re-`provideValue`s the\n * context with a different object later, existing consumers already\n * holding the captured value will still see the old one. For\n * reactive consumption, use `useContext(ctx)` — that returns an\n * accessor that re-reads the provider on each binding evaluation, so\n * reactive bindings (`class`, `text`, etc.) pick up updates\n * automatically.\n *\n * **Do NOT use `useContextValue` against a provider whose accessor\n * reads from state.** The accessor is invoked with `undefined` here,\n * so any `(s) => s.something` provider will throw or return garbage.\n * Match `provideValue` on the producer side with `useContextValue` on\n * the consumer side, and `provide` with `useContext`.\n */\nexport function useContextValue<T>(ctx: Context<T>): T {\n const accessor = useContext<unknown, T>(ctx)\n // The contract above: the producer side promised this accessor\n // doesn't read its state arg. Pass undefined.\n return accessor(undefined)\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"each.d.ts","sourceRoot":"","sources":["../../src/primitives/each.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,
|
|
1
|
+
{"version":3,"file":"each.d.ts","sourceRoot":"","sources":["../../src/primitives/each.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAA0B,MAAM,aAAa,CAAA;AAuBtE,uEAAuE;AACvE,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI,CAEpD;AAED,sEAAsE;AACtE,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,KAAK,IAAI,GAAG,IAAI,CAEzE;AAgCD,wBAAgB,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,CAmR1E"}
|
package/dist/primitives/each.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getRenderContext, setRenderContext, clearRenderContext, } from '../render-context.js';
|
|
2
|
-
import {
|
|
2
|
+
import { createLifetime, disposeLifetime, disposeLifetimesBulk, addDisposer, removeOrphanedChildren, } from '../lifetime.js';
|
|
3
3
|
import { getFlatBindings, setFlatBindings } from '../binding.js';
|
|
4
4
|
import { FULL_MASK } from '../update-loop.js';
|
|
5
5
|
// Clear callbacks — registered by selector.bind() during render, called by reconcileClear().
|
|
@@ -18,7 +18,7 @@ export function registerOnRemove(cb) {
|
|
|
18
18
|
}
|
|
19
19
|
// Reusable render context for buildEntry — avoids object allocation per entry
|
|
20
20
|
const buildCtx = {
|
|
21
|
-
|
|
21
|
+
rootLifetime: null,
|
|
22
22
|
state: null,
|
|
23
23
|
allBindings: [],
|
|
24
24
|
structuralBlocks: [],
|
|
@@ -35,7 +35,7 @@ const buildBag = {
|
|
|
35
35
|
};
|
|
36
36
|
export function each(opts) {
|
|
37
37
|
const ctx = getRenderContext('each');
|
|
38
|
-
const
|
|
38
|
+
const parentLifetime = ctx.rootLifetime;
|
|
39
39
|
const blocks = ctx.structuralBlocks;
|
|
40
40
|
const anchor = document.createComment('each');
|
|
41
41
|
const entries = [];
|
|
@@ -111,7 +111,7 @@ export function each(opts) {
|
|
|
111
111
|
lastItemsRef = newItems;
|
|
112
112
|
const oldKeys = snapshotKeys();
|
|
113
113
|
const report = opts.onTransition ? { entering: [], leaving: [] } : null;
|
|
114
|
-
reconcileEntries(entries, newItems, opts,
|
|
114
|
+
reconcileEntries(entries, newItems, opts, parentLifetime, parent, anchor, ctx, state, leaving, report);
|
|
115
115
|
if (opts.onTransition && report) {
|
|
116
116
|
opts.onTransition({ entering: report.entering, leaving: report.leaving, parent });
|
|
117
117
|
}
|
|
@@ -159,8 +159,8 @@ export function each(opts) {
|
|
|
159
159
|
s.disposalCause = 'each-remove';
|
|
160
160
|
scopes.push(s);
|
|
161
161
|
}
|
|
162
|
-
|
|
163
|
-
removeOrphanedChildren(
|
|
162
|
+
disposeLifetimesBulk(scopes);
|
|
163
|
+
removeOrphanedChildren(parentLifetime);
|
|
164
164
|
entries.length = 0;
|
|
165
165
|
emitDiff(oldKeys);
|
|
166
166
|
},
|
|
@@ -178,7 +178,7 @@ export function each(opts) {
|
|
|
178
178
|
const newLen = newItems.length;
|
|
179
179
|
if (newLen >= oldLen) {
|
|
180
180
|
// Not a removal — fallback (shouldn't happen if compiler detected correctly)
|
|
181
|
-
reconcileEntries(entries, newItems, opts,
|
|
181
|
+
reconcileEntries(entries, newItems, opts, parentLifetime, parent, anchor, ctx, state, leaving, null);
|
|
182
182
|
emitDiff(oldKeys);
|
|
183
183
|
return;
|
|
184
184
|
}
|
|
@@ -201,7 +201,7 @@ export function each(opts) {
|
|
|
201
201
|
for (const node of entry.nodes)
|
|
202
202
|
parent.removeChild(node);
|
|
203
203
|
entry.scope.disposalCause = 'each-remove';
|
|
204
|
-
|
|
204
|
+
disposeLifetime(entry.scope, true);
|
|
205
205
|
entries[oi] = null;
|
|
206
206
|
didRemove = true;
|
|
207
207
|
}
|
|
@@ -214,7 +214,7 @@ export function each(opts) {
|
|
|
214
214
|
entries[w++] = entries[r];
|
|
215
215
|
}
|
|
216
216
|
entries.length = w;
|
|
217
|
-
removeOrphanedChildren(
|
|
217
|
+
removeOrphanedChildren(parentLifetime);
|
|
218
218
|
}
|
|
219
219
|
// Update indices for remaining entries
|
|
220
220
|
for (let i = 0; i < entries.length; i++) {
|
|
@@ -245,7 +245,7 @@ export function each(opts) {
|
|
|
245
245
|
activeRemoveCallbacks = removeCallbacks;
|
|
246
246
|
for (let i = 0; i < initialItems.length; i++) {
|
|
247
247
|
const item = initialItems[i];
|
|
248
|
-
const entry = buildEntry(item, i, opts,
|
|
248
|
+
const entry = buildEntry(item, i, opts, parentLifetime, ctx);
|
|
249
249
|
entries.push(entry);
|
|
250
250
|
}
|
|
251
251
|
activeClearCallbacks = null;
|
|
@@ -257,15 +257,15 @@ export function each(opts) {
|
|
|
257
257
|
opts.enter(entry.nodes);
|
|
258
258
|
}
|
|
259
259
|
}
|
|
260
|
-
addDisposer(
|
|
260
|
+
addDisposer(parentLifetime, () => {
|
|
261
261
|
const idx = blocks.indexOf(block);
|
|
262
262
|
if (idx !== -1)
|
|
263
263
|
blocks.splice(idx, 1);
|
|
264
|
-
//
|
|
264
|
+
// parentLifetime is being disposed — its children array is about to be
|
|
265
265
|
// cleared by the recursive dispose pass, so skip per-entry parent
|
|
266
266
|
// removal (avoids O(N²) indexOf+splice).
|
|
267
267
|
for (const entry of entries) {
|
|
268
|
-
|
|
268
|
+
disposeLifetime(entry.scope, true);
|
|
269
269
|
}
|
|
270
270
|
entries.length = 0;
|
|
271
271
|
// Force-remove any mid-leave entries immediately
|
|
@@ -274,7 +274,7 @@ export function each(opts) {
|
|
|
274
274
|
if (node.parentNode)
|
|
275
275
|
node.parentNode.removeChild(node);
|
|
276
276
|
}
|
|
277
|
-
|
|
277
|
+
disposeLifetime(entry.scope, true);
|
|
278
278
|
}
|
|
279
279
|
leaving.length = 0;
|
|
280
280
|
});
|
|
@@ -298,7 +298,7 @@ function removeEntry(entry, opts, leaving) {
|
|
|
298
298
|
node.parentNode.removeChild(node);
|
|
299
299
|
}
|
|
300
300
|
entry.scope.disposalCause = 'each-remove';
|
|
301
|
-
|
|
301
|
+
disposeLifetime(entry.scope);
|
|
302
302
|
const idx = leaving.indexOf(entry);
|
|
303
303
|
if (idx !== -1)
|
|
304
304
|
leaving.splice(idx, 1);
|
|
@@ -318,12 +318,12 @@ function fireEnter(entry, opts) {
|
|
|
318
318
|
void opts.enter(entry.nodes);
|
|
319
319
|
}
|
|
320
320
|
}
|
|
321
|
-
function buildEntry(item, index, opts,
|
|
321
|
+
function buildEntry(item, index, opts, parentLifetime, ctx, state) {
|
|
322
322
|
const key = opts.key(item);
|
|
323
323
|
// Use a lightweight scope — just needs itemUpdaters for per-item bindings.
|
|
324
324
|
// Full scope features (disposers, bindings, children) are only needed when
|
|
325
325
|
// the render callback uses structural primitives or selector.bind().
|
|
326
|
-
const scope =
|
|
326
|
+
const scope = createLifetime(parentLifetime);
|
|
327
327
|
scope._kind = 'each';
|
|
328
328
|
const currentState = (state ?? ctx.state);
|
|
329
329
|
const send = ctx.send;
|
|
@@ -358,7 +358,12 @@ function buildEntry(item, index, opts, parentScope, ctx, state) {
|
|
|
358
358
|
else {
|
|
359
359
|
fieldCache = new Map();
|
|
360
360
|
}
|
|
361
|
-
|
|
361
|
+
// `current` returns the whole item — essential for primitive T
|
|
362
|
+
// (where the field map is useless) and for whole-record sampling.
|
|
363
|
+
// Caller must call it like a method: `item.current()`.
|
|
364
|
+
const accessor = key === 'current'
|
|
365
|
+
? () => entry.current
|
|
366
|
+
: () => entry.current[key];
|
|
362
367
|
accessor.__perItem = true;
|
|
363
368
|
fieldCache.set(key, accessor);
|
|
364
369
|
return accessor;
|
|
@@ -368,7 +373,7 @@ function buildEntry(item, index, opts, parentScope, ctx, state) {
|
|
|
368
373
|
};
|
|
369
374
|
const indexAccessor = () => entry.index;
|
|
370
375
|
// Reuse a single context object to avoid allocation per entry
|
|
371
|
-
buildCtx.
|
|
376
|
+
buildCtx.rootLifetime = scope;
|
|
372
377
|
buildCtx.state = currentState;
|
|
373
378
|
buildCtx.allBindings = ctx.allBindings;
|
|
374
379
|
buildCtx.structuralBlocks = ctx.structuralBlocks;
|
|
@@ -405,7 +410,7 @@ function collectNodes(target, nodes) {
|
|
|
405
410
|
for (const n of nodes)
|
|
406
411
|
target.push(n);
|
|
407
412
|
}
|
|
408
|
-
function reconcileEntries(entries, newItems, opts,
|
|
413
|
+
function reconcileEntries(entries, newItems, opts, parentLifetime, parent, anchor, ctx, state, leaving, report) {
|
|
409
414
|
const oldLen = entries.length;
|
|
410
415
|
const newLen = newItems.length;
|
|
411
416
|
const hasLeave = !!opts.leave;
|
|
@@ -440,8 +445,8 @@ function reconcileEntries(entries, newItems, opts, parentScope, parent, anchor,
|
|
|
440
445
|
s.disposalCause = 'each-remove';
|
|
441
446
|
scopes.push(s);
|
|
442
447
|
}
|
|
443
|
-
|
|
444
|
-
removeOrphanedChildren(
|
|
448
|
+
disposeLifetimesBulk(scopes);
|
|
449
|
+
removeOrphanedChildren(parentLifetime);
|
|
445
450
|
entries.length = 0;
|
|
446
451
|
return;
|
|
447
452
|
}
|
|
@@ -458,7 +463,7 @@ function reconcileEntries(entries, newItems, opts, parentScope, parent, anchor,
|
|
|
458
463
|
const frag = document.createDocumentFragment();
|
|
459
464
|
const newlyAdded = [];
|
|
460
465
|
for (let i = oldLen; i < newLen; i++) {
|
|
461
|
-
const entry = buildEntry(newItems[i], i, opts,
|
|
466
|
+
const entry = buildEntry(newItems[i], i, opts, parentLifetime, ctx, state);
|
|
462
467
|
entries.push(entry);
|
|
463
468
|
newlyAdded.push(entry);
|
|
464
469
|
for (const node of entry.nodes)
|
|
@@ -547,20 +552,20 @@ function reconcileEntries(entries, newItems, opts, parentScope, parent, anchor,
|
|
|
547
552
|
range.setEndAfter(lastEntry.nodes[lastEntry.nodes.length - 1]);
|
|
548
553
|
range.deleteContents();
|
|
549
554
|
// Bulk dispose all old scopes
|
|
550
|
-
const
|
|
555
|
+
const oldLifetimes = [];
|
|
551
556
|
for (let i = 0; i < entries.length; i++) {
|
|
552
557
|
const s = entries[i].scope;
|
|
553
558
|
s.disposalCause = 'each-remove';
|
|
554
|
-
|
|
559
|
+
oldLifetimes.push(s);
|
|
555
560
|
}
|
|
556
|
-
|
|
557
|
-
removeOrphanedChildren(
|
|
561
|
+
disposeLifetimesBulk(oldLifetimes);
|
|
562
|
+
removeOrphanedChildren(parentLifetime);
|
|
558
563
|
entries.length = 0;
|
|
559
564
|
// Build all new entries into a fragment
|
|
560
565
|
const frag = document.createDocumentFragment();
|
|
561
566
|
const newlyAdded = [];
|
|
562
567
|
for (let i = 0; i < newLen; i++) {
|
|
563
|
-
const entry = buildEntry(newItems[i], i, opts,
|
|
568
|
+
const entry = buildEntry(newItems[i], i, opts, parentLifetime, ctx, state);
|
|
564
569
|
entries.push(entry);
|
|
565
570
|
newlyAdded.push(entry);
|
|
566
571
|
for (const node of entry.nodes)
|
|
@@ -594,14 +599,14 @@ function reconcileEntries(entries, newItems, opts, parentScope, parent, anchor,
|
|
|
594
599
|
newEntries.push(existing);
|
|
595
600
|
}
|
|
596
601
|
else {
|
|
597
|
-
const entry = buildEntry(item, i, opts,
|
|
602
|
+
const entry = buildEntry(item, i, opts, parentLifetime, ctx, state);
|
|
598
603
|
newEntries.push(entry);
|
|
599
604
|
newlyAdded.push(entry);
|
|
600
605
|
}
|
|
601
606
|
}
|
|
602
607
|
// Remove entries not in the new list. Use bulk-detach pattern so
|
|
603
608
|
// disposing K removals costs O(K+P) rather than O(K*P) where P is
|
|
604
|
-
//
|
|
609
|
+
// parentLifetime.children.length (avoids K * indexOf+splice).
|
|
605
610
|
let didBulkDetach = false;
|
|
606
611
|
for (const entry of entries) {
|
|
607
612
|
if (!usedKeys.has(entry.key)) {
|
|
@@ -614,13 +619,13 @@ function reconcileEntries(entries, newItems, opts, parentScope, parent, anchor,
|
|
|
614
619
|
for (const node of entry.nodes)
|
|
615
620
|
parent.removeChild(node);
|
|
616
621
|
entry.scope.disposalCause = 'each-remove';
|
|
617
|
-
|
|
622
|
+
disposeLifetime(entry.scope, true);
|
|
618
623
|
didBulkDetach = true;
|
|
619
624
|
}
|
|
620
625
|
}
|
|
621
626
|
}
|
|
622
627
|
if (didBulkDetach)
|
|
623
|
-
removeOrphanedChildren(
|
|
628
|
+
removeOrphanedChildren(parentLifetime);
|
|
624
629
|
// Reorder DOM
|
|
625
630
|
const hasSurvivors = newEntries.some((e) => oldByKey.has(e.key));
|
|
626
631
|
if (!hasSurvivors || !survivorsInOrder(entries, newEntries, usedKeys)) {
|