@epoch32/visage 1.2.0 → 1.3.1

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 (64) hide show
  1. package/dist/api/index.js +4 -4
  2. package/dist/charts/index.js +5 -5
  3. package/dist/devtools/index.js +7 -7
  4. package/dist/dom/index.js +3 -3
  5. package/dist/form/index.js +2 -2
  6. package/dist/{index-grhdtcb5.js → index-0qat98zs.js} +3 -3
  7. package/dist/{index-h6tjqk6e.js → index-1jcd8gjf.js} +28 -17
  8. package/dist/{index-6kfe1nr4.js → index-1yhbg34h.js} +41 -9
  9. package/dist/{index-9fvarkrx.js → index-3y73zgbk.js} +1 -1
  10. package/dist/{index-y9bq7h9g.js → index-5jhzwtdz.js} +2 -2
  11. package/dist/{index-a4xns6cc.js → index-65j7a9et.js} +1 -1
  12. package/dist/{index-dyvgtp43.js → index-9bez1tmf.js} +2 -2
  13. package/dist/{index-b5wc752a.js → index-aagpn02x.js} +346 -5
  14. package/dist/{index-df5e614s.js → index-awcrz19g.js} +1 -1
  15. package/dist/{index-8x8xz80v.js → index-b7emqmej.js} +3 -3
  16. package/dist/{index-061jf06a.js → index-cgepvvd4.js} +1 -1
  17. package/dist/{index-1xtrtz1t.js → index-dmhmde2z.js} +2 -2
  18. package/dist/{index-0madxn60.js → index-enc8cmea.js} +1 -1
  19. package/dist/{index-js199er4.js → index-g1drdx7k.js} +12 -1
  20. package/dist/{index-zr6cvset.js → index-ptj8rt6b.js} +5 -5
  21. package/dist/{index-8czhpea8.js → index-x6whpv54.js} +30 -19
  22. package/dist/{index-5s255b3w.js → index-xby428n5.js} +23 -22
  23. package/dist/{index-x2rs3vab.js → index-y3nprgn7.js} +1 -1
  24. package/dist/index.d.ts +1 -1
  25. package/dist/index.js +32 -18
  26. package/dist/jsx-runtime/index.js +4 -4
  27. package/dist/query/index.js +3 -3
  28. package/dist/router/index.js +5 -5
  29. package/dist/rpc/index.js +4 -4
  30. package/dist/server/index.js +2 -2
  31. package/dist/signals/index.js +1 -1
  32. package/dist/signals/system.d.ts +2 -0
  33. package/dist/style/index.js +6 -6
  34. package/dist/template/index.js +2 -2
  35. package/dist/ui/bottom-bar/action.d.ts +3 -0
  36. package/dist/ui/bottom-bar/badge.d.ts +3 -0
  37. package/dist/ui/bottom-bar/context.d.ts +3 -0
  38. package/dist/ui/bottom-bar/index.d.ts +19 -0
  39. package/dist/ui/bottom-bar/item.d.ts +3 -0
  40. package/dist/ui/bottom-bar/root.d.ts +3 -0
  41. package/dist/ui/bottom-bar/separator.d.ts +3 -0
  42. package/dist/ui/bottom-bar/types.d.ts +72 -0
  43. package/dist/ui/index.d.ts +1 -0
  44. package/dist/ui/index.js +21 -7
  45. package/dist/virtual/index.js +5 -5
  46. package/package.json +1 -1
  47. package/src/dom/control-flow.ts +31 -21
  48. package/src/form/form.ts +29 -17
  49. package/src/index.ts +14 -0
  50. package/src/signals/effect.ts +2 -2
  51. package/src/signals/scope.ts +10 -8
  52. package/src/signals/store.ts +32 -1
  53. package/src/signals/system.ts +8 -0
  54. package/src/template/template.ts +12 -1
  55. package/src/ui/bottom-bar/action.tsx +60 -0
  56. package/src/ui/bottom-bar/badge.tsx +51 -0
  57. package/src/ui/bottom-bar/context.ts +8 -0
  58. package/src/ui/bottom-bar/index.ts +29 -0
  59. package/src/ui/bottom-bar/item.tsx +119 -0
  60. package/src/ui/bottom-bar/root.tsx +148 -0
  61. package/src/ui/bottom-bar/separator.tsx +26 -0
  62. package/src/ui/bottom-bar/types.ts +79 -0
  63. package/src/ui/index.ts +16 -0
  64. package/src/virtual/virtualizer.ts +31 -18
@@ -1,26 +1,27 @@
1
1
  import {
2
+ computed2,
2
3
  onCleanup2,
3
4
  createContext2,
4
5
  useContext2,
5
6
  signal2,
6
7
  createSignal2
7
- } from "./index-6kfe1nr4.js";
8
+ } from "./index-1yhbg34h.js";
8
9
  import {
9
10
  Show2,
10
11
  For2,
11
12
  Portal2
12
- } from "./index-5s255b3w.js";
13
+ } from "./index-xby428n5.js";
13
14
  import {
14
15
  Fragment2,
15
16
  jsx2,
16
17
  jsxs2
17
- } from "./index-9fvarkrx.js";
18
+ } from "./index-3y73zgbk.js";
18
19
  import {
19
20
  createId2,
20
21
  createControllableSignal2,
21
22
  trapFocus2,
22
23
  onClickOutside2
23
- } from "./index-8x8xz80v.js";
24
+ } from "./index-b7emqmej.js";
24
25
 
25
26
  // src/ui/accordion.tsx
26
27
  var AccordionContext = createContext2(null);
@@ -198,6 +199,346 @@ var Accordion2 = Object.assign(AccordionRoot, {
198
199
  Trigger: AccordionTrigger2,
199
200
  Content: AccordionContent2
200
201
  });
202
+ // src/ui/bottom-bar/action.tsx
203
+ function BottomBarAction2(props) {
204
+ const {
205
+ as: Tag = "button",
206
+ icon,
207
+ label,
208
+ disabled,
209
+ onClick,
210
+ children,
211
+ class: className,
212
+ style,
213
+ ...rest
214
+ } = props;
215
+ const defaultActionStyle = {
216
+ display: "inline-flex",
217
+ alignItems: "center",
218
+ justifyContent: "center",
219
+ width: "44px",
220
+ height: "44px",
221
+ minWidth: "44px",
222
+ minHeight: "44px",
223
+ borderRadius: "50%",
224
+ background: "linear-gradient(135deg, #ec4899 0%, #a855f7 100%)",
225
+ color: "#ffffff",
226
+ border: "none",
227
+ boxShadow: "0 4px 14px rgba(236, 72, 153, 0.45)",
228
+ cursor: "pointer",
229
+ outline: "none",
230
+ userSelect: "none",
231
+ WebkitTapHighlightColor: "transparent",
232
+ transition: "transform 0.15s ease, box-shadow 0.15s ease",
233
+ boxSizing: "border-box"
234
+ };
235
+ const combinedStyle = style ? { ...defaultActionStyle, ...style } : defaultActionStyle;
236
+ return /* @__PURE__ */ jsx2(Tag, {
237
+ type: Tag === "button" ? "button" : undefined,
238
+ role: "button",
239
+ "aria-label": label ?? "Primary action",
240
+ disabled,
241
+ class: `visage-bottom-bar-action ${className ?? ""}`.trim(),
242
+ style: combinedStyle,
243
+ onClick,
244
+ ...rest,
245
+ children: children !== undefined ? typeof children === "function" ? children() : children : typeof icon === "function" ? icon() : icon
246
+ });
247
+ }
248
+
249
+ // src/ui/bottom-bar/badge.tsx
250
+ function BottomBarBadge2(props) {
251
+ const { as: Tag = "span", count, children, class: className, style, ...rest } = props;
252
+ const displayContent = computed2(() => {
253
+ if (children !== undefined) {
254
+ return typeof children === "function" ? children() : children;
255
+ }
256
+ if (count !== undefined) {
257
+ return typeof count === "function" ? count() : count;
258
+ }
259
+ return "";
260
+ });
261
+ const defaultBadgeStyle = {
262
+ position: "absolute",
263
+ top: "3px",
264
+ right: "8px",
265
+ minWidth: "16px",
266
+ height: "16px",
267
+ padding: "0 4px",
268
+ borderRadius: "9999px",
269
+ backgroundColor: "#ef4444",
270
+ color: "#ffffff",
271
+ fontSize: "9px",
272
+ fontWeight: 700,
273
+ lineHeight: "16px",
274
+ textAlign: "center",
275
+ display: "inline-flex",
276
+ alignItems: "center",
277
+ justifyContent: "center",
278
+ boxShadow: "0 0 0 1.5px rgba(18, 18, 24, 0.9)",
279
+ pointerEvents: "none",
280
+ boxSizing: "border-box"
281
+ };
282
+ const combinedStyle = style ? { ...defaultBadgeStyle, ...style } : defaultBadgeStyle;
283
+ return /* @__PURE__ */ jsx2(Tag, {
284
+ class: `visage-bottom-bar-badge ${className ?? ""}`.trim(),
285
+ style: combinedStyle,
286
+ ...rest,
287
+ children: displayContent
288
+ });
289
+ }
290
+
291
+ // src/ui/bottom-bar/context.ts
292
+ var BottomBarContext = createContext2(null);
293
+ function useBottomBarContext2() {
294
+ return useContext2(BottomBarContext);
295
+ }
296
+
297
+ // src/ui/bottom-bar/item.tsx
298
+ function BottomBarItem2(props) {
299
+ const ctx = useBottomBarContext2();
300
+ const isSelected = computed2(() => ctx ? ctx.value() === props.value : false);
301
+ const handleClick = (e) => {
302
+ const isDisabled = typeof props.disabled === "function" ? props.disabled() : Boolean(props.disabled);
303
+ if (isDisabled) {
304
+ e.preventDefault();
305
+ return;
306
+ }
307
+ if (ctx) {
308
+ ctx.setValue(props.value);
309
+ }
310
+ if (props.onClick) {
311
+ props.onClick(e);
312
+ }
313
+ };
314
+ const {
315
+ as: Tag = "button",
316
+ value,
317
+ icon,
318
+ label,
319
+ badge,
320
+ disabled,
321
+ onClick: _oc,
322
+ children,
323
+ class: className,
324
+ style,
325
+ ...rest
326
+ } = props;
327
+ const defaultItemStyle = {
328
+ position: "relative",
329
+ display: "flex",
330
+ flexDirection: "column",
331
+ alignItems: "center",
332
+ justifyContent: "center",
333
+ minWidth: "52px",
334
+ minHeight: "44px",
335
+ padding: "6px 8px",
336
+ borderRadius: "16px",
337
+ border: "none",
338
+ background: () => isSelected() ? "rgba(255, 255, 255, 0.08)" : "transparent",
339
+ color: () => isSelected() ? "#ec4899" : "rgba(255, 255, 255, 0.65)",
340
+ cursor: "pointer",
341
+ outline: "none",
342
+ userSelect: "none",
343
+ WebkitTapHighlightColor: "transparent",
344
+ transition: "color 0.15s ease, background-color 0.15s ease, transform 0.1s ease",
345
+ boxSizing: "border-box"
346
+ };
347
+ const combinedStyle = style ? { ...defaultItemStyle, ...style } : defaultItemStyle;
348
+ return /* @__PURE__ */ jsxs2(Tag, {
349
+ type: Tag === "button" ? "button" : undefined,
350
+ role: "tab",
351
+ id: ctx ? `${ctx.baseId}-item-${value}` : undefined,
352
+ "aria-selected": () => isSelected(),
353
+ "data-state": () => isSelected() ? "active" : "inactive",
354
+ "data-value": value,
355
+ disabled,
356
+ class: `visage-bottom-bar-item ${className ?? ""}`.trim(),
357
+ style: combinedStyle,
358
+ onClick: handleClick,
359
+ ...rest,
360
+ children: [
361
+ badge !== undefined ? typeof badge === "function" || typeof badge === "string" || typeof badge === "number" ? /* @__PURE__ */ jsx2(BottomBarBadge2, {
362
+ count: badge
363
+ }) : badge : null,
364
+ children !== undefined ? typeof children === "function" ? children() : children : null,
365
+ icon ? /* @__PURE__ */ jsx2("span", {
366
+ class: "visage-bottom-bar-icon",
367
+ style: {
368
+ display: "inline-flex",
369
+ alignItems: "center",
370
+ justifyContent: "center",
371
+ fontSize: "18px"
372
+ },
373
+ children: typeof icon === "function" ? icon() : icon
374
+ }) : null,
375
+ label ? /* @__PURE__ */ jsx2("span", {
376
+ class: "visage-bottom-bar-label",
377
+ style: {
378
+ fontSize: "10px",
379
+ fontWeight: 500,
380
+ lineHeight: 1.2,
381
+ marginTop: "3px",
382
+ letterSpacing: "0.01em"
383
+ },
384
+ children: typeof label === "function" ? label() : label
385
+ }) : null
386
+ ]
387
+ });
388
+ }
389
+
390
+ // src/ui/bottom-bar/root.tsx
391
+ function BottomBarRoot2(props) {
392
+ const [value, setValue] = createControllableSignal2({
393
+ value: props.value,
394
+ defaultValue: props.defaultValue ?? (props.items && props.items.length > 0 ? props.items[0]?.value : ""),
395
+ onChange: props.onValueChange
396
+ });
397
+ const baseId = createId2("bottom-bar");
398
+ const variant = props.variant ?? "floating";
399
+ const hideOnScroll = props.hideOnScroll ?? false;
400
+ const threshold = props.scrollThreshold ?? 20;
401
+ const isVisible = signal2(true);
402
+ if (hideOnScroll && typeof window !== "undefined") {
403
+ let lastScrollY = window.scrollY || 0;
404
+ const onScroll = () => {
405
+ const currentScrollY = window.scrollY || 0;
406
+ const diff = currentScrollY - lastScrollY;
407
+ if (Math.abs(diff) < 5)
408
+ return;
409
+ if (currentScrollY > threshold && diff > 0) {
410
+ if (isVisible())
411
+ isVisible.set(false);
412
+ } else if (diff < 0) {
413
+ if (!isVisible())
414
+ isVisible.set(true);
415
+ }
416
+ lastScrollY = Math.max(0, currentScrollY);
417
+ };
418
+ window.addEventListener("scroll", onScroll, { passive: true });
419
+ }
420
+ const ctx = {
421
+ value,
422
+ setValue,
423
+ baseId,
424
+ variant
425
+ };
426
+ const {
427
+ as: Tag = "nav",
428
+ value: _v,
429
+ defaultValue: _dv,
430
+ onValueChange: _ovc,
431
+ variant: _var,
432
+ hideOnScroll: _hos,
433
+ scrollThreshold: _st,
434
+ items,
435
+ itemClass,
436
+ itemStyle,
437
+ children,
438
+ class: className,
439
+ style,
440
+ ...rest
441
+ } = props;
442
+ const defaultFloatingStyle = {
443
+ position: "fixed",
444
+ bottom: "max(16px, env(safe-area-inset-bottom, 16px))",
445
+ left: "50%",
446
+ zIndex: 50,
447
+ display: "inline-flex",
448
+ alignItems: "center",
449
+ gap: "6px",
450
+ padding: "6px 10px",
451
+ borderRadius: "9999px",
452
+ backgroundColor: "rgba(18, 18, 24, 0.88)",
453
+ backdropFilter: "blur(20px)",
454
+ WebkitBackdropFilter: "blur(20px)",
455
+ border: "1px solid rgba(255, 255, 255, 0.1)",
456
+ boxShadow: "0 10px 30px -5px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(255, 255, 255, 0.05)",
457
+ transform: () => isVisible() ? "translateX(-50%) translateY(0)" : "translateX(-50%) translateY(160%)",
458
+ opacity: () => isVisible() ? 1 : 0,
459
+ pointerEvents: () => isVisible() ? "auto" : "none",
460
+ transition: "transform 0.3s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.25s ease",
461
+ maxWidth: "calc(100vw - 32px)",
462
+ boxSizing: "border-box"
463
+ };
464
+ const defaultDockedStyle = {
465
+ position: "fixed",
466
+ bottom: 0,
467
+ left: 0,
468
+ right: 0,
469
+ zIndex: 50,
470
+ display: "flex",
471
+ justifyContent: "space-around",
472
+ alignItems: "center",
473
+ paddingTop: "8px",
474
+ paddingBottom: "max(10px, env(safe-area-inset-bottom, 10px))",
475
+ paddingLeft: "12px",
476
+ paddingRight: "12px",
477
+ backgroundColor: "rgba(18, 18, 24, 0.94)",
478
+ backdropFilter: "blur(20px)",
479
+ WebkitBackdropFilter: "blur(20px)",
480
+ borderTop: "1px solid rgba(255, 255, 255, 0.1)",
481
+ boxShadow: "0 -4px 20px rgba(0, 0, 0, 0.35)",
482
+ transform: () => isVisible() ? "translateY(0)" : "translateY(110%)",
483
+ opacity: () => isVisible() ? 1 : 0,
484
+ pointerEvents: () => isVisible() ? "auto" : "none",
485
+ transition: "transform 0.3s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.25s ease",
486
+ boxSizing: "border-box"
487
+ };
488
+ const computedDefaultStyle = variant === "floating" ? defaultFloatingStyle : defaultDockedStyle;
489
+ const combinedStyle = style ? { ...computedDefaultStyle, ...style } : computedDefaultStyle;
490
+ return BottomBarContext.Provider({
491
+ value: ctx,
492
+ children: () => /* @__PURE__ */ jsx2(Tag, {
493
+ role: "toolbar",
494
+ "aria-label": "Mobile toolbar",
495
+ "data-variant": variant,
496
+ "data-state": () => isVisible() ? "visible" : "hidden",
497
+ class: `visage-bottom-bar visage-bottom-bar-${variant} ${className ?? ""}`.trim(),
498
+ style: combinedStyle,
499
+ ...rest,
500
+ children: items ? items.map((item) => /* @__PURE__ */ jsx2(BottomBarItem2, {
501
+ value: item.value,
502
+ icon: item.icon,
503
+ label: item.label,
504
+ badge: item.badge,
505
+ disabled: item.disabled,
506
+ onClick: item.onClick,
507
+ class: item.class ?? itemClass,
508
+ style: item.style ?? itemStyle
509
+ })) : typeof children === "function" ? children() : children
510
+ })
511
+ });
512
+ }
513
+
514
+ // src/ui/bottom-bar/separator.tsx
515
+ function BottomBarSeparator2(props) {
516
+ const { as: Tag = "div", class: className, style, ...rest } = props;
517
+ const defaultSeparatorStyle = {
518
+ width: "1px",
519
+ height: "22px",
520
+ backgroundColor: "rgba(255, 255, 255, 0.12)",
521
+ margin: "0 2px",
522
+ flexShrink: 0
523
+ };
524
+ const combinedStyle = style ? { ...defaultSeparatorStyle, ...style } : defaultSeparatorStyle;
525
+ return /* @__PURE__ */ jsx2(Tag, {
526
+ role: "separator",
527
+ "aria-orientation": "vertical",
528
+ class: `visage-bottom-bar-separator ${className ?? ""}`.trim(),
529
+ style: combinedStyle,
530
+ ...rest
531
+ });
532
+ }
533
+
534
+ // src/ui/bottom-bar/index.ts
535
+ var BottomBar2 = Object.assign(BottomBarRoot2, {
536
+ Root: BottomBarRoot2,
537
+ Item: BottomBarItem2,
538
+ Action: BottomBarAction2,
539
+ Badge: BottomBarBadge2,
540
+ Separator: BottomBarSeparator2
541
+ });
201
542
  // src/ui/command-dialog.tsx
202
543
  function CommandDialog2(props) {
203
544
  const [open, setOpen] = createControllableSignal2({
@@ -1693,4 +2034,4 @@ var Toast2 = Object.assign(ToastRoot, {
1693
2034
  Description: ToastDescription2,
1694
2035
  Close: ToastClose2
1695
2036
  });
1696
- export { useAccordionContext2, useAccordionItemContext2, AccordionItem2, AccordionTrigger2, AccordionContent2, Accordion2, CommandDialog2, useCommandContext2, CommandInput2, CommandList2, CommandEmpty2, CommandGroup2, CommandItem2, Command2, DataTable2, useDialogContext2, DialogTrigger2, DialogPortal2, DialogOverlay2, DialogContent2, DialogTitle2, DialogDescription2, DialogClose2, Dialog2, useDropdownMenuContext2, DropdownMenuTrigger2, DropdownMenuPortal2, DropdownMenuContent2, DropdownMenuItem2, DropdownMenuSeparator2, DropdownMenu2, createIcon2, usePopoverContext2, PopoverTrigger2, PopoverPortal2, PopoverContent2, PopoverClose2, Popover2, createToastManager2, defaultToastManager2, toast2, useToast2, ToastProvider2, ToastViewport2, ToastTitle2, ToastDescription2, ToastClose2, Toast2 };
2037
+ export { useAccordionContext2, useAccordionItemContext2, AccordionItem2, AccordionTrigger2, AccordionContent2, Accordion2, BottomBarAction2, BottomBarBadge2, useBottomBarContext2, BottomBarItem2, BottomBarRoot2, BottomBarSeparator2, BottomBar2, CommandDialog2, useCommandContext2, CommandInput2, CommandList2, CommandEmpty2, CommandGroup2, CommandItem2, Command2, DataTable2, useDialogContext2, DialogTrigger2, DialogPortal2, DialogOverlay2, DialogContent2, DialogTitle2, DialogDescription2, DialogClose2, Dialog2, useDropdownMenuContext2, DropdownMenuTrigger2, DropdownMenuPortal2, DropdownMenuContent2, DropdownMenuItem2, DropdownMenuSeparator2, DropdownMenu2, createIcon2, usePopoverContext2, PopoverTrigger2, PopoverPortal2, PopoverContent2, PopoverClose2, Popover2, createToastManager2, defaultToastManager2, toast2, useToast2, ToastProvider2, ToastViewport2, ToastTitle2, ToastDescription2, ToastClose2, Toast2 };
@@ -6,7 +6,7 @@ import {
6
6
  import {
7
7
  createMutation2,
8
8
  createQuery2
9
- } from "./index-y9bq7h9g.js";
9
+ } from "./index-5jhzwtdz.js";
10
10
  // src/rpc/client.ts
11
11
  async function resolveHeaders(headers, fullPath = "") {
12
12
  if (!headers)
@@ -5,16 +5,16 @@ import {
5
5
  createContext2,
6
6
  useContext2,
7
7
  signal2
8
- } from "./index-6kfe1nr4.js";
8
+ } from "./index-1yhbg34h.js";
9
9
  import {
10
10
  Show2,
11
11
  Portal2
12
- } from "./index-5s255b3w.js";
12
+ } from "./index-xby428n5.js";
13
13
  import {
14
14
  Fragment2,
15
15
  jsx2,
16
16
  jsxs2
17
- } from "./index-9fvarkrx.js";
17
+ } from "./index-3y73zgbk.js";
18
18
 
19
19
  // src/ui/utils.ts
20
20
  var idCounter = 0;
@@ -9,7 +9,7 @@ import {
9
9
  import {
10
10
  createMutation2,
11
11
  createQuery2
12
- } from "./index-y9bq7h9g.js";
12
+ } from "./index-5jhzwtdz.js";
13
13
  // src/api/client.ts
14
14
  async function resolveHeaders(headers, fullPath = "") {
15
15
  if (!headers)
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  space2,
3
3
  resolveDimension2
4
- } from "./index-6kfe1nr4.js";
4
+ } from "./index-1yhbg34h.js";
5
5
  import {
6
6
  jsx2
7
- } from "./index-9fvarkrx.js";
7
+ } from "./index-3y73zgbk.js";
8
8
 
9
9
  // src/style/layout.tsx
10
10
  var ALIGN_MAP = {
@@ -3,7 +3,7 @@ import {
3
3
  formatStyleProp2,
4
4
  onCleanup2,
5
5
  effect2
6
- } from "./index-6kfe1nr4.js";
6
+ } from "./index-1yhbg34h.js";
7
7
 
8
8
  // src/style/hash.ts
9
9
  function hashString2(str) {
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  setAttribute2,
3
3
  insertChild2
4
- } from "./index-6kfe1nr4.js";
4
+ } from "./index-1yhbg34h.js";
5
5
 
6
6
  // src/template/template.ts
7
7
  var templateCache = new Map;
@@ -57,6 +57,17 @@ function html2(strings, ...values) {
57
57
  targetEl.removeAttribute(bind.attrName);
58
58
  setAttribute2(targetEl, bind.attrName, bind.value);
59
59
  }
60
+ } else if (bind.isAttr && !bind.attrName) {
61
+ const propAttr = `data-visage-prop-${bind.id.replace(MARKER_PREFIX, "")}`;
62
+ const targetEl = findElementWithAttributeValue(root, propAttr, bind.id);
63
+ if (targetEl) {
64
+ targetEl.removeAttribute(propAttr);
65
+ if (typeof bind.value === "object" && bind.value !== null) {
66
+ for (const key of Object.keys(bind.value)) {
67
+ setAttribute2(targetEl, key, bind.value[key]);
68
+ }
69
+ }
70
+ }
60
71
  } else if (!bind.isAttr) {
61
72
  const commentNode = findCommentNode(root, bind.id);
62
73
  if (commentNode?.parentNode) {
@@ -3,23 +3,23 @@ import {
3
3
  effect2,
4
4
  signal2,
5
5
  createSignal2
6
- } from "./index-6kfe1nr4.js";
6
+ } from "./index-1yhbg34h.js";
7
7
  import {
8
8
  Show2,
9
9
  For2
10
- } from "./index-5s255b3w.js";
10
+ } from "./index-xby428n5.js";
11
11
  import {
12
12
  jsx2,
13
13
  jsxs2
14
- } from "./index-9fvarkrx.js";
14
+ } from "./index-3y73zgbk.js";
15
15
  import {
16
16
  Tabs2,
17
17
  Tooltip3,
18
18
  Switch2
19
- } from "./index-8x8xz80v.js";
19
+ } from "./index-b7emqmej.js";
20
20
  import {
21
21
  Style2
22
- } from "./index-0madxn60.js";
22
+ } from "./index-enc8cmea.js";
23
23
 
24
24
  // src/devtools/hub.ts
25
25
  class DevToolsHub2 {
@@ -3,13 +3,13 @@ import {
3
3
  onCleanup2,
4
4
  effect2,
5
5
  signal2
6
- } from "./index-6kfe1nr4.js";
6
+ } from "./index-1yhbg34h.js";
7
7
  import {
8
8
  For2
9
- } from "./index-5s255b3w.js";
9
+ } from "./index-xby428n5.js";
10
10
  import {
11
11
  jsx2
12
- } from "./index-9fvarkrx.js";
12
+ } from "./index-3y73zgbk.js";
13
13
 
14
14
  // src/virtual/virtualizer.ts
15
15
  function createVirtualizer2(options) {
@@ -26,27 +26,35 @@ function createVirtualizer2(options) {
26
26
  return cached;
27
27
  return getItemHeight(index);
28
28
  };
29
- const totalSize = computed2(() => {
30
- const count = getCount();
31
- if (typeof options.itemHeight === "number" && dynamicSizeCache.size === 0) {
32
- return count * options.itemHeight;
33
- }
34
- let total = 0;
35
- for (let i = 0;i < count; i++) {
36
- total += getItemSize(i);
37
- }
38
- return total;
39
- });
29
+ const offsetsCache = [0];
30
+ let offsetsValidUpTo = 0;
31
+ const invalidateOffsetsFrom = (index) => {
32
+ offsetsValidUpTo = Math.min(offsetsValidUpTo, index);
33
+ };
40
34
  const getOffsetForIndex = (targetIndex) => {
35
+ if (targetIndex <= 0)
36
+ return 0;
41
37
  if (typeof options.itemHeight === "number" && dynamicSizeCache.size === 0) {
42
38
  return targetIndex * options.itemHeight;
43
39
  }
44
- let offset = 0;
45
- for (let i = 0;i < targetIndex; i++) {
46
- offset += getItemSize(i);
40
+ if (targetIndex <= offsetsValidUpTo) {
41
+ return offsetsCache[targetIndex];
42
+ }
43
+ for (let i = offsetsValidUpTo;i < targetIndex; i++) {
44
+ offsetsCache[i + 1] = (offsetsCache[i] ?? 0) + getItemSize(i);
47
45
  }
48
- return offset;
46
+ offsetsValidUpTo = targetIndex;
47
+ return offsetsCache[targetIndex];
49
48
  };
49
+ const measurementVersion = signal2(0, { internal: true });
50
+ const totalSize = computed2(() => {
51
+ measurementVersion();
52
+ const count = getCount();
53
+ if (typeof options.itemHeight === "number" && dynamicSizeCache.size === 0) {
54
+ return count * options.itemHeight;
55
+ }
56
+ return getOffsetForIndex(count);
57
+ });
50
58
  const findIndexAtOffset = (offset) => {
51
59
  const count = getCount();
52
60
  if (count === 0)
@@ -72,11 +80,13 @@ function createVirtualizer2(options) {
72
80
  return Math.max(0, Math.min(count - 1, low));
73
81
  };
74
82
  const startIndex = computed2(() => {
83
+ measurementVersion();
75
84
  const offset = scrollOffset();
76
85
  const rawStart = findIndexAtOffset(offset);
77
86
  return Math.max(0, rawStart - overscan);
78
87
  });
79
88
  const endIndex = computed2(() => {
89
+ measurementVersion();
80
90
  const count = getCount();
81
91
  if (count === 0)
82
92
  return 0;
@@ -193,7 +203,8 @@ function createVirtualizer2(options) {
193
203
  const measuredSize = isHorizontal ? element.getBoundingClientRect().width : element.getBoundingClientRect().height;
194
204
  if (measuredSize > 0 && dynamicSizeCache.get(index) !== measuredSize) {
195
205
  dynamicSizeCache.set(index, measuredSize);
196
- scrollOffset.update((o) => o);
206
+ invalidateOffsetsFrom(index);
207
+ measurementVersion.update((v) => v + 1);
197
208
  }
198
209
  };
199
210
  return {
@@ -7,12 +7,13 @@ import {
7
7
  runInScope2,
8
8
  getActiveScope2,
9
9
  useContext2,
10
- effect2
11
- } from "./index-6kfe1nr4.js";
10
+ effect2,
11
+ signal2
12
+ } from "./index-1yhbg34h.js";
12
13
  import {
13
14
  SuspenseContext2,
14
15
  createResource2
15
- } from "./index-a4xns6cc.js";
16
+ } from "./index-65j7a9et.js";
16
17
 
17
18
  // src/dom/lazy.ts
18
19
  function lazy2(importer) {
@@ -176,26 +177,24 @@ function For2(props) {
176
177
  const newItems = [];
177
178
  const prevMap = new Map;
178
179
  for (const rec of items) {
179
- prevMap.set(rec.item, rec);
180
+ const group = prevMap.get(rec.item);
181
+ if (group) {
182
+ group.push(rec);
183
+ } else {
184
+ prevMap.set(rec.item, [rec]);
185
+ }
180
186
  }
181
187
  for (let i = 0;i < list.length; i++) {
182
188
  const item = list[i];
183
- const existing = prevMap.get(item);
189
+ const existingGroup = prevMap.get(item);
190
+ const existing = existingGroup && existingGroup.length > 0 ? existingGroup.shift() : undefined;
184
191
  if (existing) {
185
- existing.indexSignal(i);
192
+ untrack2(() => {
193
+ existing.indexSignal.set(i);
194
+ });
186
195
  newItems.push(existing);
187
- prevMap.delete(item);
188
196
  } else {
189
- let indexVal = i;
190
- const indexSubscribers = new Set;
191
- const indexSignal = (...args) => {
192
- if (args.length > 0 && args[0] !== undefined && args[0] !== indexVal) {
193
- indexVal = args[0];
194
- for (const sub of Array.from(indexSubscribers))
195
- sub();
196
- }
197
- return indexVal;
198
- };
197
+ const indexSignal = signal2(i, { internal: true });
199
198
  const itemScope = new Scope2(getActiveScope2());
200
199
  let itemNodes = [];
201
200
  untrack2(() => {
@@ -214,11 +213,13 @@ function For2(props) {
214
213
  });
215
214
  }
216
215
  }
217
- for (const [, removed] of prevMap) {
218
- removed.scope.dispose();
219
- for (const node of removed.nodes) {
220
- if (node.parentNode)
221
- node.parentNode.removeChild(node);
216
+ for (const [, remainingGroup] of prevMap) {
217
+ for (const removed of remainingGroup) {
218
+ removed.scope.dispose();
219
+ for (const node of removed.nodes) {
220
+ if (node.parentNode)
221
+ node.parentNode.removeChild(node);
222
+ }
222
223
  }
223
224
  }
224
225
  if (marker.parentNode) {
@@ -2,7 +2,7 @@ import {
2
2
  insertChild2,
3
3
  onCleanup2,
4
4
  createRoot2
5
- } from "./index-6kfe1nr4.js";
5
+ } from "./index-1yhbg34h.js";
6
6
 
7
7
  // src/server/hydrate.ts
8
8
  function hydrate2(fn, container) {