@pyreon/virtual 0.22.0 → 0.23.0

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 (2) hide show
  1. package/README.md +42 -50
  2. package/package.json +6 -6
package/README.md CHANGED
@@ -1,14 +1,17 @@
1
1
  # @pyreon/virtual
2
2
 
3
- Pyreon adapter for TanStack Virtual. Efficient rendering of large lists with reactive `virtualItems`, `totalSize`, and `isScrolling` signals.
3
+ Pyreon adapter for TanStack Virtual efficient rendering of very large lists.
4
+
5
+ `@pyreon/virtual` wraps `@tanstack/virtual-core` so a Pyreon app can render 10k+ items by only drawing the slice in the viewport. `useVirtualizer` is for element-scoped scrolling (an inner scroll container); `useWindowVirtualizer` is for window-scoped scrolling and is SSR-safe. Both take **options as a function** so reactive signals (count, estimateSize, scrollElement ref) trigger automatic recalculation. The exposed reactive surface — `virtualItems`, `totalSize`, `isScrolling` — is updated in a single `batch()` so consumers don't see torn state mid-scroll.
4
6
 
5
7
  ## Install
6
8
 
7
9
  ```bash
8
- bun add @pyreon/virtual @tanstack/virtual-core
10
+ bun add @pyreon/virtual @pyreon/core @pyreon/reactivity
11
+ # @tanstack/virtual-core is a hard dependency, installed automatically
9
12
  ```
10
13
 
11
- ## Quick Start
14
+ ## Quick start (element-scoped)
12
15
 
13
16
  ```tsx
14
17
  import { signal } from '@pyreon/reactivity'
@@ -30,7 +33,6 @@ function VirtualList() {
30
33
  <div style={`height: ${totalSize()}px; position: relative;`}>
31
34
  {virtualItems().map((row) => (
32
35
  <div
33
- key={row.key}
34
36
  style={`position: absolute; top: 0; width: 100%; height: ${row.size}px; transform: translateY(${row.start}px);`}
35
37
  >
36
38
  {items[row.index]}
@@ -42,26 +44,18 @@ function VirtualList() {
42
44
  }
43
45
  ```
44
46
 
45
- ## API
46
-
47
- ### `useVirtualizer(options)`
48
-
49
- Create a reactive virtualizer for element-based scrolling. Options are passed as a function so reactive signals can be read inside, and the virtualizer updates automatically when they change.
50
-
51
- | Parameter | Type | Description |
52
- | --------- | ----------------------------- | ------------------------------------- |
53
- | `options` | `() => UseVirtualizerOptions` | Function returning virtualizer config |
47
+ ## `useVirtualizer(() => options)`
54
48
 
55
- Options extend `VirtualizerOptions` from `@tanstack/virtual-core` with `observeElementRect`, `observeElementOffset`, and `scrollToFn` pre-filled (overridable).
49
+ Element-scoped virtualizer. Pre-fills `observeElementRect`, `observeElementOffset`, and `scrollToFn` for DOM element scrolling — override if you need custom scroll handling.
56
50
 
57
- **Returns:** `UseVirtualizerResult` with:
51
+ Returns `UseVirtualizerResult`:
58
52
 
59
- | Property | Type | Description |
60
- | -------------- | ----------------------- | -------------------------------------------- |
61
- | `instance` | `Virtualizer` | The underlying TanStack Virtualizer instance |
62
- | `virtualItems` | `Signal<VirtualItem[]>` | Currently visible virtual items |
63
- | `totalSize` | `Signal<number>` | Total scrollable size in pixels |
64
- | `isScrolling` | `Signal<boolean>` | Whether the user is currently scrolling |
53
+ | Property | Type | Notes |
54
+ | -------------- | --------------------------------------- | ------------------------------------------------------ |
55
+ | `instance` | `Virtualizer<TScrollElement, TItemElement>` | Raw TanStack instance — use for `scrollToIndex`, etc. |
56
+ | `virtualItems` | `Signal<VirtualItem[]>` | Visible items |
57
+ | `totalSize` | `Signal<number>` | Total scrollable size (px) |
58
+ | `isScrolling` | `Signal<boolean>` | Active scroll |
65
59
 
66
60
  ```ts
67
61
  const parentRef = signal<HTMLDivElement | null>(null)
@@ -74,19 +68,14 @@ const { virtualItems, totalSize, isScrolling, instance } = useVirtualizer(() =>
74
68
  overscan: 5,
75
69
  }))
76
70
 
77
- // Scroll programmatically:
71
+ // Imperative scroll:
78
72
  instance.scrollToIndex(500)
73
+ instance.scrollToOffset(2000)
79
74
  ```
80
75
 
81
- ### `useWindowVirtualizer(options)`
76
+ ## `useWindowVirtualizer(() => options)`
82
77
 
83
- Create a reactive virtualizer for window-based scrolling. The scroll element is automatically set to `window`. SSR-safe — checks for `window` and `document` availability.
84
-
85
- | Parameter | Type | Description |
86
- | --------- | ----------------------------------- | -------------------------------------------------------- |
87
- | `options` | `() => UseWindowVirtualizerOptions` | Function returning config (no `getScrollElement` needed) |
88
-
89
- **Returns:** `UseWindowVirtualizerResult` — same shape as `UseVirtualizerResult` but typed with `Window` as the scroll element.
78
+ Window-scoped virtualizer. Pre-fills `observeElementRect: observeWindowRect`, `observeElementOffset: observeWindowOffset`, and `scrollToFn: windowScroll`. SSR-safe — checks for `window` / `document` availability before mounting observers.
90
79
 
91
80
  ```tsx
92
81
  function WindowList() {
@@ -101,7 +90,6 @@ function WindowList() {
101
90
  <div style={`height: ${totalSize()}px; position: relative;`}>
102
91
  {virtualItems().map((row) => (
103
92
  <div
104
- key={row.key}
105
93
  style={`position: absolute; top: 0; width: 100%; height: ${row.size}px; transform: translateY(${row.start}px);`}
106
94
  >
107
95
  {items[row.index]}
@@ -114,9 +102,9 @@ function WindowList() {
114
102
 
115
103
  ## Patterns
116
104
 
117
- ### Dynamic Item Sizes
105
+ ### Dynamic item sizes via `measureElement`
118
106
 
119
- Use `measureElement` for variable-height items that are measured after render.
107
+ For variable-height items that need to be measured after render:
120
108
 
121
109
  ```tsx
122
110
  import { measureElement } from '@pyreon/virtual'
@@ -128,17 +116,15 @@ const { virtualItems, totalSize, instance } = useVirtualizer(() => ({
128
116
  measureElement,
129
117
  }))
130
118
 
131
- // In render, set the ref on each item:
119
+ // Per row:
132
120
  virtualItems().map((row) => (
133
- <div key={row.key} ref={(el) => instance.measureElement(el)} data-index={row.index}>
121
+ <div ref={(el) => instance.measureElement(el)} data-index={row.index}>
134
122
  {items[row.index]}
135
123
  </div>
136
124
  ))
137
125
  ```
138
126
 
139
- ### Horizontal Lists
140
-
141
- Set the `horizontal` option for horizontal virtualization.
127
+ ### Horizontal lists
142
128
 
143
129
  ```ts
144
130
  const { virtualItems, totalSize } = useVirtualizer(() => ({
@@ -149,32 +135,38 @@ const { virtualItems, totalSize } = useVirtualizer(() => ({
149
135
  }))
150
136
  ```
151
137
 
152
- ### Reactive Count
153
-
154
- Since options are a function, changing the count signal re-calculates virtual items automatically.
138
+ ### Reactive count (filtered lists)
155
139
 
156
140
  ```ts
157
141
  const filteredItems = signal(allItems)
142
+
158
143
  const { virtualItems } = useVirtualizer(() => ({
159
144
  count: filteredItems().length,
160
145
  getScrollElement: () => parentRef(),
161
146
  estimateSize: () => 40,
162
147
  }))
163
148
 
164
- // Updating filteredItems triggers recalculation
165
- filteredItems.set(allItems.filter((i) => i.includes(search())))
149
+ // filteredItems.set(allItems.filter(…)) automatic recalculation
166
150
  ```
167
151
 
168
152
  ## Re-exports from `@tanstack/virtual-core`
169
153
 
170
- **Runtime:** `defaultKeyExtractor`, `defaultRangeExtractor`, `observeElementOffset`, `observeElementRect`, `observeWindowOffset`, `observeWindowRect`, `elementScroll`, `windowScroll`, `measureElement`, `Virtualizer`
154
+ **Runtime**: `defaultKeyExtractor`, `defaultRangeExtractor`, `observeElementOffset`, `observeElementRect`, `observeWindowOffset`, `observeWindowRect`, `elementScroll`, `windowScroll`, `measureElement`, `Virtualizer`.
171
155
 
172
- **Types:** `VirtualizerOptions`, `VirtualItem`, `Range`, `Rect`, `ScrollToOptions`
156
+ **Types**: `VirtualizerOptions`, `VirtualItem`, `Range`, `Rect`, `ScrollToOptions`.
173
157
 
174
158
  ## Gotchas
175
159
 
176
- - Options must be a function `() => opts` for reactive tracking. The virtualizer re-calculates when signals read inside the function change.
177
- - The `instance` is the raw TanStack Virtualizer — use it for imperative methods like `scrollToIndex()` and `scrollToOffset()`.
178
- - `virtualItems`, `totalSize`, and `isScrolling` are Pyreon signals updated via `batch()` for efficient reactive notifications.
179
- - The virtualizer's DOM observers are mounted via `onMount` and cleaned up via `onUnmount`. The component must be mounted for scroll observation to work.
180
- - `useWindowVirtualizer` checks for `window` availability and provides a safe fallback for SSR.
160
+ - **Options must be a function** `() => opts` for reactive tracking. Reading signals inside is the mechanism for live recalculation.
161
+ - **`instance` is the raw TanStack Virtualizer** — use it for imperative methods (`scrollToIndex`, `scrollToOffset`, `getVirtualItemForOffset`). The signals are the reactive subset.
162
+ - **Signals update via `batch()`** — `virtualItems`, `totalSize`, and `isScrolling` flip together; consumers don't see torn state mid-scroll frame.
163
+ - **Observers mount via `onMount`, dispose via `onUnmount`** the component must be mounted before scroll observation starts. SSR renders see an empty `virtualItems` array until hydration.
164
+ - **`useWindowVirtualizer` is SSR-safe** — checks for `window` and `document` before mounting; non-browser environments get the safe fallback shape.
165
+
166
+ ## Documentation
167
+
168
+ Full docs: [docs.pyreon.dev/docs/virtual](https://docs.pyreon.dev/docs/virtual) (or `docs/docs/virtual.md` in this repo).
169
+
170
+ ## License
171
+
172
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pyreon/virtual",
3
- "version": "0.22.0",
3
+ "version": "0.23.0",
4
4
  "description": "Pyreon adapter for TanStack Virtual",
5
5
  "homepage": "https://github.com/pyreon/pyreon/tree/main/packages/virtual#readme",
6
6
  "bugs": {
@@ -42,15 +42,15 @@
42
42
  "lint": "oxlint ."
43
43
  },
44
44
  "dependencies": {
45
- "@pyreon/core": "^0.22.0",
46
- "@pyreon/reactivity": "^0.22.0",
45
+ "@pyreon/core": "^0.23.0",
46
+ "@pyreon/reactivity": "^0.23.0",
47
47
  "@tanstack/virtual-core": "^3.0.0"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@happy-dom/global-registrator": "^20.8.9",
51
- "@pyreon/core": "^0.22.0",
51
+ "@pyreon/core": "^0.23.0",
52
52
  "@pyreon/manifest": "0.13.1",
53
- "@pyreon/reactivity": "^0.22.0",
54
- "@pyreon/runtime-dom": "^0.22.0"
53
+ "@pyreon/reactivity": "^0.23.0",
54
+ "@pyreon/runtime-dom": "^0.23.0"
55
55
  }
56
56
  }