@pyreon/virtual 0.24.5 → 0.25.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.
@@ -1,119 +0,0 @@
1
- import { onMount, onUnmount } from '@pyreon/core'
2
- import type { Signal } from '@pyreon/reactivity'
3
- import { batch, effect, signal } from '@pyreon/reactivity'
4
- import {
5
- elementScroll,
6
- observeElementOffset,
7
- observeElementRect,
8
- type VirtualItem,
9
- Virtualizer,
10
- type VirtualizerOptions,
11
- } from '@tanstack/virtual-core'
12
-
13
- export type UseVirtualizerOptions<
14
- TScrollElement extends Element,
15
- TItemElement extends Element,
16
- > = () => Omit<
17
- VirtualizerOptions<TScrollElement, TItemElement>,
18
- 'observeElementRect' | 'observeElementOffset' | 'scrollToFn'
19
- > &
20
- Partial<
21
- Pick<
22
- VirtualizerOptions<TScrollElement, TItemElement>,
23
- 'observeElementRect' | 'observeElementOffset' | 'scrollToFn'
24
- >
25
- >
26
-
27
- export interface UseVirtualizerResult<
28
- TScrollElement extends Element,
29
- TItemElement extends Element,
30
- > {
31
- /** The virtualizer instance — read to access all methods. */
32
- instance: Virtualizer<TScrollElement, TItemElement>
33
- /** Reactive signal of currently visible virtual items. */
34
- virtualItems: Signal<VirtualItem[]>
35
- /** Reactive signal of the total scrollable size in pixels. */
36
- totalSize: Signal<number>
37
- /** Reactive signal indicating whether the user is scrolling. */
38
- isScrolling: Signal<boolean>
39
- }
40
-
41
- /**
42
- * Create a reactive TanStack Virtual virtualizer for element-based scrolling.
43
- *
44
- * Options are passed as a function so reactive signals (e.g. count, estimateSize)
45
- * can be read inside, and the virtualizer updates automatically when they change.
46
- *
47
- * @example
48
- * const parentRef = signal<HTMLDivElement | null>(null)
49
- * const virtual = useVirtualizer(() => ({
50
- * count: 10000,
51
- * getScrollElement: () => parentRef(),
52
- * estimateSize: () => 35,
53
- * }))
54
- * // virtual.virtualItems() — array of visible VirtualItem
55
- * // virtual.totalSize() — total height/width for the inner container
56
- */
57
- export function useVirtualizer<TScrollElement extends Element, TItemElement extends Element>(
58
- options: UseVirtualizerOptions<TScrollElement, TItemElement>,
59
- ): UseVirtualizerResult<TScrollElement, TItemElement> {
60
- const resolvedOptions: VirtualizerOptions<TScrollElement, TItemElement> = {
61
- observeElementRect,
62
- observeElementOffset,
63
- scrollToFn: elementScroll,
64
- ...options(),
65
- }
66
-
67
- const virtualItems = signal<VirtualItem[]>([])
68
- const totalSize = signal(0)
69
- const isScrolling = signal(false)
70
-
71
- // Store latest user options so onChange always reads the freshest reference
72
- let latestUserOpts = options()
73
-
74
- const instance = new Virtualizer<TScrollElement, TItemElement>(resolvedOptions)
75
-
76
- // Track reactive options: when signals inside options() change, update the virtualizer.
77
- const effectCleanup = effect(() => {
78
- latestUserOpts = options()
79
- instance.setOptions({
80
- ...instance.options,
81
- ...latestUserOpts,
82
- onChange: (inst, sync) => {
83
- batch(() => {
84
- virtualItems.set(inst.getVirtualItems())
85
- totalSize.set(inst.getTotalSize())
86
- isScrolling.set(inst.isScrolling)
87
- })
88
- // Read latest opts to avoid stale closure
89
- latestUserOpts.onChange?.(inst, sync)
90
- },
91
- })
92
-
93
- // After updating options, recalculate and re-emit
94
- instance._willUpdate()
95
- batch(() => {
96
- virtualItems.set(instance.getVirtualItems())
97
- totalSize.set(instance.getTotalSize())
98
- })
99
- })
100
-
101
- // Lifecycle: mount observers, clean up on unmount.
102
- let mountCleanup: (() => void) | undefined
103
- onMount(() => {
104
- mountCleanup = instance._didMount()
105
- instance._willUpdate()
106
- batch(() => {
107
- virtualItems.set(instance.getVirtualItems())
108
- totalSize.set(instance.getTotalSize())
109
- })
110
- return undefined
111
- })
112
-
113
- onUnmount(() => {
114
- effectCleanup.dispose()
115
- mountCleanup?.()
116
- })
117
-
118
- return { instance, virtualItems, totalSize, isScrolling }
119
- }
@@ -1,101 +0,0 @@
1
- import { onMount, onUnmount } from '@pyreon/core'
2
- import type { Signal } from '@pyreon/reactivity'
3
- import { batch, effect, signal } from '@pyreon/reactivity'
4
- import {
5
- observeWindowOffset,
6
- observeWindowRect,
7
- type VirtualItem,
8
- Virtualizer,
9
- type VirtualizerOptions,
10
- windowScroll,
11
- } from '@tanstack/virtual-core'
12
-
13
- export type UseWindowVirtualizerOptions<TItemElement extends Element> = () => Omit<
14
- VirtualizerOptions<Window, TItemElement>,
15
- 'observeElementRect' | 'observeElementOffset' | 'scrollToFn' | 'getScrollElement'
16
- > &
17
- Partial<
18
- Pick<
19
- VirtualizerOptions<Window, TItemElement>,
20
- 'observeElementRect' | 'observeElementOffset' | 'scrollToFn'
21
- >
22
- >
23
-
24
- export interface UseWindowVirtualizerResult<TItemElement extends Element> {
25
- instance: Virtualizer<Window, TItemElement>
26
- virtualItems: Signal<VirtualItem[]>
27
- totalSize: Signal<number>
28
- isScrolling: Signal<boolean>
29
- }
30
-
31
- /**
32
- * Create a reactive TanStack Virtual virtualizer for window-based scrolling.
33
- *
34
- * @example
35
- * const virtual = useWindowVirtualizer(() => ({
36
- * count: 10000,
37
- * estimateSize: () => 35,
38
- * }))
39
- */
40
- export function useWindowVirtualizer<TItemElement extends Element>(
41
- options: UseWindowVirtualizerOptions<TItemElement>,
42
- ): UseWindowVirtualizerResult<TItemElement> {
43
- const virtualItems = signal<VirtualItem[]>([])
44
- const totalSize = signal(0)
45
- const isScrolling = signal(false)
46
-
47
- const resolvedOptions: VirtualizerOptions<Window, TItemElement> = {
48
- observeElementRect: observeWindowRect,
49
- observeElementOffset: observeWindowOffset,
50
- scrollToFn: windowScroll,
51
- initialOffset: typeof document !== 'undefined' ? window.scrollY : 0,
52
- getScrollElement: () => (typeof window !== 'undefined' ? window : (null as unknown as Window)),
53
- ...options(),
54
- }
55
-
56
- // Store latest user options so onChange always reads the freshest reference
57
- let latestUserOpts = options()
58
-
59
- const instance = new Virtualizer<Window, TItemElement>(resolvedOptions)
60
-
61
- const effectCleanup = effect(() => {
62
- latestUserOpts = options()
63
- instance.setOptions({
64
- ...instance.options,
65
- ...latestUserOpts,
66
- onChange: (inst, sync) => {
67
- batch(() => {
68
- virtualItems.set(inst.getVirtualItems())
69
- totalSize.set(inst.getTotalSize())
70
- isScrolling.set(inst.isScrolling)
71
- })
72
- // Read latest opts to avoid stale closure
73
- latestUserOpts.onChange?.(inst, sync)
74
- },
75
- })
76
-
77
- instance._willUpdate()
78
- batch(() => {
79
- virtualItems.set(instance.getVirtualItems())
80
- totalSize.set(instance.getTotalSize())
81
- })
82
- })
83
-
84
- let mountCleanup: (() => void) | undefined
85
- onMount(() => {
86
- mountCleanup = instance._didMount()
87
- instance._willUpdate()
88
- batch(() => {
89
- virtualItems.set(instance.getVirtualItems())
90
- totalSize.set(instance.getTotalSize())
91
- })
92
- return undefined
93
- })
94
-
95
- onUnmount(() => {
96
- effectCleanup.dispose()
97
- mountCleanup?.()
98
- })
99
-
100
- return { instance, virtualItems, totalSize, isScrolling }
101
- }