vlist 3.0.0-next.1 → 3.0.0-next.3

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 (68) hide show
  1. package/README.github.md +111 -65
  2. package/README.md +70 -54
  3. package/dist/config.d.ts +105 -14
  4. package/dist/config.js +1 -1
  5. package/dist/core/create.d.ts +22 -5
  6. package/dist/core/dom.d.ts +1 -1
  7. package/dist/core/hooks.d.ts +1 -1
  8. package/dist/core/index.d.ts +1 -1
  9. package/dist/core/pipeline.d.ts +1 -1
  10. package/dist/core/pool.d.ts +1 -1
  11. package/dist/core/runway.d.ts +6 -2
  12. package/dist/core/scroll.d.ts +1 -1
  13. package/dist/core/sizes.d.ts +1 -1
  14. package/dist/core/state.d.ts +13 -1
  15. package/dist/core/types.d.ts +134 -62
  16. package/dist/core/velocity.d.ts +1 -1
  17. package/dist/index.d.ts +17 -2
  18. package/dist/index.js +1 -1
  19. package/dist/internals.d.ts +0 -1
  20. package/dist/internals.js +1 -1
  21. package/dist/native.d.ts +6 -10
  22. package/dist/native.js +1 -1
  23. package/dist/plugins/a11y/index.d.ts +1 -0
  24. package/dist/plugins/a11y/plugin.d.ts +1 -1
  25. package/dist/plugins/autosize/index.d.ts +2 -1
  26. package/dist/plugins/autosize/plugin.d.ts +13 -2
  27. package/dist/plugins/carousel/engine.d.ts +1 -1
  28. package/dist/plugins/carousel/index.d.ts +1 -0
  29. package/dist/plugins/carousel/plugin.d.ts +30 -2
  30. package/dist/plugins/carousel/presets.d.ts +1 -1
  31. package/dist/plugins/data/index.d.ts +1 -0
  32. package/dist/plugins/data/plugin.d.ts +15 -2
  33. package/dist/plugins/grid/index.d.ts +2 -1
  34. package/dist/plugins/grid/plugin.d.ts +14 -2
  35. package/dist/plugins/groups/index.d.ts +1 -0
  36. package/dist/plugins/groups/plugin.d.ts +11 -3
  37. package/dist/plugins/groups/types.d.ts +6 -1
  38. package/dist/plugins/masonry/index.d.ts +2 -1
  39. package/dist/plugins/masonry/plugin.d.ts +10 -2
  40. package/dist/plugins/masonry/renderer.d.ts +5 -1
  41. package/dist/plugins/page/plugin.d.ts +1 -1
  42. package/dist/plugins/scrollbar/index.d.ts +2 -1
  43. package/dist/plugins/scrollbar/plugin.d.ts +7 -2
  44. package/dist/plugins/search/index.d.ts +1 -0
  45. package/dist/plugins/search/plugin.d.ts +26 -1
  46. package/dist/plugins/selection/index.d.ts +2 -1
  47. package/dist/plugins/selection/plugin.d.ts +23 -2
  48. package/dist/plugins/snapshots/index.d.ts +1 -0
  49. package/dist/plugins/snapshots/plugin.d.ts +9 -2
  50. package/dist/plugins/sortable/index.d.ts +1 -0
  51. package/dist/plugins/sortable/plugin.d.ts +11 -3
  52. package/dist/plugins/table/index.d.ts +1 -0
  53. package/dist/plugins/table/plugin.d.ts +19 -3
  54. package/dist/plugins/transition/plugin.d.ts +1 -1
  55. package/dist/plugins/tree/index.d.ts +1 -0
  56. package/dist/plugins/tree/layout.d.ts +1 -1
  57. package/dist/plugins/tree/plugin.d.ts +37 -3
  58. package/dist/plugins/tree/types.d.ts +1 -1
  59. package/dist/rendering/sizes.d.ts +7 -1
  60. package/dist/size.json +1 -1
  61. package/dist/synthetic.d.ts +3 -3
  62. package/dist/synthetic.js +1 -1
  63. package/dist/types.d.ts +17 -21
  64. package/dist/utils/scroll-padding.d.ts +36 -0
  65. package/dist/utils/stats.d.ts +9 -1
  66. package/dist/vlist.css +1 -1
  67. package/package.json +6 -2
  68. package/dist/plugins/grid/renderer.d.ts +0 -68
package/dist/types.d.ts CHANGED
@@ -4,10 +4,13 @@
4
4
  */
5
5
  /** Base event map with index signature */
6
6
  export type EventMap = Record<string, unknown>;
7
- /** Base item interface - must have an id */
7
+ /**
8
+ * Base item interface: an item only has to carry an id. Plugins that read
9
+ * arbitrary fields (search, table, tree) narrow the item themselves, so an
10
+ * ordinary `interface Row { id: number; name: string }` satisfies this.
11
+ */
8
12
  export interface VListItem {
9
13
  id: string | number;
10
- [key: string]: unknown;
11
14
  }
12
15
  /** Groups configuration for createVList */
13
16
  export interface GroupHeaderConfig {
@@ -291,8 +294,11 @@ export interface ScrollConfig {
291
294
  * Useful for carousels, wizards, and circular navigation.
292
295
  */
293
296
  wrap?: boolean;
294
- /** Options consumed by vlist/config; use scrollbar() with the core factory. */
295
- scrollbar?: ScrollbarOptions;
297
+ /** Native visibility: "none" hides the browser scrollbar, "native" keeps it.
298
+ * Options are consumed by vlist/config; with the low-level factory install
299
+ * scrollbar() explicitly. The synthetic entry rejects both string values.
300
+ */
301
+ scrollbar?: "native" | "none" | ScrollbarOptions;
296
302
  /** External scroll element for window scrolling */
297
303
  element?: Window;
298
304
  /** Scroll idle detection timeout in ms (default: 150) */
@@ -338,20 +344,6 @@ export type ScrollbarPadding = number | {
338
344
  bottom?: number;
339
345
  left?: number;
340
346
  };
341
- /**
342
- * Scrollbar configuration.
343
- * @deprecated Use `scroll.scrollbar` in `ScrollConfig` instead.
344
- */
345
- export interface ScrollbarConfig {
346
- /** Enable scrollbar (default: true) */
347
- enabled?: boolean;
348
- /** Auto-hide scrollbar after idle (default: true) */
349
- autoHide?: boolean;
350
- /** Auto-hide delay in milliseconds (default: 1000) */
351
- autoHideDelay?: number;
352
- /** Minimum thumb size in pixels (default: 30) */
353
- minThumbSize?: number;
354
- }
355
347
  /** Item template function */
356
348
  export type ItemTemplate<T = VListItem> = (item: T, index: number, state: ItemState) => string | HTMLElement;
357
349
  /** Tree-specific state available in templates when the tree plugin is active */
@@ -465,19 +457,23 @@ export interface ErrorViewportSnapshot {
465
457
  }
466
458
  /** Event types and their payloads */
467
459
  export interface VListEvents<T extends VListItem = VListItem> extends EventMap {
468
- /** Item clicked */
460
+ /**
461
+ * Item clicked. `index` is the DATA index — what `getItemAt`,
462
+ * `scrollToIndex` and `removeItem` take — not the layout index, which counts
463
+ * group headers and carousel laps as well.
464
+ */
469
465
  "item:click": {
470
466
  item: T;
471
467
  index: number;
472
468
  event: MouseEvent;
473
469
  };
474
- /** Item double-clicked */
470
+ /** Item double-clicked. `index` is the data index, as for `item:click`. */
475
471
  "item:dblclick": {
476
472
  item: T;
477
473
  index: number;
478
474
  event: MouseEvent;
479
475
  };
480
- /** Item right-clicked */
476
+ /** Item right-clicked. `index` is the data index, as for `item:click`. */
481
477
  "item:contextmenu": {
482
478
  item: T;
483
479
  index: number;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * vlist/utils — Scroll Padding Protocol
3
+ *
4
+ * page() keeps a band of the window clear at each end of the main axis — a
5
+ * sticky site header, a floating toolbar — and applies it to every scroll it
6
+ * computes. Layout plugins take over those computations: groups() and
7
+ * masonry() both override `_scrollItemIntoView` and install their own
8
+ * `setToIndexFn`, because only they know where an entry actually sits.
9
+ *
10
+ * So the band is published through the `_getScrollPadding` hook rather than
11
+ * baked into page's own scroll code alone, and the plugins that displace page
12
+ * read it back and fold it into their geometry. Without the hook a caller's
13
+ * `scrollPadding` silently disappeared as soon as either plugin was seated.
14
+ */
15
+ import type { PluginContext } from "../core/types";
16
+ import type { VListItem } from "../types";
17
+ /** Band to keep clear at each end of the main axis, in pixels. */
18
+ export interface ScrollPadding {
19
+ /** Top (vertical) or left (horizontal). */
20
+ readonly start: number;
21
+ /** Bottom (vertical) or right (horizontal). */
22
+ readonly end: number;
23
+ }
24
+ /** Internal hook name carrying the band across plugins. */
25
+ export declare const SCROLL_PADDING_HOOK = "_getScrollPadding";
26
+ /** No padding — the shape every list without page() sees. */
27
+ export declare const NO_SCROLL_PADDING: ScrollPadding;
28
+ /**
29
+ * Read the published scroll padding, or zero when no plugin publishes one.
30
+ *
31
+ * The hook is resolved on first read instead of during setup: the publisher is
32
+ * not guaranteed to be seated first. The band itself is read on every call,
33
+ * since page() accepts functions that measure a sticky header each time.
34
+ */
35
+ export declare function createScrollPaddingReader<T extends VListItem>(ctx: PluginContext<T>): () => ScrollPadding;
36
+ //# sourceMappingURL=scroll-padding.d.ts.map
@@ -1,5 +1,5 @@
1
1
  export interface StatsConfig {
2
- /** Returns the current scroll position (scrollTop or scrollLeft) */
2
+ /** Returns the unscaled logical position, normally list.getScrollPosition(). */
3
3
  getScrollPosition: () => number;
4
4
  /** Returns the total number of items */
5
5
  getTotal: () => number;
@@ -33,6 +33,14 @@ export interface Stats {
33
33
  *
34
34
  * All inputs are provided via callbacks so the tracker always reflects
35
35
  * the latest values without needing to be recreated when the list changes.
36
+ * Position is an unscaled logical offset on both native and synthetic entries,
37
+ * clamped to the declared content's real scroll range. Progress counts items up
38
+ * to the viewport end (including partial rows), rather than just scroll distance.
39
+ *
40
+ * Native content beyond the browser limit still uses its full declared size:
41
+ * a browser-clamped position is not expanded to the end, so progress may never
42
+ * reach 100%. Handle the core's `content:size:overflow` error and use
43
+ * `vlist/synthetic` when the full range must remain reachable.
36
44
  *
37
45
  * ```ts
38
46
  * const stats = createStats({
package/dist/vlist.css CHANGED
@@ -1 +1 @@
1
- @keyframes vlist-fade-in{from{opacity:0.6}to{opacity:1}}@keyframes vlist-placeholder-pulse{0%,100%{opacity:0.6}50%{opacity:0.4}}:root{--vlist-scrollbar-width:8px;--vlist-scrollbar-track-color:transparent;--vlist-scrollbar-radius:4px;--vlist-custom-scrollbar-track-color:transparent;--vlist-custom-scrollbar-padding-top:2px;--vlist-custom-scrollbar-padding-right:2px;--vlist-custom-scrollbar-padding-bottom:2px;--vlist-custom-scrollbar-padding-left:2px;--vlist-custom-scrollbar-min-thumb-size:15px;--vlist-item-padding-x:1rem;--vlist-item-padding-y:0.75rem;--vlist-border-radius:0.5rem;--vlist-transition-duration:150ms;--vlist-transition-timing:ease-in-out;--vlist-bg:#ffffff;--vlist-bg-hover:rgba(0,0,0,0.04);--vlist-bg-striped:rgba(0,0,0,0.02);--vlist-bg-selected:rgba(59,130,246,0.12);--vlist-bg-selected-hover:rgba(59,130,246,0.18);--vlist-border:#e5e7eb;--vlist-border-selected:#3b82f6;--vlist-text:#111827;--vlist-text-muted:#6b7280;--vlist-focus-ring:#3b82f6;--vlist-group-header-bg:#f3f4f6;--vlist-scrollbar-thumb-color:#d1d5db;--vlist-scrollbar-thumb-hover-color:#9ca3af;--vlist-custom-scrollbar-thumb-color:rgba(0,0,0,0.2);--vlist-custom-scrollbar-thumb-hover-color:rgba(0,0,0,0.3);--vlist-placeholder-bg:rgba(0,0,0,0.2);--vlist-placeholder-bg-strong:rgba(0,0,0,0.24)}[data-theme-mode="dark"]{--vlist-bg:#111827;--vlist-bg-hover:rgba(255,255,255,0.06);--vlist-bg-striped:rgba(255,255,255,0.02);--vlist-bg-selected:rgba(59,130,246,0.2);--vlist-bg-selected-hover:rgba(59,130,246,0.28);--vlist-border:#374151;--vlist-border-selected:#3b82f6;--vlist-text:#f9fafb;--vlist-text-muted:#9ca3af;--vlist-focus-ring:#3b82f6;--vlist-group-header-bg:#1e2433;--vlist-scrollbar-thumb-color:#4b5563;--vlist-scrollbar-thumb-hover-color:#6b7280;--vlist-custom-scrollbar-thumb-color:rgba(255,255,255,0.2);--vlist-custom-scrollbar-thumb-hover-color:rgba(255,255,255,0.3);--vlist-placeholder-bg:rgba(255,255,255,0.3);--vlist-placeholder-bg-strong:rgba(255,255,255,0.35)}@media (prefers-color-scheme:dark){:root:not([data-theme-mode="light"]):not([data-theme-mode="dark"]){--vlist-bg:#111827;--vlist-bg-hover:rgba(255,255,255,0.06);--vlist-bg-striped:rgba(255,255,255,0.02);--vlist-bg-selected:rgba(59,130,246,0.2);--vlist-bg-selected-hover:rgba(59,130,246,0.28);--vlist-border:#374151;--vlist-border-selected:#3b82f6;--vlist-text:#f9fafb;--vlist-text-muted:#9ca3af;--vlist-focus-ring:#3b82f6;--vlist-group-header-bg:#1e2433;--vlist-scrollbar-thumb-color:#4b5563;--vlist-scrollbar-thumb-hover-color:#6b7280;--vlist-custom-scrollbar-thumb-color:rgba(255,255,255,0.2);--vlist-custom-scrollbar-thumb-hover-color:rgba(255,255,255,0.3);--vlist-placeholder-bg:rgba(255,255,255,0.3);--vlist-placeholder-bg-strong:rgba(255,255,255,0.35)}}.dark{--vlist-bg:#111827;--vlist-bg-hover:rgba(255,255,255,0.06);--vlist-bg-striped:rgba(255,255,255,0.02);--vlist-bg-selected:rgba(59,130,246,0.2);--vlist-bg-selected-hover:rgba(59,130,246,0.28);--vlist-border:#374151;--vlist-border-selected:#3b82f6;--vlist-text:#f9fafb;--vlist-text-muted:#9ca3af;--vlist-focus-ring:#3b82f6;--vlist-group-header-bg:#1e2433;--vlist-scrollbar-thumb-color:#4b5563;--vlist-scrollbar-thumb-hover-color:#6b7280;--vlist-custom-scrollbar-thumb-color:rgba(255,255,255,0.2);--vlist-custom-scrollbar-thumb-hover-color:rgba(255,255,255,0.3);--vlist-placeholder-bg:rgba(255,255,255,0.3);--vlist-placeholder-bg-strong:rgba(255,255,255,0.35)}.vlist{position:relative;width:100%;height:100%;box-sizing:border-box;overflow:hidden;background-color:var(--vlist-bg);color:var(--vlist-text);color-scheme:light;border:1px solid var(--vlist-border);border-radius:var(--vlist-border-radius);outline:none}[data-theme-mode="dark"] .vlist,.dark .vlist{color-scheme:dark}@media (prefers-color-scheme:dark){:root:not([data-theme-mode="light"]) .vlist{color-scheme:dark}}.vlist:focus,.vlist:focus-visible,.vlist-content:focus,.vlist-content:focus-visible{outline:none}.vlist-viewport{width:100%;height:100%;overflow:auto;scrollbar-color:var(--vlist-scrollbar-thumb-color) var(--vlist-scrollbar-track-color)}@supports (-moz-appearance:none) and (scrollbar-width:1px){.vlist-viewport{scrollbar-width:var(--vlist-scrollbar-width)}}.vlist-viewport::-webkit-scrollbar{width:var(--vlist-scrollbar-width);height:var(--vlist-scrollbar-width)}.vlist-viewport::-webkit-scrollbar-track{background:var(--vlist-scrollbar-track-color)}.vlist-viewport::-webkit-scrollbar-thumb{background-color:var(--vlist-scrollbar-thumb-color);border-radius:var(--vlist-scrollbar-radius)}.vlist-viewport::-webkit-scrollbar-thumb:hover{background-color:var(--vlist-scrollbar-thumb-hover-color)}.vlist-content{position:relative;width:100%;contain:layout style}.vlist-item,.vlist-group-header{position:absolute;top:0;left:0;right:0;display:flex;align-items:center;background-color:var(--vlist-bg);cursor:default;user-select:none;opacity:1;transition:background-color var(--vlist-transition-duration) var(--vlist-transition-timing),opacity var(--vlist-transition-duration) var(--vlist-transition-timing);contain:content;box-sizing:border-box;will-change:transform}.vlist--scrolling .vlist-item,.vlist--settling .vlist-item,.vlist--scrolling .vlist-group-header,.vlist--settling .vlist-group-header{transition:none}.vlist--selectable .vlist-item{cursor:pointer}.vlist--selectable .vlist-item:hover{background-color:var(--vlist-bg-hover)}.vlist-item.vlist-item--odd{background:var(--vlist-bg-striped)}.vlist-item.vlist-item--selected{background-color:var(--vlist-bg-selected)}.vlist-item.vlist-item--odd.vlist-item--selected{background:var(--vlist-bg-selected)}.vlist-item.vlist-item--selected:hover{background-color:var(--vlist-bg-selected-hover)}.vlist-item.vlist-item--odd.vlist-item--selected:hover{background:var(--vlist-bg-selected-hover)}.vlist-item.vlist-item--focused{outline:2px solid var(--vlist-focus-ring,#3b82f6);outline-offset:-2px;z-index:1}.vlist-item--replaced{animation:vlist-fade-in 0.3s ease-out}.vlist-item--placeholder{opacity:0.6;animation:vlist-placeholder-pulse 2s ease-in-out infinite}.vlist-item--placeholder *:not(:has(*)){color:transparent;background-color:var(--vlist-placeholder-bg);border-radius:4px;width:fit-content;line-height:1.2;transform:scaleY(0.8)}.vlist-item--placeholder:is(strong,b,h1,h2,h3,h4):not(:has(*)){background-color:var(--vlist-placeholder-bg-strong)}.vlist-item.vlist-item--selected.vlist-item--focused{outline-color:var(--vlist-border-selected)}.vlist-scrollbar:focus-visible{outline:2px solid currentColor;outline-offset:-2px;opacity:1;pointer-events:auto}.vlist-scrollbar{position:absolute;top:var(--vlist-custom-scrollbar-padding-top);right:var(--vlist-custom-scrollbar-padding-right);bottom:var(--vlist-custom-scrollbar-padding-bottom);width:var(--vlist-custom-scrollbar-width);background-color:var(--vlist-custom-scrollbar-track-color);opacity:0;transition:opacity 0.2s ease-out;touch-action:none;z-index:10;pointer-events:none}.vlist-scrollbar__hover{position:absolute;top:0;right:0;height:100%;z-index:9;touch-action:none;pointer-events:auto}.vlist-scrollbar__hover--horizontal{top:auto;right:auto;bottom:0;left:0;width:100%;height:auto}.vlist-scrollbar--visible{opacity:1;pointer-events:auto}.vlist-scrollbar__thumb{position:absolute;top:0;left:0;right:0;width:100%;background-color:var(--vlist-custom-scrollbar-thumb-color);border-radius:var(--vlist-custom-scrollbar-radius);cursor:pointer;transition:background-color 0.15s ease-out}.vlist-scrollbar__thumb:hover{background-color:var(--vlist-custom-scrollbar-thumb-hover-color)}.vlist-scrollbar--dragging{opacity:1;pointer-events:auto}.vlist-scrollbar--dragging .vlist-scrollbar__thumb{background-color:var(--vlist-custom-scrollbar-thumb-hover-color)}@media (forced-colors:active){.vlist-scrollbar{forced-color-adjust:none;background-color:Canvas}.vlist-scrollbar__thumb{background-color:CanvasText}.vlist-scrollbar__thumb:hover,.vlist-scrollbar--dragging .vlist-scrollbar__thumb{background-color:Highlight}.vlist-scrollbar:focus-visible{outline-color:Highlight}}.vlist-group-header{cursor:default;background-color:var(--vlist-group-header-bg,#f3f4f6);z-index:1;overflow:hidden}.vlist-group-header:hover{background-color:var(--vlist-group-header-bg,#f3f4f6)}.vlist-sticky-header{left:0;right:0;background-color:var(--vlist-group-header-bg,#f3f4f6)}.vlist--horizontal{overflow:hidden}.vlist--horizontal .vlist-viewport{overflow-x:auto;overflow-y:hidden}.vlist--horizontal .vlist-content{height:100%;width:auto}.vlist--horizontal .vlist-item,.vlist--horizontal .vlist-group-header{position:absolute;top:0;bottom:0;left:0;right:auto;display:flex;align-items:center;justify-content:center;border-bottom:none}.vlist-scrollbar--horizontal{top:auto;left:var(--vlist-custom-scrollbar-padding-left);right:var(--vlist-custom-scrollbar-padding-right);bottom:var(--vlist-custom-scrollbar-padding-bottom);width:auto;height:var(--vlist-custom-scrollbar-width)}.vlist-scrollbar--horizontal .vlist-scrollbar__thumb{top:0;left:0;width:auto;height:100%}.vlist-viewport--gutter{padding-right:calc( var(--vlist-custom-scrollbar-padding-right) + var(--vlist-custom-scrollbar-width) + var(--vlist-custom-scrollbar-padding-left) )}.vlist--horizontal .vlist-viewport--gutter{padding-right:0;padding-bottom:calc( var(--vlist-custom-scrollbar-padding-bottom) + var(--vlist-custom-scrollbar-width) + var(--vlist-custom-scrollbar-padding-top) )}.vlist-viewport--custom-scrollbar{scrollbar-width:none;-ms-overflow-style:none}.vlist-viewport--custom-scrollbar::-webkit-scrollbar{display:none}.vlist-viewport--no-scrollbar{scrollbar-width:none;-ms-overflow-style:none}.vlist-viewport--no-scrollbar::-webkit-scrollbar{display:none}.vlist-viewport--gutter-stable{scrollbar-gutter:stable}.vlist-sort-ghost{opacity:0.85;box-shadow:0 4px 16px rgba(0,0,0,0.18);cursor:grabbing}.vlist-item--drag-source{opacity:0 !important;pointer-events:none}.vlist--sorting{cursor:grabbing;-webkit-user-select:none;user-select:none;-webkit-touch-callout:none}.vlist--sorting *{cursor:inherit !important}.vlist-item--kb-sorting{outline:2px solid var(--vlist-focus-ring,#3b82f6);outline-offset:-2px;background-color:var(--vlist-bg-selected);z-index:1}
1
+ @keyframes vlist-fade-in{from{opacity:0.6}to{opacity:1}}@keyframes vlist-placeholder-pulse{0%,100%{opacity:0.6}50%{opacity:0.4}}:root{--vlist-scrollbar-width:8px;--vlist-scrollbar-track-color:transparent;--vlist-scrollbar-radius:4px;--vlist-custom-scrollbar-track-color:transparent;--vlist-custom-scrollbar-padding-top:2px;--vlist-custom-scrollbar-padding-right:2px;--vlist-custom-scrollbar-padding-bottom:2px;--vlist-custom-scrollbar-padding-left:2px;--vlist-custom-scrollbar-min-thumb-size:15px;--vlist-item-padding-x:1rem;--vlist-item-padding-y:0.75rem;--vlist-border-radius:0.5rem;--vlist-transition-duration:150ms;--vlist-transition-timing:ease-in-out;--vlist-bg:#ffffff;--vlist-bg-hover:rgba(0,0,0,0.04);--vlist-bg-striped:rgba(0,0,0,0.02);--vlist-bg-selected:rgba(59,130,246,0.12);--vlist-bg-selected-hover:rgba(59,130,246,0.18);--vlist-border:#e5e7eb;--vlist-border-selected:#3b82f6;--vlist-text:#111827;--vlist-text-muted:#6b7280;--vlist-focus-ring:#3b82f6;--vlist-group-header-bg:#f3f4f6;--vlist-scrollbar-thumb-color:#d1d5db;--vlist-scrollbar-thumb-hover-color:#9ca3af;--vlist-custom-scrollbar-thumb-color:rgba(0,0,0,0.2);--vlist-custom-scrollbar-thumb-hover-color:rgba(0,0,0,0.3);--vlist-placeholder-bg:rgba(0,0,0,0.2);--vlist-placeholder-bg-strong:rgba(0,0,0,0.24)}[data-theme-mode="dark"]{--vlist-bg:#111827;--vlist-bg-hover:rgba(255,255,255,0.06);--vlist-bg-striped:rgba(255,255,255,0.02);--vlist-bg-selected:rgba(59,130,246,0.2);--vlist-bg-selected-hover:rgba(59,130,246,0.28);--vlist-border:#374151;--vlist-border-selected:#3b82f6;--vlist-text:#f9fafb;--vlist-text-muted:#9ca3af;--vlist-focus-ring:#3b82f6;--vlist-group-header-bg:#1e2433;--vlist-scrollbar-thumb-color:#4b5563;--vlist-scrollbar-thumb-hover-color:#6b7280;--vlist-custom-scrollbar-thumb-color:rgba(255,255,255,0.2);--vlist-custom-scrollbar-thumb-hover-color:rgba(255,255,255,0.3);--vlist-placeholder-bg:rgba(255,255,255,0.3);--vlist-placeholder-bg-strong:rgba(255,255,255,0.35)}@media (prefers-color-scheme:dark){:root:not([data-theme-mode="light"]):not([data-theme-mode="dark"]){--vlist-bg:#111827;--vlist-bg-hover:rgba(255,255,255,0.06);--vlist-bg-striped:rgba(255,255,255,0.02);--vlist-bg-selected:rgba(59,130,246,0.2);--vlist-bg-selected-hover:rgba(59,130,246,0.28);--vlist-border:#374151;--vlist-border-selected:#3b82f6;--vlist-text:#f9fafb;--vlist-text-muted:#9ca3af;--vlist-focus-ring:#3b82f6;--vlist-group-header-bg:#1e2433;--vlist-scrollbar-thumb-color:#4b5563;--vlist-scrollbar-thumb-hover-color:#6b7280;--vlist-custom-scrollbar-thumb-color:rgba(255,255,255,0.2);--vlist-custom-scrollbar-thumb-hover-color:rgba(255,255,255,0.3);--vlist-placeholder-bg:rgba(255,255,255,0.3);--vlist-placeholder-bg-strong:rgba(255,255,255,0.35)}}.dark{--vlist-bg:#111827;--vlist-bg-hover:rgba(255,255,255,0.06);--vlist-bg-striped:rgba(255,255,255,0.02);--vlist-bg-selected:rgba(59,130,246,0.2);--vlist-bg-selected-hover:rgba(59,130,246,0.28);--vlist-border:#374151;--vlist-border-selected:#3b82f6;--vlist-text:#f9fafb;--vlist-text-muted:#9ca3af;--vlist-focus-ring:#3b82f6;--vlist-group-header-bg:#1e2433;--vlist-scrollbar-thumb-color:#4b5563;--vlist-scrollbar-thumb-hover-color:#6b7280;--vlist-custom-scrollbar-thumb-color:rgba(255,255,255,0.2);--vlist-custom-scrollbar-thumb-hover-color:rgba(255,255,255,0.3);--vlist-placeholder-bg:rgba(255,255,255,0.3);--vlist-placeholder-bg-strong:rgba(255,255,255,0.35)}.vlist{position:relative;width:100%;height:100%;box-sizing:border-box;overflow:hidden;background-color:var(--vlist-bg);color:var(--vlist-text);color-scheme:light;border:1px solid var(--vlist-border);border-radius:var(--vlist-border-radius);outline:none}[data-theme-mode="dark"] .vlist,.dark .vlist{color-scheme:dark}@media (prefers-color-scheme:dark){:root:not([data-theme-mode="light"]) .vlist{color-scheme:dark}}.vlist:focus,.vlist:focus-visible,.vlist-content:focus,.vlist-content:focus-visible{outline:none}.vlist-viewport{width:100%;height:100%;overflow:auto;scrollbar-color:var(--vlist-scrollbar-thumb-color) var(--vlist-scrollbar-track-color)}@supports (-moz-appearance:none) and (scrollbar-width:1px){.vlist-viewport{scrollbar-width:var(--vlist-scrollbar-width)}}.vlist-viewport::-webkit-scrollbar{width:var(--vlist-scrollbar-width);height:var(--vlist-scrollbar-width)}.vlist-viewport::-webkit-scrollbar-track{background:var(--vlist-scrollbar-track-color)}.vlist-viewport::-webkit-scrollbar-thumb{background-color:var(--vlist-scrollbar-thumb-color);border-radius:var(--vlist-scrollbar-radius)}.vlist-viewport::-webkit-scrollbar-thumb:hover{background-color:var(--vlist-scrollbar-thumb-hover-color)}.vlist-content{position:relative;width:100%;contain:layout style}.vlist-item,.vlist-group-header{position:absolute;top:0;left:0;right:0;display:flex;align-items:center;background-color:var(--vlist-bg);cursor:default;user-select:none;opacity:1;transition:background-color var(--vlist-transition-duration) var(--vlist-transition-timing),opacity var(--vlist-transition-duration) var(--vlist-transition-timing);contain:content;box-sizing:border-box;will-change:transform}.vlist--scrolling .vlist-item,.vlist--settling .vlist-item,.vlist--scrolling .vlist-group-header,.vlist--settling .vlist-group-header{transition:none}.vlist--selectable .vlist-item{cursor:pointer}.vlist--selectable .vlist-item:hover{background-color:var(--vlist-bg-hover)}.vlist-item.vlist-item--odd{background:var(--vlist-bg-striped)}.vlist-item.vlist-item--selected{background-color:var(--vlist-bg-selected)}.vlist-item.vlist-item--odd.vlist-item--selected{background:var(--vlist-bg-selected)}.vlist-item.vlist-item--selected:hover{background-color:var(--vlist-bg-selected-hover)}.vlist-item.vlist-item--odd.vlist-item--selected:hover{background:var(--vlist-bg-selected-hover)}.vlist-item.vlist-item--focused{outline:2px solid var(--vlist-focus-ring,#3b82f6);outline-offset:-2px;z-index:1}.vlist-item--replaced{animation:vlist-fade-in 0.3s ease-out}.vlist-item--placeholder{opacity:0.6;animation:vlist-placeholder-pulse 2s ease-in-out infinite}.vlist-item--placeholder *:not(:has(*)){color:transparent;background-color:var(--vlist-placeholder-bg);border-radius:4px;width:fit-content;line-height:1.2;transform:scaleY(0.8)}.vlist-item--placeholder:is(strong,b,h1,h2,h3,h4):not(:has(*)){background-color:var(--vlist-placeholder-bg-strong)}.vlist-item.vlist-item--selected.vlist-item--focused{outline-color:var(--vlist-border-selected)}.vlist-scrollbar:focus-visible{outline:2px solid currentColor;outline-offset:-2px;opacity:1;pointer-events:auto}.vlist-scrollbar{position:absolute;top:var(--vlist-custom-scrollbar-padding-top);right:var(--vlist-custom-scrollbar-padding-right);bottom:var(--vlist-custom-scrollbar-padding-bottom);width:var(--vlist-custom-scrollbar-width);background-color:var(--vlist-custom-scrollbar-track-color);opacity:0;transition:opacity 0.2s ease-out;touch-action:none;z-index:10;pointer-events:none}.vlist-scrollbar__hover{position:absolute;top:0;right:0;height:100%;z-index:9;touch-action:none;pointer-events:auto}.vlist-scrollbar__hover--horizontal{top:auto;right:auto;bottom:0;left:0;width:100%;height:auto}.vlist-scrollbar--visible{opacity:1;pointer-events:auto}.vlist-scrollbar__thumb{position:absolute;top:0;left:0;right:0;width:100%;background-color:var(--vlist-custom-scrollbar-thumb-color);border-radius:var(--vlist-custom-scrollbar-radius);cursor:pointer;transition:background-color 0.15s ease-out}.vlist-scrollbar__thumb:hover{background-color:var(--vlist-custom-scrollbar-thumb-hover-color)}.vlist-scrollbar--dragging{opacity:1;pointer-events:auto}.vlist-scrollbar--dragging .vlist-scrollbar__thumb{background-color:var(--vlist-custom-scrollbar-thumb-hover-color)}@media (forced-colors:active){.vlist-scrollbar{forced-color-adjust:none;background-color:Canvas}.vlist-scrollbar__thumb{background-color:CanvasText}.vlist-scrollbar__thumb:hover,.vlist-scrollbar--dragging .vlist-scrollbar__thumb{background-color:Highlight}.vlist-scrollbar:focus-visible{outline-color:Highlight}}.vlist-group-header{cursor:default;background-color:var(--vlist-group-header-bg,#f3f4f6);z-index:1;overflow:hidden}.vlist-group-header:hover{background-color:var(--vlist-group-header-bg,#f3f4f6)}.vlist-sticky-header{left:0;right:0;background-color:var(--vlist-group-header-bg,#f3f4f6)}.vlist--horizontal{overflow:hidden}.vlist--horizontal .vlist-viewport{overflow-x:auto;overflow-y:hidden}.vlist--horizontal .vlist-content{height:100%;width:auto}.vlist--horizontal .vlist-item,.vlist--horizontal .vlist-group-header{position:absolute;top:0;bottom:0;left:0;right:auto;display:flex;align-items:center;justify-content:center;border-bottom:none}.vlist-scrollbar--horizontal{top:auto;left:var(--vlist-custom-scrollbar-padding-left);right:var(--vlist-custom-scrollbar-padding-right);bottom:var(--vlist-custom-scrollbar-padding-bottom);width:auto;height:var(--vlist-custom-scrollbar-width)}.vlist-scrollbar--horizontal .vlist-scrollbar__thumb{top:0;left:0;width:auto;height:100%}.vlist-viewport--gutter{padding-right:calc( var(--vlist-custom-scrollbar-padding-right) + var(--vlist-custom-scrollbar-width) + var(--vlist-custom-scrollbar-padding-left) )}.vlist--horizontal .vlist-viewport--gutter{padding-right:0;padding-bottom:calc( var(--vlist-custom-scrollbar-padding-bottom) + var(--vlist-custom-scrollbar-width) + var(--vlist-custom-scrollbar-padding-top) )}.vlist-viewport--custom-scrollbar{scrollbar-width:none;-ms-overflow-style:none}.vlist-viewport--custom-scrollbar::-webkit-scrollbar{display:none}.vlist-viewport--no-scrollbar{scrollbar-width:none;-ms-overflow-style:none}.vlist-viewport--no-scrollbar::-webkit-scrollbar{display:none}.vlist-viewport--gutter-stable{scrollbar-gutter:stable}.vlist-sort-ghost{opacity:0.85;box-shadow:0 4px 16px rgba(0,0,0,0.18);cursor:grabbing}.vlist-item--touch-sort{-webkit-user-select:none;user-select:none;-webkit-touch-callout:none}.vlist-sort-ghost--touch{outline:2px solid var(--vlist-focus-ring,#3b82f6);outline-offset:2px}.vlist-item--drag-source{opacity:0 !important;pointer-events:none}.vlist--sorting{cursor:grabbing;-webkit-user-select:none;user-select:none;-webkit-touch-callout:none}.vlist--sorting *{cursor:inherit !important}.vlist-item--kb-sorting{outline:2px solid var(--vlist-focus-ring,#3b82f6);outline-offset:-2px;background-color:var(--vlist-bg-selected);z-index:1}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vlist",
3
- "version": "3.0.0-next.1",
3
+ "version": "3.0.0-next.3",
4
4
  "description": "Lightweight, high-performance virtual list with zero dependencies",
5
5
  "author": {
6
6
  "name": "Floor IO",
@@ -79,12 +79,15 @@
79
79
  "dev": "bun run build.ts --watch",
80
80
  "test": "bun test",
81
81
  "test:changed": "bun test --changed",
82
- "typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.test.json",
82
+ "test:browser": "bun scripts/rtl-cross-axis-browser.mjs && bun scripts/synthetic-browser.mjs && bun scripts/synthetic-carousel-browser.mjs && bun scripts/synthetic-sortable-browser.mjs",
83
+ "typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.types.json && tsc --noEmit -p tsconfig.test.json",
83
84
  "bench": "bun run scripts/bench.ts",
84
85
  "bench:quick": "bun run scripts/bench.ts --quick",
85
86
  "bench:full": "bun run scripts/bench.ts --full",
86
87
  "bench:compare": "bun run scripts/bench.ts --quick --compare",
87
88
  "size": "bun run scripts/size.ts",
89
+ "heap": "bun run scripts/heap.ts",
90
+ "docs:check": "bun run scripts/docs-check.ts",
88
91
  "release": "bun run scripts/release.ts",
89
92
  "prepublishOnly": "bun run build -- --types && cp README.md README.github.md && cp npm-readme.md README.md",
90
93
  "postpublish": "cp README.github.md README.md && rm README.github.md"
@@ -99,6 +102,7 @@
99
102
  "@types/jsdom": "^27.0.0",
100
103
  "happy-dom": "^20.9.0",
101
104
  "jsdom": "^28.0.0",
105
+ "puppeteer-core": "^24.43.1",
102
106
  "typescript": "^5.0.0"
103
107
  }
104
108
  }
@@ -1,68 +0,0 @@
1
- /**
2
- * vlist - Grid Renderer
3
- * Renders items in a 2D grid layout within the virtual scroll container.
4
- *
5
- * Extends the base renderer pattern but positions items using both
6
- * row offsets (translateY from the size cache) and column offsets
7
- * (translateX calculated from column index and container width).
8
- *
9
- * Key differences from the list renderer:
10
- * - Items are positioned with translate(x, y) instead of just translateY(y)
11
- * - Item width is set to columnWidth (containerWidth / columns - gaps)
12
- * - The "index" in the rendered map is the FLAT ITEM INDEX (not row index)
13
- * - Row offsets come from the size cache (which operates on row indices)
14
- * - Column offsets are calculated from itemIndex % columns
15
- *
16
- * Performance:
17
- * - Element pooling avoids createElement cost
18
- * - Template re-evaluation skipped when item data + state unchanged (change tracking)
19
- * - Position update skipped when coordinates unchanged (position tracking)
20
- * - O(1) Set-based visibility diffing (not O(n) .some())
21
- * - Release grace period prevents boundary thrashing (hover blink, transition replay)
22
- * - Released elements removed from DOM immediately
23
- * - DocumentFragment batched insertion for new elements
24
- */
25
- import type { VListItem, ItemTemplate, Range } from "../../types";
26
- import type { SizeCache } from "../../rendering/sizes";
27
- import type { GridLayout } from "./types";
28
- /** Grid renderer instance */
29
- export interface GridRenderer<T extends VListItem = VListItem> {
30
- /** Render items for a flat item range, positioned in a 2D grid */
31
- render: (items: T[], range: Range, selectedIds: Set<string | number>, focusedIndex: number) => void;
32
- /** Update a single item (used by selection feature for focused item changes) */
33
- updateItem: (index: number, item: T, isSelected: boolean, isFocused: boolean) => void;
34
- /** Update only CSS classes on a rendered item (no template re-evaluation) */
35
- updateItemClasses: (index: number, isSelected: boolean, isFocused: boolean) => void;
36
- /** Get rendered item element by flat item index */
37
- getElement: (index: number) => HTMLElement | undefined;
38
- /**
39
- * Reorder DOM children to match logical item order (by data-index).
40
- * Called on scroll idle so screen readers encounter items in the correct
41
- * sequence. Items are position:absolute so visual layout is unaffected.
42
- */
43
- sortDOM: () => void;
44
- /** Update container width (call on resize) */
45
- updateContainerWidth: (width: number) => void;
46
- /** Clear all rendered items */
47
- clear: () => void;
48
- /** Destroy renderer and cleanup */
49
- destroy: () => void;
50
- }
51
- /**
52
- * Create a grid renderer for managing DOM elements in a 2D layout.
53
- *
54
- * The grid renderer receives flat item ranges (not row ranges) and
55
- * positions each item at the correct (row, col) coordinate.
56
- *
57
- * @param itemsContainer - The DOM element that holds rendered items
58
- * @param template - Item template function
59
- * @param sizeCache - Size cache operating on ROW indices
60
- * @param gridLayout - Grid layout for row/col calculations
61
- * @param classPrefix - CSS class prefix
62
- * @param initialContainerWidth - Initial container width for column sizing
63
- * @param totalItemsGetter - Optional getter for total item count (for aria-setsize)
64
- * @param ariaIdPrefix - Optional unique prefix for element IDs (for aria-activedescendant)
65
- * @param isHorizontal - Whether layout is horizontal (scrolls right)
66
- */
67
- export declare const createGridRenderer: <T extends VListItem = VListItem>(itemsContainer: HTMLElement, template: ItemTemplate<T>, sizeCache: SizeCache, gridLayout: GridLayout, classPrefix: string, initialContainerWidth: number, totalItemsGetter?: () => number, ariaIdPrefix?: string, isHorizontal?: boolean, ariaPosInSetGetter?: (layoutIndex: number) => number, interactive?: boolean) => GridRenderer<T>;
68
- //# sourceMappingURL=renderer.d.ts.map