@effect-tui/react 0.6.0 → 0.6.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.
- package/dist/src/components/ListView.d.ts +5 -2
- package/dist/src/components/ListView.d.ts.map +1 -1
- package/dist/src/components/ListView.js +18 -5
- package/dist/src/components/ListView.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/components/ListView.tsx +34 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effect-tui/react",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "React bindings for @effect-tui/core",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"prepublishOnly": "bun run typecheck && bun run build"
|
|
84
84
|
},
|
|
85
85
|
"dependencies": {
|
|
86
|
-
"@effect-tui/core": "^0.6.
|
|
86
|
+
"@effect-tui/core": "^0.6.1",
|
|
87
87
|
"@effect/platform": "^0.94.0",
|
|
88
88
|
"@effect/platform-bun": "^0.87.0",
|
|
89
89
|
"@effect/rpc": "^0.73.0",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// ListView.tsx —
|
|
1
|
+
// ListView.tsx — Virtualized selection-aware scrolling list
|
|
2
2
|
import type { ReactNode } from "react"
|
|
3
3
|
import { useEffect, useRef } from "react"
|
|
4
4
|
import { useScroll } from "../hooks/use-scroll.js"
|
|
@@ -20,12 +20,15 @@ export interface ListViewProps<T> {
|
|
|
20
20
|
showScrollbar?: boolean
|
|
21
21
|
/** Empty state content when items is empty */
|
|
22
22
|
emptyContent?: ReactNode
|
|
23
|
+
/** Number of extra items to render above/below viewport for smooth scrolling (default: 3) */
|
|
24
|
+
overscan?: number
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
/**
|
|
26
|
-
* A scrolling list view with selection and scroll-into-view behavior.
|
|
28
|
+
* A virtualized scrolling list view with selection and scroll-into-view behavior.
|
|
27
29
|
*
|
|
28
30
|
* Features:
|
|
31
|
+
* - **Virtualized**: Only renders visible items + overscan buffer for performance
|
|
29
32
|
* - Automatically scrolls to keep selected item visible when selection changes
|
|
30
33
|
* - Supports mouse wheel scrolling with macOS-style acceleration
|
|
31
34
|
* - Shows a scroll bar when content overflows
|
|
@@ -68,8 +71,9 @@ export function ListView<T>({
|
|
|
68
71
|
scrollPadding = 0,
|
|
69
72
|
showScrollbar = true,
|
|
70
73
|
emptyContent,
|
|
74
|
+
overscan = 3,
|
|
71
75
|
}: ListViewProps<T>) {
|
|
72
|
-
const { scrollProps, scrollToVisible } = useScroll({
|
|
76
|
+
const { state, scrollProps, scrollToVisible } = useScroll({
|
|
73
77
|
enableKeyboard: false, // Parent handles keyboard for selection
|
|
74
78
|
enableMouseWheel: true, // Free scroll with wheel
|
|
75
79
|
})
|
|
@@ -98,14 +102,36 @@ export function ListView<T>({
|
|
|
98
102
|
return <>{emptyContent}</>
|
|
99
103
|
}
|
|
100
104
|
|
|
105
|
+
// Calculate visible range for virtualization
|
|
106
|
+
const totalHeight = items.length * itemHeight
|
|
107
|
+
const startIndex = Math.max(0, Math.floor(state.offset / itemHeight) - overscan)
|
|
108
|
+
const endIndex = Math.min(items.length, Math.ceil((state.offset + state.viewportSize) / itemHeight) + overscan)
|
|
109
|
+
|
|
110
|
+
// Calculate spacer heights for virtual scrolling
|
|
111
|
+
const topSpacerHeight = startIndex * itemHeight
|
|
112
|
+
const bottomSpacerHeight = Math.max(0, totalHeight - endIndex * itemHeight)
|
|
113
|
+
|
|
114
|
+
// Get visible slice of items
|
|
115
|
+
const visibleItems = items.slice(startIndex, endIndex)
|
|
116
|
+
|
|
101
117
|
return (
|
|
102
118
|
<scroll {...scrollProps} showScrollbar={showScrollbar}>
|
|
103
119
|
<vstack>
|
|
104
|
-
{
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
)
|
|
120
|
+
{/* Virtual top spacer */}
|
|
121
|
+
{topSpacerHeight > 0 && <spacer height={topSpacerHeight} />}
|
|
122
|
+
|
|
123
|
+
{/* Render only visible items */}
|
|
124
|
+
{visibleItems.map((item, i) => {
|
|
125
|
+
const actualIndex = startIndex + i
|
|
126
|
+
return (
|
|
127
|
+
<vstack key={keyExtractor(item, actualIndex)}>
|
|
128
|
+
{renderItem(item, actualIndex, actualIndex === selectedIndex)}
|
|
129
|
+
</vstack>
|
|
130
|
+
)
|
|
131
|
+
})}
|
|
132
|
+
|
|
133
|
+
{/* Virtual bottom spacer */}
|
|
134
|
+
{bottomSpacerHeight > 0 && <spacer height={bottomSpacerHeight} />}
|
|
109
135
|
</vstack>
|
|
110
136
|
</scroll>
|
|
111
137
|
)
|