elements-kit 0.0.14 → 0.0.16

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.
Files changed (112) hide show
  1. package/README.md +138 -31
  2. package/dist/attributes-Dtn68R1u.d.mts +94 -0
  3. package/dist/attributes.d.mts +1 -93
  4. package/dist/custom-elements.d.mts +74 -0
  5. package/dist/custom-elements.mjs +100 -0
  6. package/dist/define-CjbTZ3VG.d.mts +23 -0
  7. package/dist/{element-C_4VbkvQ.mjs → element-CGVy_8TW.mjs} +12 -18
  8. package/dist/for.d.mts +45 -0
  9. package/dist/for.mjs +136 -0
  10. package/dist/{index-DUshSQ_6.d.mts → index-DydGTqZU.d.mts} +60 -10
  11. package/dist/index.d.mts +1 -36
  12. package/dist/index.mjs +1 -144
  13. package/dist/infer-BfzRJoCn.d.mts +203 -0
  14. package/dist/integrations/react.d.mts +1 -1
  15. package/dist/integrations/react.mjs +1 -1
  16. package/dist/jsx-runtime/index.d.mts +18 -25
  17. package/dist/jsx-runtime/index.mjs +2 -2
  18. package/dist/signals/index.d.mts +2 -2
  19. package/dist/signals/index.mjs +2 -2
  20. package/dist/{signals-BHmWX6ox.mjs → signals-J8dK_rA4.mjs} +57 -17
  21. package/dist/slot-C7GQZe-r.d.mts +104 -0
  22. package/dist/{slot-Cydy7-0L.mjs → slot-Kb61AcgW.mjs} +21 -2
  23. package/dist/slot.d.mts +2 -84
  24. package/dist/slot.mjs +2 -2
  25. package/dist/{test.BmQO5GaM-ANkhHvbr.mjs → test.BmQO5GaM-DfGStnii.mjs} +1 -1
  26. package/dist/utilities/_observe.d.mts +11 -0
  27. package/dist/utilities/_observe.mjs +15 -0
  28. package/dist/utilities/active-element.d.mts +1 -1
  29. package/dist/utilities/active-element.mjs +6 -2
  30. package/dist/utilities/active-element.test.mjs +1 -1
  31. package/dist/utilities/async.d.mts +1 -1
  32. package/dist/utilities/async.mjs +1 -1
  33. package/dist/utilities/async.test.mjs +2 -2
  34. package/dist/utilities/debounced.d.mts +1 -1
  35. package/dist/utilities/debounced.mjs +1 -1
  36. package/dist/utilities/debounced.test.mjs +2 -2
  37. package/dist/utilities/element-rect.d.mts +1 -1
  38. package/dist/utilities/element-rect.mjs +1 -1
  39. package/dist/utilities/element-rect.test.mjs +2 -2
  40. package/dist/utilities/element-scroll.d.mts +1 -1
  41. package/dist/utilities/element-scroll.mjs +7 -34
  42. package/dist/utilities/element-scroll.test.mjs +2 -2
  43. package/dist/utilities/environment.d.mts +5 -0
  44. package/dist/utilities/environment.mjs +5 -0
  45. package/dist/utilities/event-driven.d.mts +1 -1
  46. package/dist/utilities/event-driven.mjs +2 -2
  47. package/dist/utilities/event-listener.d.mts +1 -1
  48. package/dist/utilities/event-listener.mjs +1 -1
  49. package/dist/utilities/event-listener.test.mjs +2 -2
  50. package/dist/utilities/focus-within.d.mts +1 -1
  51. package/dist/utilities/focus-within.mjs +1 -1
  52. package/dist/utilities/focus-within.test.mjs +2 -2
  53. package/dist/utilities/hover.d.mts +1 -1
  54. package/dist/utilities/hover.mjs +1 -1
  55. package/dist/utilities/hover.test.mjs +2 -2
  56. package/dist/utilities/intersection-observer.mjs +4 -6
  57. package/dist/utilities/intersection-observer.test.mjs +2 -2
  58. package/dist/utilities/interval.d.mts +1 -1
  59. package/dist/utilities/interval.mjs +1 -1
  60. package/dist/utilities/interval.test.mjs +18 -2
  61. package/dist/utilities/location.d.mts +2 -14
  62. package/dist/utilities/location.mjs +12 -3
  63. package/dist/utilities/location.test.mjs +5 -5
  64. package/dist/utilities/long-press.mjs +1 -1
  65. package/dist/utilities/long-press.test.mjs +2 -2
  66. package/dist/utilities/media-devices.d.mts +1 -1
  67. package/dist/utilities/media-devices.mjs +1 -1
  68. package/dist/utilities/media-devices.test.mjs +2 -2
  69. package/dist/utilities/media-player.d.mts +1 -1
  70. package/dist/utilities/media-player.test.mjs +2 -2
  71. package/dist/utilities/media-query.d.mts +2 -3
  72. package/dist/utilities/media-query.mjs +3 -3
  73. package/dist/utilities/mutation-observer.mjs +4 -7
  74. package/dist/utilities/mutation-observer.test.mjs +2 -2
  75. package/dist/utilities/network.d.mts +2 -2
  76. package/dist/utilities/network.mjs +5 -3
  77. package/dist/utilities/network.test.mjs +1 -1
  78. package/dist/utilities/on-click-outside.test.mjs +2 -2
  79. package/dist/utilities/orientation.d.mts +1 -1
  80. package/dist/utilities/orientation.mjs +9 -1
  81. package/dist/utilities/previous.d.mts +1 -1
  82. package/dist/utilities/previous.mjs +1 -1
  83. package/dist/utilities/previous.test.mjs +2 -2
  84. package/dist/utilities/promise.d.mts +1 -1
  85. package/dist/utilities/promise.mjs +1 -1
  86. package/dist/utilities/promise.test.mjs +2 -2
  87. package/dist/utilities/resize-observer.mjs +4 -6
  88. package/dist/utilities/retry.mjs +1 -1
  89. package/dist/utilities/retry.test.mjs +2 -2
  90. package/dist/utilities/routing.d.mts +1 -1
  91. package/dist/utilities/routing.mjs +1 -1
  92. package/dist/utilities/routing.test.mjs +1 -1
  93. package/dist/utilities/search-params.d.mts +1 -1
  94. package/dist/utilities/search-params.test.mjs +2 -2
  95. package/dist/utilities/ssr.test.d.mts +1 -0
  96. package/dist/utilities/ssr.test.mjs +64 -0
  97. package/dist/utilities/storage.d.mts +1 -1
  98. package/dist/utilities/storage.test.mjs +2 -2
  99. package/dist/utilities/throttled.d.mts +1 -1
  100. package/dist/utilities/throttled.mjs +1 -1
  101. package/dist/utilities/throttled.test.mjs +2 -2
  102. package/dist/utilities/timeout.d.mts +1 -1
  103. package/dist/utilities/timeout.mjs +1 -1
  104. package/dist/utilities/timeout.test.mjs +57 -2
  105. package/dist/utilities/window-focus.d.mts +2 -2
  106. package/dist/utilities/window-focus.mjs +5 -3
  107. package/dist/utilities/window-size.d.mts +1 -1
  108. package/dist/utilities/window-size.mjs +10 -5
  109. package/dist/utilities/window-size.test.mjs +1 -1
  110. package/package.json +11 -1
  111. package/dist/lib-JA05lzCN.d.mts +0 -4
  112. /package/dist/{polyfill-DR5XVnh_.d.mts → polyfill-BVNd6ogU.d.mts} +0 -0
@@ -1,6 +1,7 @@
1
- import { d as effectScope, g as onCleanup, t as isReactive, u as effect, y as untracked } from "./signals-BHmWX6ox.mjs";
1
+ import { _ as onCleanup, b as untracked, d as effect, f as effectScope, t as isReactive } from "./signals-J8dK_rA4.mjs";
2
2
  import "./polyfill-B1lNNcum.mjs";
3
- import { i as resolveNode$1, n as Slot, r as Slots, t as $slots } from "./slot-Cydy7-0L.mjs";
3
+ import { on } from "./utilities/event-listener.mjs";
4
+ import { i as resolveNode$1, n as Slot, r as Slots, t as SLOTS } from "./slot-Kb61AcgW.mjs";
4
5
  //#region \0rolldown/runtime.js
5
6
  var __defProp = Object.defineProperty;
6
7
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -40,21 +41,21 @@ const ReservedNameSpaces = new Set([
40
41
  //#endregion
41
42
  //#region src/jsx-runtime/children.ts
42
43
  function hasSlots(node) {
43
- return $slots in node;
44
+ return SLOTS in node;
44
45
  }
45
46
  function isChildrenProperty(node, key) {
46
47
  if (key === "children" && (node instanceof Element || node instanceof DocumentFragment)) return true;
47
48
  if (hasSlots(node)) {
48
49
  const slotName = key.replace(/^slot:/, "");
49
- if (Slots.has(node[$slots], slotName)) return true;
50
+ if (Slots.has(node[SLOTS], slotName)) return true;
50
51
  }
51
52
  return key in node && node[key] instanceof Slot;
52
53
  }
53
54
  function applyChildren(node, key, value) {
54
55
  if (hasSlots(node)) {
55
56
  const slotName = key.replace(/^slot:/, "");
56
- if (Slots.has(node[$slots], slotName)) {
57
- applySlot(node[$slots][slotName], value);
57
+ if (Slots.has(node[SLOTS], slotName)) {
58
+ applySlot(node[SLOTS][slotName], value);
58
59
  return;
59
60
  }
60
61
  }
@@ -126,7 +127,7 @@ function applyProps(node, props) {
126
127
  if (isReactive(value)) {
127
128
  if (isEventKey(key)) {
128
129
  effect(() => {
129
- onCleanup(setEvent(node, key, value()));
130
+ on(node, eventName(key), value());
130
131
  });
131
132
  continue;
132
133
  }
@@ -134,7 +135,7 @@ function applyProps(node, props) {
134
135
  continue;
135
136
  }
136
137
  if (isEventKey(key)) {
137
- onCleanup(setEvent(node, key, value));
138
+ on(node, eventName(key), value);
138
139
  continue;
139
140
  }
140
141
  setProp(node, key, value);
@@ -210,19 +211,12 @@ function setAttribute(el, key, value) {
210
211
  function isEventKey(key) {
211
212
  return key.startsWith("on:") || key.length > 2 && key.startsWith("on") && key[2] >= "A" && key[2] <= "Z";
212
213
  }
213
- function setEvent(el, key, handler) {
214
- const event = key.startsWith("on:") ? key.slice(3) : key[2].toLowerCase() + key.slice(3);
215
- el.addEventListener(event, handler);
216
- return () => el.removeEventListener(event, handler);
214
+ function eventName(key) {
215
+ return key.startsWith("on:") ? key.slice(3) : key[2].toLowerCase() + key.slice(3);
217
216
  }
218
217
  //#endregion
219
- //#region src/jsx-runtime/ref.ts
220
- const $ref = Symbol("ref");
221
- //#endregion
222
218
  //#region src/jsx-runtime/element.ts
223
- function createElement(type, rawProps = {}) {
224
- const ref = rawProps[$ref];
225
- const props = Object.fromEntries(Object.entries(rawProps));
219
+ function createElement(type, { ref, ...props } = {}) {
226
220
  if (typeof type === "function" && !type.prototype?.render) return createFunctionElement(type, props, ref);
227
221
  return createNodeElement(type, props, ref);
228
222
  }
package/dist/for.d.mts ADDED
@@ -0,0 +1,45 @@
1
+ import { a as Props, o as Require } from "./infer-BfzRJoCn.mjs";
2
+
3
+ //#region src/for.d.ts
4
+ type KeyFn<T> = (item: T, index: number) => string | number;
5
+ type RenderFn<T> = (item: T, index: number) => Element | DocumentFragment | null;
6
+ /**
7
+ * Keyed list renderer. Reconciles a reactive array into the DOM using a key
8
+ * function to match existing nodes — minimising create/destroy churn.
9
+ *
10
+ * Reconciliation strategy (inspired by udomdiff / dom-expressions):
11
+ * 1. Remove stale entries (keys absent from the new array).
12
+ * 2. Skip unchanged common prefix and suffix.
13
+ * 3. Pure-append fast path when the surviving old range is empty.
14
+ * 4. Backward scan of the middle region — move or create entries so that
15
+ * each entry's range lands immediately before the already-correct cursor.
16
+ *
17
+ * @example
18
+ * ```tsx
19
+ * <For each={visibleTodos} by={(todo) => todo.id}>
20
+ * {(todo) => <li>{todo.text}</li>}
21
+ * </For>
22
+ * ```
23
+ *
24
+ * Props:
25
+ * each — reactive getter returning the array (e.g. a `computed`)
26
+ * by — extracts a stable key per item (default: index)
27
+ * children — render function called once per new key
28
+ */
29
+ /**
30
+ * Props for `<For>`, derived from its public instance fields.
31
+ * All props are optional — the class initializes sane defaults at runtime.
32
+ * Non-function props also accept a reactive getter.
33
+ */
34
+ type ForProps<T> = Require<Props<For<T>>, "each" | "children">;
35
+ declare class For<T = unknown> {
36
+ #private;
37
+ constructor(_props?: ForProps<T>);
38
+ get each(): T[];
39
+ set each(v: T[]);
40
+ by: KeyFn<T>;
41
+ children: RenderFn<T>;
42
+ render(): DocumentFragment;
43
+ }
44
+ //#endregion
45
+ export { For, ForProps };
package/dist/for.mjs ADDED
@@ -0,0 +1,136 @@
1
+ import { n as disposeElement } from "./element-CGVy_8TW.mjs";
2
+ import { _ as onCleanup, b as untracked, d as effect, f as effectScope, v as signal, y as trigger } from "./signals-J8dK_rA4.mjs";
3
+ //#region src/for.ts
4
+ var For = class {
5
+ constructor(_props) {}
6
+ #each = signal([]);
7
+ get each() {
8
+ return this.#each();
9
+ }
10
+ set each(v) {
11
+ if (v === untracked(this.#each)) {
12
+ trigger(this.#each);
13
+ return;
14
+ }
15
+ this.#each(v);
16
+ }
17
+ by = (_, i) => i;
18
+ children = () => null;
19
+ #start = document.createComment("<For>");
20
+ #end = document.createComment("</For>");
21
+ #cache = /* @__PURE__ */ new Map();
22
+ /** Keys in current DOM order — needed for prefix/suffix optimisation. */
23
+ #order = [];
24
+ render() {
25
+ const fragment = document.createDocumentFragment();
26
+ fragment.appendChild(this.#start);
27
+ fragment.appendChild(this.#end);
28
+ effect(() => this.#reconcile());
29
+ onCleanup(() => {
30
+ for (const entry of this.#cache.values()) cleanEntry(entry);
31
+ this.#cache.clear();
32
+ this.#order = [];
33
+ });
34
+ return fragment;
35
+ }
36
+ #reconcile() {
37
+ const parent = this.#start.parentNode;
38
+ if (!parent) return;
39
+ const items = this.each;
40
+ const b = items.map((item, i) => this.by(item, i));
41
+ const bSet = new Set(b);
42
+ for (const [key, entry] of this.#cache) if (!bSet.has(key)) {
43
+ removeEntry(entry);
44
+ this.#cache.delete(key);
45
+ }
46
+ const a = this.#order.filter((k) => this.#cache.has(k));
47
+ let aStart = 0, aEnd = a.length;
48
+ let bStart = 0, bEnd = b.length;
49
+ while (aStart < aEnd && bStart < bEnd && a[aStart] === b[bStart]) {
50
+ aStart++;
51
+ bStart++;
52
+ }
53
+ while (aEnd > aStart && bEnd > bStart && a[aEnd - 1] === b[bEnd - 1]) {
54
+ aEnd--;
55
+ bEnd--;
56
+ }
57
+ const after = bEnd < b.length ? this.#cache.get(b[bEnd]).start : this.#end;
58
+ if (aStart === aEnd) {
59
+ let cursor = after;
60
+ for (let i = bEnd - 1; i >= bStart; i--) {
61
+ const key = b[i];
62
+ const { entry, rendered } = this.#makeEntry(key, items[i], i);
63
+ insertEntry(parent, entry, cursor, rendered);
64
+ this.#cache.set(key, entry);
65
+ cursor = entry.start;
66
+ }
67
+ this.#order = [...b];
68
+ return;
69
+ }
70
+ let cursor = after;
71
+ for (let i = bEnd - 1; i >= bStart; i--) {
72
+ const key = b[i];
73
+ let entry = this.#cache.get(key);
74
+ if (!entry) {
75
+ const made = this.#makeEntry(key, items[i], i);
76
+ entry = made.entry;
77
+ insertEntry(parent, entry, cursor, made.rendered);
78
+ this.#cache.set(key, entry);
79
+ } else if (entry.end.nextSibling !== cursor) moveEntry(parent, entry, cursor);
80
+ cursor = entry.start;
81
+ }
82
+ this.#order = [...b];
83
+ }
84
+ #makeEntry(key, item, index) {
85
+ let rendered = null;
86
+ let dispose;
87
+ untracked(() => {
88
+ dispose = effectScope(() => {
89
+ rendered = this.children(item, index);
90
+ });
91
+ });
92
+ return {
93
+ entry: {
94
+ start: document.createComment(`[${key}]`),
95
+ end: document.createComment(`[/${key}]`),
96
+ dispose
97
+ },
98
+ rendered
99
+ };
100
+ }
101
+ };
102
+ function insertEntry(parent, entry, before, rendered) {
103
+ parent.insertBefore(entry.start, before);
104
+ if (rendered) parent.insertBefore(rendered, before);
105
+ parent.insertBefore(entry.end, before);
106
+ }
107
+ function cleanEntry(entry) {
108
+ let node = entry.start.nextSibling;
109
+ while (node && node !== entry.end) {
110
+ const next = node.nextSibling;
111
+ if (node instanceof Element) disposeElement(node);
112
+ node = next;
113
+ }
114
+ entry.dispose();
115
+ }
116
+ function removeEntry(entry) {
117
+ let node = entry.start.nextSibling;
118
+ while (node && node !== entry.end) {
119
+ const next = node.nextSibling;
120
+ if (node instanceof Element) disposeElement(node);
121
+ node = next;
122
+ }
123
+ entry.dispose();
124
+ const range = document.createRange();
125
+ range.setStartBefore(entry.start);
126
+ range.setEndAfter(entry.end);
127
+ range.deleteContents();
128
+ }
129
+ function moveEntry(parent, entry, before) {
130
+ const range = document.createRange();
131
+ range.setStartBefore(entry.start);
132
+ range.setEndAfter(entry.end);
133
+ parent.insertBefore(range.extractContents(), before);
134
+ }
135
+ //#endregion
136
+ export { For };
@@ -1,24 +1,24 @@
1
1
  //#region src/signals/lib.d.ts
2
- declare const $signal: unique symbol;
3
- declare const $computed: unique symbol;
4
- declare const $effect: unique symbol;
5
- declare const $effectScope: unique symbol;
2
+ declare const SIGNAL: unique symbol;
3
+ declare const COMPUTED: unique symbol;
4
+ declare const EFFECT: unique symbol;
5
+ declare const EFFECT_SCOPE: unique symbol;
6
6
  /**
7
7
  * Returns `true` if `fn` is a signal handle created by {@link signal}.
8
8
  *
9
- * Relies on `$signal` matching the internal `signalOper` function name.
9
+ * Relies on the SIGNAL symbol.
10
10
  */
11
11
  declare function isSignal(fn: unknown): boolean;
12
12
  /**
13
13
  * Returns `true` if `fn` is a computed handle created by {@link computed}.
14
14
  *
15
- * Relies on `$computed` matching the internal `computedOper` function name.
15
+ * Relies on the COMPUTED symbol.
16
16
  */
17
17
  declare function isComputed(fn: unknown): boolean;
18
18
  /**
19
19
  * Returns `true` if `fn` is an effect cleanup handle created by {@link effect}.
20
20
  *
21
- * Relies on the $effect symbol branding.
21
+ * Relies on the EFFECT symbol.
22
22
  */
23
23
  declare function isEffect(fn: unknown): boolean;
24
24
  /**
@@ -255,11 +255,61 @@ type Signal<T> = Updater<T> & Computed<T>;
255
255
  * ```
256
256
  */
257
257
  declare function reactive<This extends object, Value>(source?: (self: This) => Signal<Value>): (_target: unknown, context: ClassFieldDecoratorContext<This, Value>) => (this: This, initialValue: Value) => Value;
258
+ /**
259
+ * A value that may be static or reactive. Accepts a plain `T` or a
260
+ * zero-arg getter (`() => T`) — typically a `signal` or `computed`.
261
+ *
262
+ * Used across the library anywhere a prop or attribute may be bound to
263
+ * reactive state. Resolve with {@link resolve}, detect with {@link isReactive}.
264
+ *
265
+ * @template T — the value type.
266
+ *
267
+ * @example
268
+ * ```ts
269
+ * import { signal, computed } from "elements-kit/signals";
270
+ *
271
+ * const count = signal(0);
272
+ * const double = computed(() => count() * 2);
273
+ *
274
+ * const a: MaybeReactive<number> = 5; // static
275
+ * const b: MaybeReactive<number> = count; // signal (getter)
276
+ * const c: MaybeReactive<number> = double; // computed (getter)
277
+ * ```
278
+ */
258
279
  type MaybeReactive<T> = T | Computed<T>;
259
280
  /**
260
- * Resolves a MaybeReactive<T> to its current value.
261
- * If the input is a function, calls it; otherwise returns as-is.
281
+ * Resolve a {@link MaybeReactive} to its current value. Calls the getter
282
+ * when reactive; returns the value as-is when static.
283
+ *
284
+ * @example
285
+ * ```ts
286
+ * resolve(5); // 5
287
+ * resolve(() => count()); // current count value
288
+ * ```
262
289
  */
263
290
  declare function resolve<T>(value: MaybeReactive<T>): T;
291
+ /**
292
+ * Turn a reactive-props object into a bag of per-key getters. Callers may
293
+ * pass values or reactive sources (`signal`, `computed`); reading
294
+ * `props.name()` inside an effect or JSX getter subscribes to whatever
295
+ * drives it. Static values become stable thunks, signals and computed pass
296
+ * through unchanged — so identity is preserved (`props.name === props.name`).
297
+ *
298
+ * @example
299
+ * ```tsx
300
+ * import { resolveProps } from "elements-kit/signals";
301
+ *
302
+ * function Greeting(raw: MaybeReactiveProps<{ name: string; excited?: boolean }>) {
303
+ * const props = resolveProps(raw);
304
+ * return (
305
+ * <p>
306
+ * Hello, {props.name}
307
+ * {() => (props.excited() ? "!" : ".")}
308
+ * </p>
309
+ * );
310
+ * }
311
+ * ```
312
+ */
313
+ declare function resolveProps<P extends object>(raw: { [K in keyof P]: MaybeReactive<P[K]> }): { readonly [K in keyof P]: Computed<P[K]> };
264
314
  //#endregion
265
- export { untracked as C, trigger as S, isEffect as _, isReactive as a, onCleanup as b, $computed as c, $signal as d, batch as f, isComputed as g, effectScope as h, Updater as i, $effect as l, effect as m, MaybeReactive as n, reactive as o, computed as p, Signal as r, resolve as s, Computed as t, $effectScope as u, isEffectScope as v, signal as x, isSignal as y };
315
+ export { trigger as C, signal as S, isComputed as _, isReactive as a, isSignal as b, resolveProps as c, EFFECT_SCOPE as d, SIGNAL as f, effectScope as g, effect as h, Updater as i, COMPUTED as l, computed as m, MaybeReactive as n, reactive as o, batch as p, Signal as r, resolve as s, Computed as t, EFFECT as u, isEffect as v, untracked as w, onCleanup as x, isEffectScope as y };
package/dist/index.d.mts CHANGED
@@ -1,36 +1 @@
1
- //#region src/jsx-runtime/for.d.ts
2
- type KeyFn<T> = (item: T, index: number) => string | number;
3
- type RenderFn<T> = (item: T, index: number) => Element | DocumentFragment | null;
4
- /**
5
- * Keyed list renderer. Reconciles a reactive array into the DOM using a key
6
- * function to match existing nodes — minimising create/destroy churn.
7
- *
8
- * Reconciliation strategy (inspired by udomdiff / dom-expressions):
9
- * 1. Remove stale entries (keys absent from the new array).
10
- * 2. Skip unchanged common prefix and suffix.
11
- * 3. Pure-append fast path when the surviving old range is empty.
12
- * 4. Backward scan of the middle region — move or create entries so that
13
- * each entry's range lands immediately before the already-correct cursor.
14
- *
15
- * @example
16
- * ```tsx
17
- * <For each={visibleTodos} by={(todo) => todo.id}>
18
- * {(todo) => <li>{todo.text}</li>}
19
- * </For>
20
- * ```
21
- *
22
- * Props:
23
- * each — reactive getter returning the array (e.g. a `computed`)
24
- * by — extracts a stable key per item (default: index)
25
- * children — render function called once per new key
26
- */
27
- declare class For<T = unknown> {
28
- #private;
29
- get each(): T[];
30
- set each(v: T[]);
31
- by: KeyFn<T>;
32
- children: RenderFn<T>;
33
- render(): DocumentFragment;
34
- }
35
- //#endregion
36
- export { For };
1
+ export { };
package/dist/index.mjs CHANGED
@@ -1,144 +1 @@
1
- import { n as disposeElement } from "./element-C_4VbkvQ.mjs";
2
- import { _ as signal, g as onCleanup, u as effect, v as trigger, y as untracked } from "./signals-BHmWX6ox.mjs";
3
- //#region src/jsx-runtime/for.ts
4
- /**
5
- * Keyed list renderer. Reconciles a reactive array into the DOM using a key
6
- * function to match existing nodes — minimising create/destroy churn.
7
- *
8
- * Reconciliation strategy (inspired by udomdiff / dom-expressions):
9
- * 1. Remove stale entries (keys absent from the new array).
10
- * 2. Skip unchanged common prefix and suffix.
11
- * 3. Pure-append fast path when the surviving old range is empty.
12
- * 4. Backward scan of the middle region — move or create entries so that
13
- * each entry's range lands immediately before the already-correct cursor.
14
- *
15
- * @example
16
- * ```tsx
17
- * <For each={visibleTodos} by={(todo) => todo.id}>
18
- * {(todo) => <li>{todo.text}</li>}
19
- * </For>
20
- * ```
21
- *
22
- * Props:
23
- * each — reactive getter returning the array (e.g. a `computed`)
24
- * by — extracts a stable key per item (default: index)
25
- * children — render function called once per new key
26
- */
27
- var For = class {
28
- #each = signal([]);
29
- get each() {
30
- return this.#each();
31
- }
32
- set each(v) {
33
- if (v === untracked(this.#each)) {
34
- trigger(this.#each);
35
- return;
36
- }
37
- this.#each(v);
38
- }
39
- by = (_, i) => i;
40
- children = () => null;
41
- #start = document.createComment("<For>");
42
- #end = document.createComment("</For>");
43
- #cache = /* @__PURE__ */ new Map();
44
- /** Keys in current DOM order — needed for prefix/suffix optimisation. */
45
- #order = [];
46
- render() {
47
- const fragment = document.createDocumentFragment();
48
- fragment.appendChild(this.#start);
49
- fragment.appendChild(this.#end);
50
- effect(() => this.#reconcile());
51
- onCleanup(() => {
52
- for (const entry of this.#cache.values()) cleanEntry(entry);
53
- this.#cache.clear();
54
- this.#order = [];
55
- });
56
- return fragment;
57
- }
58
- #reconcile() {
59
- const parent = this.#start.parentNode;
60
- if (!parent) return;
61
- const items = this.each;
62
- const b = items.map((item, i) => this.by(item, i));
63
- const bSet = new Set(b);
64
- for (const [key, entry] of this.#cache) if (!bSet.has(key)) {
65
- removeEntry(entry);
66
- this.#cache.delete(key);
67
- }
68
- const a = this.#order.filter((k) => this.#cache.has(k));
69
- let aStart = 0, aEnd = a.length;
70
- let bStart = 0, bEnd = b.length;
71
- while (aStart < aEnd && bStart < bEnd && a[aStart] === b[bStart]) {
72
- aStart++;
73
- bStart++;
74
- }
75
- while (aEnd > aStart && bEnd > bStart && a[aEnd - 1] === b[bEnd - 1]) {
76
- aEnd--;
77
- bEnd--;
78
- }
79
- const after = bEnd < b.length ? this.#cache.get(b[bEnd]).start : this.#end;
80
- if (aStart === aEnd) {
81
- let cursor = after;
82
- for (let i = bEnd - 1; i >= bStart; i--) {
83
- const key = b[i];
84
- const entry = this.#makeEntry(key);
85
- insertEntry(parent, entry, cursor, untracked(() => this.children(items[i], i)));
86
- this.#cache.set(key, entry);
87
- cursor = entry.start;
88
- }
89
- this.#order = [...b];
90
- return;
91
- }
92
- let cursor = after;
93
- for (let i = bEnd - 1; i >= bStart; i--) {
94
- const key = b[i];
95
- let entry = this.#cache.get(key);
96
- if (!entry) {
97
- entry = this.#makeEntry(key);
98
- insertEntry(parent, entry, cursor, untracked(() => this.children(items[i], i)));
99
- this.#cache.set(key, entry);
100
- } else if (entry.end.nextSibling !== cursor) moveEntry(parent, entry, cursor);
101
- cursor = entry.start;
102
- }
103
- this.#order = [...b];
104
- }
105
- #makeEntry(key) {
106
- return {
107
- start: document.createComment(`[${key}]`),
108
- end: document.createComment(`[/${key}]`)
109
- };
110
- }
111
- };
112
- function insertEntry(parent, entry, before, rendered) {
113
- parent.insertBefore(entry.start, before);
114
- if (rendered) parent.insertBefore(rendered, before);
115
- parent.insertBefore(entry.end, before);
116
- }
117
- function cleanEntry(entry) {
118
- let node = entry.start.nextSibling;
119
- while (node && node !== entry.end) {
120
- const next = node.nextSibling;
121
- if (node instanceof Element) disposeElement(node);
122
- node = next;
123
- }
124
- }
125
- function removeEntry(entry) {
126
- let node = entry.start.nextSibling;
127
- while (node && node !== entry.end) {
128
- const next = node.nextSibling;
129
- if (node instanceof Element) disposeElement(node);
130
- node = next;
131
- }
132
- const range = document.createRange();
133
- range.setStartBefore(entry.start);
134
- range.setEndAfter(entry.end);
135
- range.deleteContents();
136
- }
137
- function moveEntry(parent, entry, before) {
138
- const range = document.createRange();
139
- range.setStartBefore(entry.start);
140
- range.setEndAfter(entry.end);
141
- parent.insertBefore(range.extractContents(), before);
142
- }
143
- //#endregion
144
- export { For };
1
+ export {};