@vertz/ui 0.2.10 → 0.2.12

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.
@@ -6,10 +6,10 @@ import {
6
6
  globalCss,
7
7
  s,
8
8
  variants
9
- } from "../shared/chunk-qacth5ah.js";
10
- import"../shared/chunk-ryb49346.js";
9
+ } from "../shared/chunk-kg898f92.js";
10
+ import"../shared/chunk-662f9zrb.js";
11
11
  import"../shared/chunk-g4rch80a.js";
12
- import"../shared/chunk-hrd0mft1.js";
12
+ import"../shared/chunk-2rs8a26p.js";
13
13
  import"../shared/chunk-prj7nm08.js";
14
14
  export {
15
15
  variants,
@@ -3,8 +3,8 @@ import {
3
3
  form,
4
4
  formDataToObject,
5
5
  validate
6
- } from "../shared/chunk-q6cpe5k7.js";
7
- import"../shared/chunk-hrd0mft1.js";
6
+ } from "../shared/chunk-55tgkc7s.js";
7
+ import"../shared/chunk-2rs8a26p.js";
8
8
  export {
9
9
  validate,
10
10
  formDataToObject,
package/dist/index.d.ts CHANGED
@@ -148,6 +148,20 @@ declare function ErrorBoundary(props: ErrorBoundaryProps): Node;
148
148
  * ```
149
149
  */
150
150
  declare function onMount2(callback: () => (() => void) | void): void;
151
+ interface ListTransitionProps<T> {
152
+ each: T[];
153
+ keyFn: (item: T, index: number) => string | number;
154
+ children: (item: T) => HTMLElement | SVGElement;
155
+ }
156
+ /**
157
+ * ListTransition component for animated list item enter/exit.
158
+ * New items get `data-presence="enter"`, removed items get `data-presence="exit"`
159
+ * with DOM removal deferred until CSS animation completes.
160
+ *
161
+ * Props are accessed as getters (not destructured) so the compiler-generated
162
+ * reactive getters are tracked by the underlying domEffect.
163
+ */
164
+ declare function ListTransition<T>(props: ListTransitionProps<T>): Node;
151
165
  interface PresenceProps {
152
166
  when: boolean;
153
167
  children: () => HTMLElement;
@@ -735,7 +749,7 @@ interface MountHandle {
735
749
  * @param options - Mount options (theme, styles, onMount, etc.)
736
750
  * @returns MountHandle with unmount function and root element
737
751
  */
738
- declare function mount<AppFn extends () => Element>(app: AppFn, selector: string | HTMLElement, options?: MountOptions): MountHandle;
752
+ declare function mount<AppFn extends () => Element | DocumentFragment>(app: AppFn, selector: string | HTMLElement, options?: MountOptions): MountHandle;
739
753
  import { QueryDescriptor as QueryDescriptor2 } from "@vertz/fetch";
740
754
  import { isQueryDescriptor } from "@vertz/fetch";
741
755
  /**
@@ -1174,8 +1188,9 @@ declare class DisposalScopeError extends Error {
1174
1188
  declare function batch(fn: () => void): void;
1175
1189
  /**
1176
1190
  * Create a reactive signal with an initial value.
1191
+ * Optional key is used by HMR to match signals by name across re-mounts.
1177
1192
  */
1178
- declare function signal<T>(initial: T): Signal<T>;
1193
+ declare function signal<T>(initial: T, key?: string): Signal<T>;
1179
1194
  /**
1180
1195
  * Create a computed (derived) reactive value.
1181
1196
  * The function is lazily evaluated and cached.
@@ -1284,4 +1299,4 @@ declare class EntityStore {
1284
1299
  * ```
1285
1300
  */
1286
1301
  declare function createTestStore(data: Record<string, Record<string, unknown>>): EntityStore;
1287
- export { zoomOut, zoomIn, variants, validate, useSearchParams, useRouter, useParams, useDialogStack, useContext, untrack, slideOutToTop, slideOutToRight, slideOutToLeft, slideOutToBottom, slideInFromTop, slideInFromRight, slideInFromLeft, slideInFromBottom, signal, setAdapter, s, resolveChildren, resetInjectedStyles, ref, queryMatch, query, parseSearchParams, palettes, onMount2 as onMount, mount, keyframes, isRenderNode, isQueryDescriptor, injectCSS, hydrate, globalCss, getInjectedCSS, getAdapter, formDataToObject, form, fadeOut, fadeIn, defineTheme, defineRoutes, css, createTestStore, createRouter, createLink, createFieldState, createDialogStack, createDOMAdapter, createContext, computed, compileTheme, children, batch, accordionUp, accordionDown, __staticText, __exitChildren, __enterChildren, __element, __append, VariantsConfig, VariantProps, VariantFunction, ValidationResult, UnwrapSignals, TypedRoutes, TypedRouter, ThemeProviderProps, ThemeProvider, ThemeInput, Theme, SuspenseProps, Suspense, StyleValue, StyleEntry, Signal, SerializedStore, SearchParamSchema, SdkMethodWithMeta, SdkMethod, RouterViewProps, RouterView, RouterOptions, RouterContext, Router, RoutePaths, RouteMatch, RouteDefinitionMap, RouteConfig, RenderText, RenderNode, RenderElement, RenderAdapter, Ref, ReadonlySignal, RawDeclaration, RENDER_NODE_BRAND, QueryResult, QueryOptions, QueryMatchHandlers, QueryDescriptor2 as QueryDescriptor, PresenceProps, Presence, PathWithParams, OutletContextValue, OutletContext, Outlet, NavigateOptions, MountOptions, MountHandle, MatchedRoute, LoaderData, LinkProps, LinkFactoryOptions, InferRouteMap, GlobalCSSOutput, GlobalCSSInput, FormSchema, FormOptions, FormInstance, FormDataOptions, FieldState, ExtractParams, ErrorBoundaryProps, ErrorBoundary, EntityStoreOptions, EntityStore, DisposeFn, DisposalScopeError, DialogStackContext, DialogStack, DialogHandle, DialogDismissedError, DialogComponent, Context, Computed, ComponentRegistry, ComponentLoader, ComponentFunction, CompiledTheme, CompiledRoute, ColorPalette, ChildrenAccessor, ChildValue, CacheStore, CSSOutput, CSSInput, ANIMATION_EASING, ANIMATION_DURATION };
1302
+ export { zoomOut, zoomIn, variants, validate, useSearchParams, useRouter, useParams, useDialogStack, useContext, untrack, slideOutToTop, slideOutToRight, slideOutToLeft, slideOutToBottom, slideInFromTop, slideInFromRight, slideInFromLeft, slideInFromBottom, signal, setAdapter, s, resolveChildren, resetInjectedStyles, ref, queryMatch, query, parseSearchParams, palettes, onMount2 as onMount, mount, keyframes, isRenderNode, isQueryDescriptor, injectCSS, hydrate, globalCss, getInjectedCSS, getAdapter, formDataToObject, form, fadeOut, fadeIn, defineTheme, defineRoutes, css, createTestStore, createRouter, createLink, createFieldState, createDialogStack, createDOMAdapter, createContext, computed, compileTheme, children, batch, accordionUp, accordionDown, __staticText, __exitChildren, __enterChildren, __element, __append, VariantsConfig, VariantProps, VariantFunction, ValidationResult, UnwrapSignals, TypedRoutes, TypedRouter, ThemeProviderProps, ThemeProvider, ThemeInput, Theme, SuspenseProps, Suspense, StyleValue, StyleEntry, Signal, SerializedStore, SearchParamSchema, SdkMethodWithMeta, SdkMethod, RouterViewProps, RouterView, RouterOptions, RouterContext, Router, RoutePaths, RouteMatch, RouteDefinitionMap, RouteConfig, RenderText, RenderNode, RenderElement, RenderAdapter, Ref, ReadonlySignal, RawDeclaration, RENDER_NODE_BRAND, QueryResult, QueryOptions, QueryMatchHandlers, QueryDescriptor2 as QueryDescriptor, PresenceProps, Presence, PathWithParams, OutletContextValue, OutletContext, Outlet, NavigateOptions, MountOptions, MountHandle, MatchedRoute, LoaderData, ListTransitionProps, ListTransition, LinkProps, LinkFactoryOptions, InferRouteMap, GlobalCSSOutput, GlobalCSSInput, FormSchema, FormOptions, FormInstance, FormDataOptions, FieldState, ExtractParams, ErrorBoundaryProps, ErrorBoundary, EntityStoreOptions, EntityStore, DisposeFn, DisposalScopeError, DialogStackContext, DialogStack, DialogHandle, DialogDismissedError, DialogComponent, Context, Computed, ComponentRegistry, ComponentLoader, ComponentFunction, CompiledTheme, CompiledRoute, ColorPalette, ChildrenAccessor, ChildValue, CacheStore, CSSOutput, CSSInput, ANIMATION_EASING, ANIMATION_DURATION };
package/dist/index.js CHANGED
@@ -20,7 +20,7 @@ import {
20
20
  slideOutToTop,
21
21
  zoomIn,
22
22
  zoomOut
23
- } from "./shared/chunk-n91rwj2r.js";
23
+ } from "./shared/chunk-bjcpcq5j.js";
24
24
  import {
25
25
  Outlet,
26
26
  OutletContext,
@@ -31,11 +31,11 @@ import {
31
31
  useParams,
32
32
  useRouter,
33
33
  useSearchParams
34
- } from "./shared/chunk-0xcmwgdb.js";
35
- import"./shared/chunk-v3yyf79g.js";
34
+ } from "./shared/chunk-18jzqefd.js";
35
+ import"./shared/chunk-wn4gv1qd.js";
36
36
  import {
37
37
  createRouter
38
- } from "./shared/chunk-ka5ked7n.js";
38
+ } from "./shared/chunk-g1gf16fz.js";
39
39
  import {
40
40
  defineRoutes
41
41
  } from "./shared/chunk-9e92w0wt.js";
@@ -44,11 +44,11 @@ import {
44
44
  form,
45
45
  formDataToObject,
46
46
  validate
47
- } from "./shared/chunk-q6cpe5k7.js";
47
+ } from "./shared/chunk-55tgkc7s.js";
48
48
  import {
49
49
  query,
50
50
  queryMatch
51
- } from "./shared/chunk-hh0dhmb4.js";
51
+ } from "./shared/chunk-9k2z3jfx.js";
52
52
  import"./shared/chunk-jrtrk5z4.js";
53
53
  import {
54
54
  ThemeProvider,
@@ -63,7 +63,7 @@ import {
63
63
  resolveChildren,
64
64
  s,
65
65
  variants
66
- } from "./shared/chunk-qacth5ah.js";
66
+ } from "./shared/chunk-kg898f92.js";
67
67
  import {
68
68
  __append,
69
69
  __element,
@@ -72,7 +72,7 @@ import {
72
72
  __staticText,
73
73
  endHydration,
74
74
  startHydration
75
- } from "./shared/chunk-ryb49346.js";
75
+ } from "./shared/chunk-662f9zrb.js";
76
76
  import {
77
77
  RENDER_NODE_BRAND,
78
78
  createDOMAdapter,
@@ -95,7 +95,7 @@ import {
95
95
  signal,
96
96
  untrack,
97
97
  useContext
98
- } from "./shared/chunk-hrd0mft1.js";
98
+ } from "./shared/chunk-2rs8a26p.js";
99
99
  import"./shared/chunk-prj7nm08.js";
100
100
  // src/component/error-boundary-context.ts
101
101
  var handlerStack = [];
@@ -174,6 +174,179 @@ function onMount(callback) {
174
174
  }
175
175
  }
176
176
  }
177
+ // src/dom/list-transition.ts
178
+ function createItemProxy(itemSignal) {
179
+ if (typeof itemSignal.peek() !== "object" || itemSignal.peek() == null) {
180
+ return itemSignal.peek();
181
+ }
182
+ return new Proxy({}, {
183
+ get(_target, prop, receiver) {
184
+ const current = itemSignal.value;
185
+ if (current == null)
186
+ return;
187
+ const value = Reflect.get(current, prop, receiver);
188
+ if (typeof value === "function") {
189
+ return value.bind(current);
190
+ }
191
+ return value;
192
+ },
193
+ has(_target, prop) {
194
+ const current = itemSignal.value;
195
+ if (current == null)
196
+ return false;
197
+ return Reflect.has(current, prop);
198
+ },
199
+ ownKeys() {
200
+ const current = itemSignal.value;
201
+ if (current == null)
202
+ return [];
203
+ return Reflect.ownKeys(current);
204
+ },
205
+ getOwnPropertyDescriptor(_target, prop) {
206
+ const current = itemSignal.value;
207
+ if (current == null)
208
+ return;
209
+ return Reflect.getOwnPropertyDescriptor(current, prop);
210
+ }
211
+ });
212
+ }
213
+ function listTransition(startMarker, endMarker, items, keyFn, renderFn) {
214
+ const getItems = typeof items === "function" ? items : () => items.value;
215
+ const nodeMap = new Map;
216
+ const scopeMap = new Map;
217
+ const itemSignalMap = new Map;
218
+ const exitingNodes = new Set;
219
+ const exitingKeyMap = new Map;
220
+ const keyGeneration = new Map;
221
+ let isFirstRun = true;
222
+ const outerScope = pushScope();
223
+ try {
224
+ domEffect(() => {
225
+ const newItems = getItems() ?? [];
226
+ const newKeySet = new Set(newItems.map((item, i) => keyFn(item, i)));
227
+ if (isFirstRun) {
228
+ isFirstRun = false;
229
+ for (const [i, item] of newItems.entries()) {
230
+ const key = keyFn(item, i);
231
+ const itemSig = signal(item);
232
+ const proxy = createItemProxy(itemSig);
233
+ const scope = pushScope();
234
+ const node = renderFn(proxy);
235
+ popScope();
236
+ nodeMap.set(key, node);
237
+ scopeMap.set(key, scope);
238
+ itemSignalMap.set(key, itemSig);
239
+ endMarker.parentNode?.insertBefore(node, endMarker);
240
+ }
241
+ return;
242
+ }
243
+ for (const [key, node] of nodeMap) {
244
+ if (!newKeySet.has(key)) {
245
+ const scope = scopeMap.get(key);
246
+ if (scope) {
247
+ runCleanups(scope);
248
+ scopeMap.delete(key);
249
+ }
250
+ nodeMap.delete(key);
251
+ itemSignalMap.delete(key);
252
+ const gen = (keyGeneration.get(key) ?? 0) + 1;
253
+ keyGeneration.set(key, gen);
254
+ exitingNodes.add(node);
255
+ exitingKeyMap.set(key, node);
256
+ node.setAttribute("data-presence", "exit");
257
+ onAnimationsComplete(node, () => {
258
+ if (keyGeneration.get(key) === gen) {
259
+ node.parentNode?.removeChild(node);
260
+ exitingNodes.delete(node);
261
+ exitingKeyMap.delete(key);
262
+ }
263
+ });
264
+ }
265
+ }
266
+ const desiredNodes = [];
267
+ const enterNodes = [];
268
+ for (const [i, item] of newItems.entries()) {
269
+ const key = keyFn(item, i);
270
+ let node = nodeMap.get(key);
271
+ if (!node) {
272
+ const oldExiting = exitingKeyMap.get(key);
273
+ if (oldExiting) {
274
+ oldExiting.parentNode?.removeChild(oldExiting);
275
+ exitingNodes.delete(oldExiting);
276
+ exitingKeyMap.delete(key);
277
+ }
278
+ const gen = (keyGeneration.get(key) ?? 0) + 1;
279
+ keyGeneration.set(key, gen);
280
+ const itemSig = signal(item);
281
+ const proxy = createItemProxy(itemSig);
282
+ const scope = pushScope();
283
+ node = renderFn(proxy);
284
+ popScope();
285
+ nodeMap.set(key, node);
286
+ scopeMap.set(key, scope);
287
+ itemSignalMap.set(key, itemSig);
288
+ node.setAttribute("data-presence", "enter");
289
+ enterNodes.push({ node, key });
290
+ } else {
291
+ const itemSig = itemSignalMap.get(key);
292
+ if (itemSig) {
293
+ itemSig.value = item;
294
+ }
295
+ }
296
+ desiredNodes.push(node);
297
+ }
298
+ const parent = startMarker.parentNode;
299
+ if (parent) {
300
+ let cursor = startMarker.nextSibling;
301
+ for (const desired of desiredNodes) {
302
+ while (cursor && cursor !== endMarker && exitingNodes.has(cursor)) {
303
+ cursor = cursor.nextSibling;
304
+ }
305
+ if (cursor === desired) {
306
+ cursor = cursor.nextSibling;
307
+ } else {
308
+ parent.insertBefore(desired, cursor);
309
+ }
310
+ }
311
+ }
312
+ for (const { node: enterNode, key } of enterNodes) {
313
+ onAnimationsComplete(enterNode, () => {
314
+ if (nodeMap.get(key) === enterNode) {
315
+ enterNode.removeAttribute("data-presence");
316
+ }
317
+ });
318
+ }
319
+ });
320
+ } finally {
321
+ popScope();
322
+ }
323
+ const dispose = () => {
324
+ for (const scope of scopeMap.values()) {
325
+ runCleanups(scope);
326
+ }
327
+ scopeMap.clear();
328
+ for (const node of exitingNodes) {
329
+ node.parentNode?.removeChild(node);
330
+ }
331
+ exitingNodes.clear();
332
+ exitingKeyMap.clear();
333
+ runCleanups(outerScope);
334
+ };
335
+ _tryOnCleanup(dispose);
336
+ return dispose;
337
+ }
338
+
339
+ // src/component/list-transition.ts
340
+ function ListTransition(props) {
341
+ const startMarker = document.createComment("lt-start");
342
+ const endMarker = document.createComment("lt-end");
343
+ const fragment = document.createDocumentFragment();
344
+ fragment.appendChild(startMarker);
345
+ fragment.appendChild(endMarker);
346
+ const dispose = listTransition(startMarker, endMarker, () => props.each, props.keyFn, props.children);
347
+ _tryOnCleanup(dispose);
348
+ return Object.assign(fragment, { dispose });
349
+ }
177
350
  // src/component/presence.ts
178
351
  function Presence(props) {
179
352
  const anchor = document.createComment("presence");
@@ -293,7 +466,7 @@ function Suspense(props) {
293
466
  }
294
467
  }
295
468
  // src/dialog/dialog-stack.ts
296
- var DialogStackContext = createContext();
469
+ var DialogStackContext = createContext(undefined, "@vertz/ui::DialogStackContext");
297
470
  function useDialogStack() {
298
471
  const stack = useContext(DialogStackContext);
299
472
  if (!stack) {
@@ -432,6 +605,11 @@ function createDialogStack(container) {
432
605
  }
433
606
  }
434
607
  // src/mount.ts
608
+ var MOUNTED_KEY = Symbol.for("vertz:mounted-roots");
609
+ var _global = globalThis;
610
+ if (!_global[MOUNTED_KEY])
611
+ _global[MOUNTED_KEY] = new WeakMap;
612
+ var mountedRoots = _global[MOUNTED_KEY];
435
613
  function mount(app, selector, options) {
436
614
  if (typeof selector !== "string" && !(selector instanceof HTMLElement)) {
437
615
  throw new Error(`mount(): selector must be a string or HTMLElement, got ${typeof selector}`);
@@ -440,6 +618,9 @@ function mount(app, selector, options) {
440
618
  if (!root) {
441
619
  throw new Error(`mount(): root element "${selector}" not found`);
442
620
  }
621
+ const existingHandle = mountedRoots.get(root);
622
+ if (existingHandle)
623
+ return existingHandle;
443
624
  if (options?.theme) {
444
625
  const { css: css2 } = compileTheme(options.theme);
445
626
  injectCSS(css2);
@@ -457,13 +638,16 @@ function mount(app, selector, options) {
457
638
  endHydration();
458
639
  popScope();
459
640
  options?.onMount?.(root);
460
- return {
641
+ const handle2 = {
461
642
  unmount: () => {
643
+ mountedRoots.delete(root);
462
644
  runCleanups(scope2);
463
645
  root.textContent = "";
464
646
  },
465
647
  root
466
648
  };
649
+ mountedRoots.set(root, handle2);
650
+ return handle2;
467
651
  } catch (e) {
468
652
  endHydration();
469
653
  popScope();
@@ -479,13 +663,16 @@ function mount(app, selector, options) {
479
663
  root.appendChild(appElement);
480
664
  popScope();
481
665
  options?.onMount?.(root);
482
- return {
666
+ const handle = {
483
667
  unmount: () => {
668
+ mountedRoots.delete(root);
484
669
  runCleanups(scope);
485
670
  root.textContent = "";
486
671
  },
487
672
  root
488
673
  };
674
+ mountedRoots.set(root, handle);
675
+ return handle;
489
676
  }
490
677
  // src/store/merge.ts
491
678
  function shallowMerge(existing, incoming) {
@@ -773,6 +960,7 @@ export {
773
960
  Presence,
774
961
  OutletContext,
775
962
  Outlet,
963
+ ListTransition,
776
964
  ErrorBoundary,
777
965
  EntityStore,
778
966
  DisposalScopeError,
@@ -195,7 +195,7 @@ declare function setAdapter(adapter: RenderAdapter | null): void;
195
195
  * If no animations are running, calls back immediately.
196
196
  * Respects prefers-reduced-motion by skipping the wait.
197
197
  */
198
- declare function onAnimationsComplete(el: HTMLElement, callback: () => void): void;
198
+ declare function onAnimationsComplete(el: Element, callback: () => void): void;
199
199
  /**
200
200
  * Create a reactive attribute binding.
201
201
  * When the value returned by `fn` changes, the attribute is updated.
@@ -340,6 +340,11 @@ declare function clearChildren(container: Node): void;
340
340
  * Efficiently updates a container's children when the items signal changes.
341
341
  * Reuses existing DOM nodes based on key identity — no virtual DOM.
342
342
  *
343
+ * Each item is wrapped in a reactive proxy backed by a signal. When the item
344
+ * at an existing key changes (e.g., after refetch), the signal updates and
345
+ * any reactive bindings inside the node (domEffect, __child) re-run
346
+ * automatically — without re-creating the DOM node.
347
+ *
343
348
  * Compiler output target for .map() / for-each expressions in JSX.
344
349
  *
345
350
  * @param container - The parent DOM element
package/dist/internals.js CHANGED
@@ -2,13 +2,13 @@ import {
2
2
  deserializeProps,
3
3
  onAnimationsComplete,
4
4
  resolveComponent
5
- } from "./shared/chunk-n91rwj2r.js";
5
+ } from "./shared/chunk-bjcpcq5j.js";
6
6
  import {
7
7
  __attr,
8
8
  __classList,
9
9
  __on,
10
10
  __show
11
- } from "./shared/chunk-v3yyf79g.js";
11
+ } from "./shared/chunk-wn4gv1qd.js";
12
12
  import {
13
13
  executeLoaders,
14
14
  matchPath,
@@ -17,7 +17,7 @@ import {
17
17
  import {
18
18
  MemoryCache,
19
19
  deriveKey
20
- } from "./shared/chunk-hh0dhmb4.js";
20
+ } from "./shared/chunk-9k2z3jfx.js";
21
21
  import"./shared/chunk-jrtrk5z4.js";
22
22
  import {
23
23
  ALIGNMENT_MAP,
@@ -38,7 +38,7 @@ import {
38
38
  SIZE_KEYWORDS,
39
39
  SPACING_SCALE,
40
40
  compileTheme
41
- } from "./shared/chunk-qacth5ah.js";
41
+ } from "./shared/chunk-kg898f92.js";
42
42
  import {
43
43
  __append,
44
44
  __child,
@@ -51,7 +51,7 @@ import {
51
51
  claimComment,
52
52
  claimText,
53
53
  getIsHydrating
54
- } from "./shared/chunk-ryb49346.js";
54
+ } from "./shared/chunk-662f9zrb.js";
55
55
  import {
56
56
  RENDER_NODE_BRAND,
57
57
  createDOMAdapter,
@@ -69,11 +69,27 @@ import {
69
69
  pushScope,
70
70
  runCleanups,
71
71
  setContextScope,
72
+ signal,
72
73
  startSignalCollection,
73
74
  stopSignalCollection
74
- } from "./shared/chunk-hrd0mft1.js";
75
+ } from "./shared/chunk-2rs8a26p.js";
75
76
  import"./shared/chunk-prj7nm08.js";
76
77
  // src/dom/conditional.ts
78
+ function normalizeNode(branchResult) {
79
+ if (branchResult == null || typeof branchResult === "boolean") {
80
+ return getAdapter().createComment("empty");
81
+ }
82
+ if (isRenderNode(branchResult)) {
83
+ if (branchResult.nodeType === 11) {
84
+ const wrap = getAdapter().createElement("span");
85
+ wrap.style.display = "contents";
86
+ wrap.appendChild(branchResult);
87
+ return wrap;
88
+ }
89
+ return branchResult;
90
+ }
91
+ return getAdapter().createTextNode(String(branchResult));
92
+ }
77
93
  function __conditional(condFn, trueFn, falseFn) {
78
94
  if (getIsHydrating()) {
79
95
  return hydrateConditional(condFn, trueFn, falseFn);
@@ -109,14 +125,7 @@ function hydrateConditional(condFn, trueFn, falseFn) {
109
125
  const branchResult = show ? trueFn() : falseFn();
110
126
  popScope();
111
127
  branchCleanups = scope;
112
- let newNode;
113
- if (branchResult == null || typeof branchResult === "boolean") {
114
- newNode = getAdapter().createComment("empty");
115
- } else if (isRenderNode(branchResult)) {
116
- newNode = branchResult;
117
- } else {
118
- newNode = getAdapter().createTextNode(String(branchResult));
119
- }
128
+ const newNode = normalizeNode(branchResult);
120
129
  if (currentNode?.parentNode) {
121
130
  currentNode.parentNode.replaceChild(newNode, currentNode);
122
131
  } else if (anchor.parentNode) {
@@ -147,14 +156,7 @@ function csrConditional(condFn, trueFn, falseFn) {
147
156
  const branchResult = show ? trueFn() : falseFn();
148
157
  popScope();
149
158
  branchCleanups = scope;
150
- let newNode;
151
- if (branchResult == null || typeof branchResult === "boolean") {
152
- newNode = getAdapter().createComment("empty");
153
- } else if (isRenderNode(branchResult)) {
154
- newNode = branchResult;
155
- } else {
156
- newNode = getAdapter().createTextNode(String(branchResult));
157
- }
159
+ const newNode = normalizeNode(branchResult);
158
160
  if (currentNode?.parentNode) {
159
161
  currentNode.parentNode.replaceChild(newNode, currentNode);
160
162
  } else if (anchor.parentNode) {
@@ -189,10 +191,46 @@ function clearChildren(container) {
189
191
  }
190
192
  }
191
193
  // src/dom/list.ts
194
+ function createItemProxy(itemSignal) {
195
+ if (typeof itemSignal.peek() !== "object" || itemSignal.peek() == null) {
196
+ return itemSignal.peek();
197
+ }
198
+ return new Proxy({}, {
199
+ get(_target, prop, receiver) {
200
+ const current = itemSignal.value;
201
+ if (current == null)
202
+ return;
203
+ const value = Reflect.get(current, prop, receiver);
204
+ if (typeof value === "function") {
205
+ return value.bind(current);
206
+ }
207
+ return value;
208
+ },
209
+ has(_target, prop) {
210
+ const current = itemSignal.value;
211
+ if (current == null)
212
+ return false;
213
+ return Reflect.has(current, prop);
214
+ },
215
+ ownKeys() {
216
+ const current = itemSignal.value;
217
+ if (current == null)
218
+ return [];
219
+ return Reflect.ownKeys(current);
220
+ },
221
+ getOwnPropertyDescriptor(_target, prop) {
222
+ const current = itemSignal.value;
223
+ if (current == null)
224
+ return;
225
+ return Reflect.getOwnPropertyDescriptor(current, prop);
226
+ }
227
+ });
228
+ }
192
229
  function __list(container, items, keyFn, renderFn) {
193
230
  const getItems = typeof items === "function" ? items : () => items.value;
194
231
  const nodeMap = new Map;
195
232
  const scopeMap = new Map;
233
+ const itemSignalMap = new Map;
196
234
  const isHydrationRun = getIsHydrating();
197
235
  const outerScope = pushScope();
198
236
  let isFirstRun = true;
@@ -202,11 +240,14 @@ function __list(container, items, keyFn, renderFn) {
202
240
  isFirstRun = false;
203
241
  for (const [i, item] of newItems.entries()) {
204
242
  const key = keyFn(item, i);
243
+ const itemSig = signal(item);
244
+ const proxy = createItemProxy(itemSig);
205
245
  const scope = pushScope();
206
- const node = renderFn(item);
246
+ const node = renderFn(proxy);
207
247
  popScope();
208
248
  nodeMap.set(key, node);
209
249
  scopeMap.set(key, scope);
250
+ itemSignalMap.set(key, itemSig);
210
251
  }
211
252
  return;
212
253
  }
@@ -221,6 +262,7 @@ function __list(container, items, keyFn, renderFn) {
221
262
  }
222
263
  node.parentNode?.removeChild(node);
223
264
  nodeMap.delete(key);
265
+ itemSignalMap.delete(key);
224
266
  }
225
267
  }
226
268
  const desiredNodes = [];
@@ -228,11 +270,19 @@ function __list(container, items, keyFn, renderFn) {
228
270
  const key = keyFn(item, i);
229
271
  let node = nodeMap.get(key);
230
272
  if (!node) {
273
+ const itemSig = signal(item);
274
+ const proxy = createItemProxy(itemSig);
231
275
  const scope = pushScope();
232
- node = renderFn(item);
276
+ node = renderFn(proxy);
233
277
  popScope();
234
278
  nodeMap.set(key, node);
235
279
  scopeMap.set(key, scope);
280
+ itemSignalMap.set(key, itemSig);
281
+ } else {
282
+ const itemSig = itemSignalMap.get(key);
283
+ if (itemSig) {
284
+ itemSig.value = item;
285
+ }
236
286
  }
237
287
  desiredNodes.push(node);
238
288
  }
@@ -3,7 +3,7 @@
3
3
  * to understand intrinsic element types and component types.
4
4
  */
5
5
  declare namespace JSX {
6
- type Element = HTMLElement | SVGElement;
6
+ type Element = HTMLElement | SVGElement | DocumentFragment;
7
7
  type JSXComponent = (props: Record<string, unknown>) => Element;
8
8
  interface HTMLAttributes {
9
9
  [key: string]: unknown;
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  query,
3
3
  queryMatch
4
- } from "../shared/chunk-hh0dhmb4.js";
4
+ } from "../shared/chunk-9k2z3jfx.js";
5
5
  import"../shared/chunk-jrtrk5z4.js";
6
6
  import"../shared/chunk-g4rch80a.js";
7
- import"../shared/chunk-hrd0mft1.js";
7
+ import"../shared/chunk-2rs8a26p.js";
8
8
 
9
9
  // src/query/public.ts
10
10
  import { isQueryDescriptor } from "@vertz/fetch";
@@ -8,18 +8,18 @@ import {
8
8
  useParams,
9
9
  useRouter,
10
10
  useSearchParams
11
- } from "../shared/chunk-0xcmwgdb.js";
12
- import"../shared/chunk-v3yyf79g.js";
11
+ } from "../shared/chunk-18jzqefd.js";
12
+ import"../shared/chunk-wn4gv1qd.js";
13
13
  import {
14
14
  createRouter
15
- } from "../shared/chunk-ka5ked7n.js";
15
+ } from "../shared/chunk-g1gf16fz.js";
16
16
  import {
17
17
  defineRoutes
18
18
  } from "../shared/chunk-9e92w0wt.js";
19
19
  import"../shared/chunk-jrtrk5z4.js";
20
- import"../shared/chunk-ryb49346.js";
20
+ import"../shared/chunk-662f9zrb.js";
21
21
  import"../shared/chunk-g4rch80a.js";
22
- import"../shared/chunk-hrd0mft1.js";
22
+ import"../shared/chunk-2rs8a26p.js";
23
23
  import"../shared/chunk-prj7nm08.js";
24
24
  export {
25
25
  useSearchParams,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  __classList,
3
3
  __on
4
- } from "./chunk-v3yyf79g.js";
4
+ } from "./chunk-wn4gv1qd.js";
5
5
  import {
6
6
  __append,
7
7
  __element,
@@ -9,7 +9,7 @@ import {
9
9
  __exitChildren,
10
10
  __staticText,
11
11
  getIsHydrating
12
- } from "./chunk-ryb49346.js";
12
+ } from "./chunk-662f9zrb.js";
13
13
  import {
14
14
  _tryOnCleanup,
15
15
  createContext,
@@ -20,7 +20,7 @@ import {
20
20
  signal,
21
21
  untrack,
22
22
  useContext
23
- } from "./chunk-hrd0mft1.js";
23
+ } from "./chunk-2rs8a26p.js";
24
24
 
25
25
  // src/router/link.ts
26
26
  var DANGEROUS_SCHEMES = ["javascript:", "data:", "vbscript:"];
@@ -90,7 +90,7 @@ function createLink(currentPath, navigate, factoryOptions) {
90
90
  }
91
91
 
92
92
  // src/router/router-context.ts
93
- var RouterContext = createContext();
93
+ var RouterContext = createContext(undefined, "@vertz/ui::RouterContext");
94
94
  function useRouter() {
95
95
  const router = useContext(RouterContext);
96
96
  if (!router) {
@@ -107,7 +107,7 @@ function useParams() {
107
107
  }
108
108
 
109
109
  // src/router/outlet.ts
110
- var OutletContext = createContext();
110
+ var OutletContext = createContext(undefined, "@vertz/ui::OutletContext");
111
111
  function Outlet() {
112
112
  const ctx = useContext(OutletContext);
113
113
  if (!ctx) {
@@ -220,7 +220,11 @@ function isSSR() {
220
220
  const check = typeof globalThis !== "undefined" && globalThis.__VERTZ_IS_SSR__;
221
221
  return typeof check === "function" ? check() : false;
222
222
  }
223
- var signalCollectorStack = [];
223
+ var COLLECTOR_KEY = Symbol.for("vertz:signal-collector-stack");
224
+ var _global = globalThis;
225
+ if (!_global[COLLECTOR_KEY])
226
+ _global[COLLECTOR_KEY] = [];
227
+ var signalCollectorStack = _global[COLLECTOR_KEY];
224
228
  function startSignalCollection() {
225
229
  signalCollectorStack.push([]);
226
230
  }
@@ -232,8 +236,10 @@ var nextId = 0;
232
236
  class SignalImpl {
233
237
  _value;
234
238
  _subscribers = new Set;
235
- constructor(initial) {
239
+ _hmrKey;
240
+ constructor(initial, key) {
236
241
  this._value = initial;
242
+ this._hmrKey = key;
237
243
  }
238
244
  get value() {
239
245
  const sub = getSubscriber();
@@ -267,8 +273,8 @@ class SignalImpl {
267
273
  });
268
274
  }
269
275
  }
270
- function signal(initial) {
271
- const s = new SignalImpl(initial);
276
+ function signal(initial, key) {
277
+ const s = new SignalImpl(initial, key);
272
278
  const collector = signalCollectorStack[signalCollectorStack.length - 1];
273
279
  if (collector) {
274
280
  collector.push(s);
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  computed,
3
3
  signal
4
- } from "./chunk-hrd0mft1.js";
4
+ } from "./chunk-2rs8a26p.js";
5
5
 
6
6
  // src/form/field-state.ts
7
7
  function createFieldState(_name, initialValue) {
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-g4rch80a.js";
5
5
  import {
6
6
  domEffect
7
- } from "./chunk-hrd0mft1.js";
7
+ } from "./chunk-2rs8a26p.js";
8
8
  import {
9
9
  SVG_NS,
10
10
  isSVGTag,
@@ -16,7 +16,7 @@ import {
16
16
  setReadValueCallback,
17
17
  signal,
18
18
  untrack
19
- } from "./chunk-hrd0mft1.js";
19
+ } from "./chunk-2rs8a26p.js";
20
20
 
21
21
  // src/query/cache.ts
22
22
  class MemoryCache {
@@ -459,7 +459,7 @@ function queryMatch(queryResult, handlers) {
459
459
  } else {
460
460
  branch = "data";
461
461
  }
462
- if (branch === currentBranch && branch !== "data") {
462
+ if (branch === currentBranch) {
463
463
  return;
464
464
  }
465
465
  runCleanups(branchCleanups);
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  injectCSS
3
- } from "./chunk-qacth5ah.js";
3
+ } from "./chunk-kg898f92.js";
4
4
 
5
5
  // src/dom/animation.ts
6
6
  function onAnimationsComplete(el, callback) {
@@ -8,7 +8,9 @@ function onAnimationsComplete(el, callback) {
8
8
  callback();
9
9
  return;
10
10
  }
11
- el.offsetHeight;
11
+ if ("offsetHeight" in el) {
12
+ el.offsetHeight;
13
+ }
12
14
  if (typeof el.getAnimations === "function") {
13
15
  const animations = el.getAnimations();
14
16
  if (animations.length > 0) {
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-jrtrk5z4.js";
8
8
  import {
9
9
  signal
10
- } from "./chunk-hrd0mft1.js";
10
+ } from "./chunk-2rs8a26p.js";
11
11
 
12
12
  // src/router/navigate.ts
13
13
  var DEFAULT_NAV_THRESHOLD_MS = 500;
@@ -3,7 +3,7 @@ import {
3
3
  __element,
4
4
  __enterChildren,
5
5
  __exitChildren
6
- } from "./chunk-ryb49346.js";
6
+ } from "./chunk-662f9zrb.js";
7
7
 
8
8
  // src/component/children.ts
9
9
  var MAX_RESOLVE_DEPTH = 100;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  domEffect
3
- } from "./chunk-hrd0mft1.js";
3
+ } from "./chunk-2rs8a26p.js";
4
4
 
5
5
  // src/dom/attributes.ts
6
6
  function __attr(el, name, fn) {
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  createRouter
3
- } from "../shared/chunk-ka5ked7n.js";
3
+ } from "../shared/chunk-g1gf16fz.js";
4
4
  import {
5
5
  defineRoutes
6
6
  } from "../shared/chunk-9e92w0wt.js";
7
7
  import"../shared/chunk-jrtrk5z4.js";
8
- import"../shared/chunk-hrd0mft1.js";
8
+ import"../shared/chunk-2rs8a26p.js";
9
9
 
10
10
  // src/test/interactions.ts
11
11
  async function click(el) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vertz/ui",
3
- "version": "0.2.10",
3
+ "version": "0.2.12",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Vertz UI framework — signals, components, JSX runtime",
@@ -63,11 +63,11 @@
63
63
  "typecheck": "tsc --noEmit"
64
64
  },
65
65
  "dependencies": {
66
- "@vertz/fetch": "^0.2.1"
66
+ "@vertz/fetch": "^0.2.11"
67
67
  },
68
68
  "devDependencies": {
69
69
  "@happy-dom/global-registrator": "^20.7.0",
70
- "@vertz/schema": "^0.2.4",
70
+ "@vertz/schema": "^0.2.11",
71
71
  "bunup": "^0.16.31",
72
72
  "happy-dom": "^20.7.0",
73
73
  "typescript": "^5.7.0"