@react-fabric/data 1.0.0-beta.6 → 1.0.0-beta.61
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/{src/histogram/Histogram.module.css → css/_histogram.css} +16 -13
- package/{dist/esm/tree/types.js → css/styles.css} +2 -7
- package/dist/cjs/checklist/CheckList.js +27 -4
- package/dist/cjs/histogram/Histogram.js +39 -9
- package/dist/cjs/hooks/useFilteredList.js +88 -10
- package/dist/cjs/hooks/usePagination.js +27 -4
- package/dist/cjs/hooks/useSelectableList.js +29 -0
- package/dist/cjs/i18n/ar.json.js +8 -1
- package/dist/cjs/i18n/en.json.js +8 -1
- package/dist/cjs/json/JsonViewer.js +68 -28
- package/dist/cjs/pagination/Pagination.js +14 -4
- package/dist/cjs/table/AddColumn.js +3 -3
- package/dist/cjs/table/BodyCell.js +14 -6
- package/dist/cjs/table/BodyRow.js +4 -4
- package/dist/cjs/table/CheckboxCell.js +2 -2
- package/dist/cjs/table/ColumnFilters.js +97 -0
- package/dist/cjs/table/HeaderCell.js +71 -43
- package/dist/cjs/table/Table.js +62 -13
- package/dist/cjs/table/types.js +29 -0
- package/dist/cjs/table/useResizer.js +4 -4
- package/dist/cjs/table/useVirtualTable.js +127 -19
- package/dist/cjs/tree/TreeNode.js +50 -32
- package/dist/cjs/tree/TreePanel.js +52 -19
- package/dist/cjs/tree/useTree.js +199 -38
- package/dist/cjs/tree/utils.js +39 -12
- package/dist/cjs/virtual/Gallery.js +97 -45
- package/dist/cjs/virtual/List.js +93 -39
- package/dist/cjs/virtual/Scroller.js +16 -8
- package/dist/esm/checklist/CheckList.js +28 -5
- package/dist/esm/histogram/Histogram.js +41 -11
- package/dist/esm/hooks/useFilteredList.js +89 -11
- package/dist/esm/hooks/usePagination.js +27 -4
- package/dist/esm/hooks/useSelectableList.js +29 -0
- package/dist/esm/i18n/ar.json.js +8 -2
- package/dist/esm/i18n/en.json.js +8 -2
- package/dist/esm/json/JsonViewer.js +69 -29
- package/dist/esm/pagination/Pagination.js +15 -5
- package/dist/esm/table/AddColumn.js +4 -4
- package/dist/esm/table/BodyCell.js +15 -7
- package/dist/esm/table/BodyRow.js +4 -4
- package/dist/esm/table/CheckboxCell.js +3 -3
- package/dist/esm/table/ColumnFilters.js +97 -0
- package/dist/esm/table/HeaderCell.js +72 -44
- package/dist/esm/table/Table.js +63 -14
- package/dist/esm/table/types.js +29 -0
- package/dist/esm/table/useResizer.js +4 -4
- package/dist/esm/table/useVirtualTable.js +129 -21
- package/dist/esm/tree/TreeNode.js +53 -35
- package/dist/esm/tree/TreePanel.js +54 -21
- package/dist/esm/tree/useTree.js +202 -41
- package/dist/esm/tree/utils.js +40 -13
- package/dist/esm/virtual/Gallery.js +97 -45
- package/dist/esm/virtual/List.js +93 -39
- package/dist/esm/virtual/Scroller.js +17 -9
- package/dist/types/checklist/CheckList.d.ts +22 -0
- package/dist/types/histogram/Histogram.d.ts +28 -1
- package/dist/types/hooks/useFilteredList.d.ts +66 -5
- package/dist/types/hooks/usePagination.d.ts +19 -0
- package/dist/types/hooks/useSelectableList.d.ts +29 -0
- package/dist/types/json/JsonViewer.d.ts +27 -2
- package/dist/types/pagination/Pagination.d.ts +10 -1
- package/dist/types/table/ColumnFilters.d.ts +9 -0
- package/dist/types/table/HeaderCell.d.ts +1 -1
- package/dist/types/table/Table.d.ts +14 -1
- package/dist/types/table/types.d.ts +46 -7
- package/dist/types/table/useVirtualTable.d.ts +23 -9
- package/dist/types/tree/TreeNode.d.ts +1 -1
- package/dist/types/tree/TreePanel.d.ts +42 -1
- package/dist/types/tree/types.d.ts +65 -21
- package/dist/types/tree/useTree.d.ts +9 -10
- package/dist/types/tree/utils.d.ts +10 -3
- package/dist/types/virtual/Gallery.d.ts +42 -3
- package/dist/types/virtual/List.d.ts +42 -3
- package/package.json +16 -14
- package/src/checklist/CheckList.tsx +39 -7
- package/src/histogram/Histogram.tsx +43 -19
- package/src/hooks/useFilteredList.tsx +109 -9
- package/src/hooks/usePagination.tsx +32 -7
- package/src/hooks/useSelectableList.ts +29 -0
- package/src/i18n/ar.json +5 -0
- package/src/i18n/en.json +5 -0
- package/src/json/JsonViewer.tsx +76 -37
- package/src/pagination/Pagination.tsx +15 -5
- package/src/table/AddColumn.tsx +4 -4
- package/src/table/BodyCell.tsx +24 -7
- package/src/table/BodyRow.tsx +7 -4
- package/src/table/CheckboxCell.tsx +5 -5
- package/src/table/ColumnFilters.tsx +105 -0
- package/src/table/HeaderCell.tsx +74 -21
- package/src/table/Table.tsx +107 -12
- package/src/table/types.ts +49 -10
- package/src/table/useResizer.ts +5 -3
- package/src/table/useVirtualTable.ts +158 -27
- package/src/tree/TreeNode.tsx +96 -62
- package/src/tree/TreePanel.tsx +88 -22
- package/src/tree/types.ts +73 -23
- package/src/tree/useTree.ts +220 -44
- package/src/tree/utils.ts +58 -20
- package/src/virtual/Gallery.tsx +118 -54
- package/src/virtual/List.tsx +99 -39
- package/src/virtual/Scroller.tsx +25 -9
- package/dist/cjs/histogram/Histogram.module.css +0 -1
- package/dist/cjs/tree/types.js +0 -30
- package/dist/esm/histogram/Histogram.module.css +0 -1
package/src/virtual/Gallery.tsx
CHANGED
|
@@ -50,7 +50,7 @@ export interface VirtualGalleryRef {
|
|
|
50
50
|
|
|
51
51
|
export interface VirtualGalleryProps<T> extends TestProps {
|
|
52
52
|
children: (props: {
|
|
53
|
-
|
|
53
|
+
item: T;
|
|
54
54
|
index: number;
|
|
55
55
|
isLast: boolean;
|
|
56
56
|
}) => ReactElement;
|
|
@@ -67,6 +67,12 @@ export interface VirtualGalleryProps<T> extends TestProps {
|
|
|
67
67
|
* default item width
|
|
68
68
|
*/
|
|
69
69
|
defaultWidth?: number;
|
|
70
|
+
/**
|
|
71
|
+
* full width list
|
|
72
|
+
*/
|
|
73
|
+
fullWidth?: boolean;
|
|
74
|
+
maxWidth?: string | number;
|
|
75
|
+
minWidth?: string | number;
|
|
70
76
|
/**
|
|
71
77
|
* padding
|
|
72
78
|
*/
|
|
@@ -75,6 +81,7 @@ export interface VirtualGalleryProps<T> extends TestProps {
|
|
|
75
81
|
* data list
|
|
76
82
|
*/
|
|
77
83
|
items: T[];
|
|
84
|
+
total?: number;
|
|
78
85
|
/**
|
|
79
86
|
* loading state
|
|
80
87
|
*/
|
|
@@ -87,7 +94,7 @@ export interface VirtualGalleryProps<T> extends TestProps {
|
|
|
87
94
|
/**
|
|
88
95
|
* load more callback
|
|
89
96
|
*/
|
|
90
|
-
onLoadMore?: () => void;
|
|
97
|
+
onLoadMore?: (lastIndex?: number) => void;
|
|
91
98
|
/**
|
|
92
99
|
* scroll handler
|
|
93
100
|
*/
|
|
@@ -96,10 +103,14 @@ export interface VirtualGalleryProps<T> extends TestProps {
|
|
|
96
103
|
|
|
97
104
|
const createItemList = memoize((items) => items);
|
|
98
105
|
|
|
99
|
-
const _VirtualGallery = <T extends
|
|
106
|
+
const _VirtualGallery = <T extends AnyObject>({
|
|
100
107
|
items,
|
|
101
108
|
children,
|
|
102
109
|
columns,
|
|
110
|
+
fullWidth,
|
|
111
|
+
maxWidth,
|
|
112
|
+
minWidth,
|
|
113
|
+
total = 0,
|
|
103
114
|
padding = "md",
|
|
104
115
|
defaultHeight: height = 48,
|
|
105
116
|
defaultWidth: width = 48,
|
|
@@ -112,18 +123,18 @@ const _VirtualGallery = <T extends KeyValue>({
|
|
|
112
123
|
...aria
|
|
113
124
|
}: VirtualGalleryProps<T>) => {
|
|
114
125
|
const count = useDeferredValue(items.length);
|
|
115
|
-
const extraSize = useMemo(() => (onLoadMore ?
|
|
126
|
+
const extraSize = useMemo(() => (onLoadMore ? 8 : 0), [onLoadMore]);
|
|
116
127
|
const itemList: Array<{
|
|
117
128
|
index: number;
|
|
118
|
-
|
|
129
|
+
item: T;
|
|
119
130
|
isLast: boolean;
|
|
120
131
|
isSticky: boolean;
|
|
121
132
|
}> = createItemList(
|
|
122
|
-
items.map((item: AnyObject,
|
|
133
|
+
items.map((item: AnyObject, index) => {
|
|
123
134
|
return {
|
|
124
|
-
index
|
|
125
|
-
|
|
126
|
-
isLast:
|
|
135
|
+
index,
|
|
136
|
+
item,
|
|
137
|
+
isLast: index + 1 === items.length,
|
|
127
138
|
};
|
|
128
139
|
}),
|
|
129
140
|
);
|
|
@@ -139,9 +150,19 @@ const _VirtualGallery = <T extends KeyValue>({
|
|
|
139
150
|
if (scrollerRef.current) {
|
|
140
151
|
const el = scrollerRef.current;
|
|
141
152
|
const handler = () => {
|
|
153
|
+
const pad = parseInt(
|
|
154
|
+
getComputedStyle(el.firstElementChild?.firstElementChild as any)
|
|
155
|
+
.padding ?? 8,
|
|
156
|
+
);
|
|
142
157
|
setColumnCount(
|
|
143
158
|
columns ??
|
|
144
|
-
Math.max(
|
|
159
|
+
Math.max(
|
|
160
|
+
1,
|
|
161
|
+
Math.floor(
|
|
162
|
+
((el?.offsetWidth ?? 800) - 32 - (isNaN(pad) ? 8 : pad) * 2) /
|
|
163
|
+
width,
|
|
164
|
+
),
|
|
165
|
+
),
|
|
145
166
|
);
|
|
146
167
|
};
|
|
147
168
|
const ob = new ResizeObserver(handler);
|
|
@@ -161,13 +182,14 @@ const _VirtualGallery = <T extends KeyValue>({
|
|
|
161
182
|
count: Math.ceil(count / columnCount),
|
|
162
183
|
getScrollElement: () => scrollerRef.current,
|
|
163
184
|
estimateSize: () => height,
|
|
185
|
+
overscan: 0,
|
|
164
186
|
});
|
|
165
187
|
|
|
166
188
|
const virtualItems = virtualizer.getVirtualItems();
|
|
167
189
|
|
|
168
190
|
const currentIndex = useRef(0);
|
|
169
191
|
useEffect(() => {
|
|
170
|
-
currentIndex.current = virtualItems[
|
|
192
|
+
currentIndex.current = virtualItems[0]?.index ?? 0;
|
|
171
193
|
if (
|
|
172
194
|
scrollerRef.current &&
|
|
173
195
|
virtualItems[0] &&
|
|
@@ -205,47 +227,44 @@ const _VirtualGallery = <T extends KeyValue>({
|
|
|
205
227
|
}, [count, calculateActiveScroll]);
|
|
206
228
|
|
|
207
229
|
useEffect(() => {
|
|
208
|
-
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
db();
|
|
218
|
-
return () => db.cancel();
|
|
219
|
-
}, [onLoadMore, loading, itemList, virtualItems, count, columnCount]);
|
|
220
|
-
|
|
221
|
-
useEffect(() => {
|
|
222
|
-
const firstItem = virtualItems[0]?.index;
|
|
223
|
-
if (
|
|
224
|
-
onLoadMore &&
|
|
225
|
-
firstItem !== undefined &&
|
|
226
|
-
items[firstItem * columnCount] === null
|
|
227
|
-
) {
|
|
230
|
+
if (onLoadMore) {
|
|
231
|
+
const firstItem = virtualItems[0]?.index;
|
|
232
|
+
const lastItem = virtualItems?.slice(-1).pop()?.index;
|
|
233
|
+
const firstIndex = firstItem * columnCount;
|
|
234
|
+
const lastIndex = (lastItem ?? 0) * columnCount + columnCount;
|
|
235
|
+
const canLoadPrevPage =
|
|
236
|
+
firstItem !== undefined && items[firstIndex] === null;
|
|
237
|
+
const canLoadNextPage =
|
|
238
|
+
lastItem !== undefined && lastIndex >= count - 1 && count < total;
|
|
228
239
|
const db = debounce(onLoadMore);
|
|
229
|
-
|
|
240
|
+
if (canLoadPrevPage) {
|
|
241
|
+
db(firstIndex);
|
|
242
|
+
} else if (!loading && canLoadNextPage) {
|
|
243
|
+
db(lastIndex + 1);
|
|
244
|
+
}
|
|
230
245
|
return () => db.cancel();
|
|
231
246
|
}
|
|
232
|
-
}, [onLoadMore,
|
|
247
|
+
}, [onLoadMore, virtualItems]);
|
|
233
248
|
|
|
249
|
+
const isRenderedRef = useRef(false);
|
|
234
250
|
useLayoutEffect(() => {
|
|
235
|
-
|
|
251
|
+
if (!isRenderedRef.current && itemList.length > 0) {
|
|
252
|
+
isRenderedRef.current = true;
|
|
236
253
|
initialScroll &&
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
254
|
+
setTimeout(() => {
|
|
255
|
+
virtualizer.scrollToIndex(Math.floor(initialScroll / columnCount), {
|
|
256
|
+
align: "start",
|
|
257
|
+
});
|
|
258
|
+
}, 200);
|
|
259
|
+
}
|
|
260
|
+
}, [columnCount, initialScroll, itemList]);
|
|
242
261
|
|
|
243
262
|
useImperativeHandle(
|
|
244
263
|
galleryRef,
|
|
245
264
|
() => ({
|
|
246
265
|
scrollToItem(index) {
|
|
247
266
|
virtualizer.scrollToIndex(Math.floor(index / columnCount), {
|
|
248
|
-
align: "
|
|
267
|
+
align: "auto",
|
|
249
268
|
});
|
|
250
269
|
},
|
|
251
270
|
hilight(index) {
|
|
@@ -253,7 +272,7 @@ const _VirtualGallery = <T extends KeyValue>({
|
|
|
253
272
|
?.querySelector(".hilight")
|
|
254
273
|
?.classList.remove("hilight", "ring-1");
|
|
255
274
|
virtualizer.scrollToIndex(Math.floor(index / columnCount), {
|
|
256
|
-
align: "
|
|
275
|
+
align: "auto",
|
|
257
276
|
});
|
|
258
277
|
setTimeout(() => {
|
|
259
278
|
scrollerRef.current
|
|
@@ -274,53 +293,65 @@ const _VirtualGallery = <T extends KeyValue>({
|
|
|
274
293
|
<div
|
|
275
294
|
ref={scrollerRef}
|
|
276
295
|
onScroll={handleScroll}
|
|
277
|
-
className={classNames(
|
|
278
|
-
"area-content bg-base overflow-auto contain-strict",
|
|
279
|
-
)}
|
|
296
|
+
className={classNames("area-content bg-base overflow-auto")}
|
|
280
297
|
{...aria}
|
|
281
298
|
>
|
|
282
299
|
<div
|
|
283
300
|
className={classNames(
|
|
284
|
-
"
|
|
301
|
+
"flex flex-nowrap justify-center gap-2 min-h-full",
|
|
285
302
|
)}
|
|
286
303
|
>
|
|
287
304
|
<div
|
|
288
305
|
className={classNames(
|
|
289
306
|
"box-content",
|
|
307
|
+
fullWidth && "flex-1",
|
|
290
308
|
padding === "sm" && "p-1",
|
|
291
309
|
padding === "md" && "p-2",
|
|
292
310
|
padding === "lg" && "p-4",
|
|
293
311
|
)}
|
|
312
|
+
style={{
|
|
313
|
+
minWidth,
|
|
314
|
+
maxWidth,
|
|
315
|
+
}}
|
|
294
316
|
>
|
|
295
317
|
<div
|
|
296
318
|
style={{
|
|
297
|
-
height: virtualizer.getTotalSize(),
|
|
319
|
+
height: virtualizer.getTotalSize() + extraSize,
|
|
298
320
|
}}
|
|
299
321
|
>
|
|
300
322
|
<div style={{ height: virtualItems[0]?.start }} />
|
|
301
|
-
{virtualItems.map(({ key, index
|
|
323
|
+
{virtualItems.map(({ key, index }) => (
|
|
302
324
|
<div
|
|
303
325
|
className="flex"
|
|
304
326
|
key={key}
|
|
305
327
|
data-index={index}
|
|
306
|
-
ref={measureElement}
|
|
328
|
+
ref={virtualizer.measureElement}
|
|
307
329
|
>
|
|
308
330
|
{columnMap.map((col) => (
|
|
309
331
|
<div
|
|
310
332
|
key={`${key}:${col}`}
|
|
311
333
|
data-index={index * columnCount + col}
|
|
312
334
|
className={classNames("grid rounded-capped")}
|
|
335
|
+
style={{
|
|
336
|
+
minWidth: width,
|
|
337
|
+
minHeight: height,
|
|
338
|
+
}}
|
|
313
339
|
>
|
|
314
|
-
{itemList[index * columnCount + col]
|
|
315
|
-
children(
|
|
316
|
-
|
|
317
|
-
|
|
340
|
+
{itemList[index * columnCount + col]?.item
|
|
341
|
+
? children(
|
|
342
|
+
itemList[index * columnCount + col] as AnyObject,
|
|
343
|
+
)
|
|
344
|
+
: itemList[index * columnCount + col]?.item === null && (
|
|
345
|
+
<Skeleton className="p-2" />
|
|
346
|
+
)}
|
|
318
347
|
</div>
|
|
319
348
|
))}
|
|
320
349
|
</div>
|
|
321
350
|
))}
|
|
322
|
-
{
|
|
323
|
-
<div
|
|
351
|
+
{loading && (
|
|
352
|
+
<div>
|
|
353
|
+
<Skeleton />
|
|
354
|
+
</div>
|
|
324
355
|
)}
|
|
325
356
|
</div>
|
|
326
357
|
</div>
|
|
@@ -349,4 +380,37 @@ const _VirtualGallery = <T extends KeyValue>({
|
|
|
349
380
|
_VirtualGallery.displayName = "VirtualGallery";
|
|
350
381
|
|
|
351
382
|
const GenericMemo: <T>(c: T) => T = memo;
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* VirtualGallery component provides a virtualized gallery view for displaying a large list of items efficiently.
|
|
386
|
+
* It supports features like fixed columns, loading states, and customizable item rendering.
|
|
387
|
+
* The component uses virtualization to render only the visible items, improving performance for large datasets.
|
|
388
|
+
* It also provides a scroller for easy navigation through the items.
|
|
389
|
+
*
|
|
390
|
+
* @param {VirtualGalleryProps<T>} props - The properties for the VirtualGallery component.
|
|
391
|
+
* @returns {JSX.Element} The rendered VirtualGallery component.
|
|
392
|
+
*
|
|
393
|
+
* @example
|
|
394
|
+
* ```jsx
|
|
395
|
+
* <VirtualGallery
|
|
396
|
+
* items={items}
|
|
397
|
+
* columns={3}
|
|
398
|
+
* defaultHeight={100}
|
|
399
|
+
* defaultWidth={100}
|
|
400
|
+
* fullWidth={true}
|
|
401
|
+
* onLoadMore={(lastIndex) => console.log("Load more items from index:", lastIndex)}
|
|
402
|
+
* onScroll={(top) => console.log("Scrolled to top:", top)}
|
|
403
|
+
* >
|
|
404
|
+
* {({ item, index, isLast }) => (
|
|
405
|
+
* <div className="gallery-item" key={item.id}>
|
|
406
|
+
* <img src={item.image} alt={item.label} />
|
|
407
|
+
* <p>{item.label}</p>
|
|
408
|
+
* </div>
|
|
409
|
+
* )}
|
|
410
|
+
* </VirtualGallery>
|
|
411
|
+
* ```
|
|
412
|
+
*
|
|
413
|
+
* @see {@link https://adarshpastakia.github.io/react-fabric/?path=/docs/data-virtualgallery--docs}
|
|
414
|
+
* @see {@link https://tanstack.com/docs/react-virtual/latest/overview}
|
|
415
|
+
*/
|
|
352
416
|
export const VirtualGallery = GenericMemo(_VirtualGallery);
|
package/src/virtual/List.tsx
CHANGED
|
@@ -57,7 +57,7 @@ export interface VirtualListRef {
|
|
|
57
57
|
|
|
58
58
|
export interface VirtualListProps<T> extends TestProps {
|
|
59
59
|
children: (props: {
|
|
60
|
-
|
|
60
|
+
item: T;
|
|
61
61
|
index: number;
|
|
62
62
|
isLast: boolean;
|
|
63
63
|
}) => ReactElement;
|
|
@@ -74,6 +74,8 @@ export interface VirtualListProps<T> extends TestProps {
|
|
|
74
74
|
* full width list
|
|
75
75
|
*/
|
|
76
76
|
fullWidth?: boolean;
|
|
77
|
+
maxWidth?: string | number;
|
|
78
|
+
minWidth?: string | number;
|
|
77
79
|
/**
|
|
78
80
|
* padding
|
|
79
81
|
*/
|
|
@@ -86,6 +88,7 @@ export interface VirtualListProps<T> extends TestProps {
|
|
|
86
88
|
* data list
|
|
87
89
|
*/
|
|
88
90
|
items: T[];
|
|
91
|
+
total?: number;
|
|
89
92
|
initialScroll?: number;
|
|
90
93
|
/**
|
|
91
94
|
* loading state
|
|
@@ -98,7 +101,7 @@ export interface VirtualListProps<T> extends TestProps {
|
|
|
98
101
|
/**
|
|
99
102
|
* load more callback
|
|
100
103
|
*/
|
|
101
|
-
onLoadMore?: (
|
|
104
|
+
onLoadMore?: (lastIndex?: number) => void;
|
|
102
105
|
/**
|
|
103
106
|
* scroll handler
|
|
104
107
|
*/
|
|
@@ -107,10 +110,13 @@ export interface VirtualListProps<T> extends TestProps {
|
|
|
107
110
|
|
|
108
111
|
const createItemList = memoizeOne((items) => items);
|
|
109
112
|
|
|
110
|
-
const _VirtualList = <T extends
|
|
113
|
+
const _VirtualList = <T extends AnyObject>({
|
|
111
114
|
items,
|
|
115
|
+
total = 0,
|
|
112
116
|
children,
|
|
113
117
|
fullWidth,
|
|
118
|
+
maxWidth,
|
|
119
|
+
minWidth,
|
|
114
120
|
padding = "md",
|
|
115
121
|
orientation = "vertical",
|
|
116
122
|
defaultHeight: height = 48,
|
|
@@ -124,18 +130,18 @@ const _VirtualList = <T extends KeyValue>({
|
|
|
124
130
|
...aria
|
|
125
131
|
}: VirtualListProps<T>) => {
|
|
126
132
|
const count = useDeferredValue(items.length);
|
|
127
|
-
const extraSize = useMemo(() => (onLoadMore ?
|
|
133
|
+
const extraSize = useMemo(() => (onLoadMore ? 8 : 0), [onLoadMore]);
|
|
128
134
|
const itemList: Array<{
|
|
129
135
|
index: number;
|
|
130
|
-
|
|
136
|
+
item: T;
|
|
131
137
|
isLast: boolean;
|
|
132
138
|
isSticky: boolean;
|
|
133
139
|
}> = createItemList(
|
|
134
|
-
items.map((item: AnyObject,
|
|
140
|
+
items.map((item: AnyObject, index) => {
|
|
135
141
|
return {
|
|
136
|
-
index
|
|
137
|
-
|
|
138
|
-
isLast:
|
|
142
|
+
index,
|
|
143
|
+
item,
|
|
144
|
+
isLast: index + 1 === items.length,
|
|
139
145
|
};
|
|
140
146
|
}),
|
|
141
147
|
);
|
|
@@ -143,6 +149,7 @@ const _VirtualList = <T extends KeyValue>({
|
|
|
143
149
|
const scrollerRef = useRef<HTMLDivElement>(null);
|
|
144
150
|
const virtualizer = useVirtualizer({
|
|
145
151
|
count,
|
|
152
|
+
overscan: 0,
|
|
146
153
|
horizontal: orientation === "horizontal",
|
|
147
154
|
getScrollElement: () => scrollerRef.current,
|
|
148
155
|
estimateSize: () => (orientation === "horizontal" ? width : height),
|
|
@@ -152,7 +159,7 @@ const _VirtualList = <T extends KeyValue>({
|
|
|
152
159
|
|
|
153
160
|
const currentIndex = useRef(0);
|
|
154
161
|
useEffect(() => {
|
|
155
|
-
currentIndex.current = virtualItems[
|
|
162
|
+
currentIndex.current = virtualItems[0]?.index ?? 0;
|
|
156
163
|
if (
|
|
157
164
|
scrollerRef.current &&
|
|
158
165
|
virtualItems[0] &&
|
|
@@ -201,42 +208,48 @@ const _VirtualList = <T extends KeyValue>({
|
|
|
201
208
|
}, [itemList, calculateActiveScroll]);
|
|
202
209
|
|
|
203
210
|
useEffect(() => {
|
|
204
|
-
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
useEffect(() => {
|
|
213
|
-
const firstItem = virtualItems[0]?.index;
|
|
214
|
-
if (onLoadMore && firstItem !== undefined && items[firstItem] === null) {
|
|
211
|
+
if (onLoadMore) {
|
|
212
|
+
const firstItem = virtualItems[0]?.index;
|
|
213
|
+
const lastItem = virtualItems?.slice(-1).pop()?.index;
|
|
214
|
+
const canLoadPrevPage =
|
|
215
|
+
firstItem !== undefined && items[firstItem] === null;
|
|
216
|
+
const canLoadNextPage =
|
|
217
|
+
lastItem !== undefined && lastItem === count - 1 && count < total;
|
|
215
218
|
const db = debounce(onLoadMore);
|
|
216
|
-
|
|
219
|
+
if (canLoadPrevPage) {
|
|
220
|
+
db(firstItem);
|
|
221
|
+
} else if (!loading && canLoadNextPage) {
|
|
222
|
+
db(lastItem + 1);
|
|
223
|
+
}
|
|
217
224
|
return () => db.cancel();
|
|
218
225
|
}
|
|
219
|
-
}, [onLoadMore,
|
|
226
|
+
}, [onLoadMore, virtualItems]);
|
|
220
227
|
|
|
228
|
+
const isRenderedRef = useRef(false);
|
|
221
229
|
useLayoutEffect(() => {
|
|
222
|
-
|
|
230
|
+
if (!isRenderedRef.current && itemList.length > 0) {
|
|
231
|
+
isRenderedRef.current = true;
|
|
223
232
|
initialScroll &&
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
233
|
+
setTimeout(() => {
|
|
234
|
+
virtualizer.scrollToIndex(Math.floor(initialScroll), {
|
|
235
|
+
align: "start",
|
|
236
|
+
});
|
|
237
|
+
}, 200);
|
|
238
|
+
}
|
|
239
|
+
}, [itemList, initialScroll]);
|
|
227
240
|
|
|
228
241
|
useImperativeHandle(
|
|
229
242
|
listRef,
|
|
230
243
|
() => ({
|
|
231
244
|
scrollToItem(index) {
|
|
232
|
-
virtualizer.scrollToIndex(index, { align: "
|
|
245
|
+
virtualizer.scrollToIndex(index, { align: "auto" });
|
|
233
246
|
},
|
|
234
247
|
hilight(index) {
|
|
235
248
|
scrollerRef.current
|
|
236
249
|
?.querySelector(".hilight")
|
|
237
250
|
?.classList.remove("hilight", "ring-1");
|
|
238
251
|
virtualizer.scrollToIndex(Math.floor(index), {
|
|
239
|
-
align: "
|
|
252
|
+
align: "auto",
|
|
240
253
|
});
|
|
241
254
|
setTimeout(() => {
|
|
242
255
|
scrollerRef.current
|
|
@@ -257,14 +270,12 @@ const _VirtualList = <T extends KeyValue>({
|
|
|
257
270
|
<div
|
|
258
271
|
ref={scrollerRef}
|
|
259
272
|
onScroll={handleScroll}
|
|
260
|
-
className={classNames(
|
|
261
|
-
"area-content bg-base overflow-auto contain-strict",
|
|
262
|
-
)}
|
|
273
|
+
className={classNames("area-content bg-base overflow-auto")}
|
|
263
274
|
{...aria}
|
|
264
275
|
>
|
|
265
276
|
<div
|
|
266
277
|
className={classNames(
|
|
267
|
-
"
|
|
278
|
+
"flex flex-nowrap gap-2 justify-center min-h-full min-w-full",
|
|
268
279
|
orientation === "horizontal" && "flex-col",
|
|
269
280
|
)}
|
|
270
281
|
>
|
|
@@ -278,12 +289,16 @@ const _VirtualList = <T extends KeyValue>({
|
|
|
278
289
|
padding === "md" && "p-2",
|
|
279
290
|
padding === "lg" && "p-4",
|
|
280
291
|
)}
|
|
292
|
+
style={{
|
|
293
|
+
minWidth,
|
|
294
|
+
maxWidth,
|
|
295
|
+
}}
|
|
281
296
|
>
|
|
282
297
|
<div
|
|
283
298
|
style={
|
|
284
299
|
orientation === "vertical"
|
|
285
|
-
? { height: virtualizer.getTotalSize() }
|
|
286
|
-
: { width: virtualizer.getTotalSize() }
|
|
300
|
+
? { height: virtualizer.getTotalSize() + extraSize }
|
|
301
|
+
: { width: virtualizer.getTotalSize() + extraSize }
|
|
287
302
|
}
|
|
288
303
|
>
|
|
289
304
|
<div
|
|
@@ -302,14 +317,22 @@ const _VirtualList = <T extends KeyValue>({
|
|
|
302
317
|
"rounded-capped",
|
|
303
318
|
orientation === "horizontal" ? "inline-grid" : "grid",
|
|
304
319
|
)}
|
|
320
|
+
style={{
|
|
321
|
+
minWidth: width,
|
|
322
|
+
minHeight: height,
|
|
323
|
+
}}
|
|
305
324
|
>
|
|
306
|
-
{
|
|
325
|
+
{itemList[index]?.item
|
|
326
|
+
? children(itemList[index] as AnyObject)
|
|
327
|
+
: itemList[index]?.item === null && (
|
|
328
|
+
<Skeleton className="p-2" />
|
|
329
|
+
)}
|
|
307
330
|
</div>
|
|
308
331
|
))}
|
|
309
332
|
</div>
|
|
310
|
-
{
|
|
311
|
-
<div className={orientation === "horizontal" ? "min-w-16" : "
|
|
312
|
-
|
|
333
|
+
{loading && (
|
|
334
|
+
<div className={orientation === "horizontal" ? "min-w-16" : ""}>
|
|
335
|
+
<Skeleton />
|
|
313
336
|
</div>
|
|
314
337
|
)}
|
|
315
338
|
</div>
|
|
@@ -351,4 +374,41 @@ const _VirtualList = <T extends KeyValue>({
|
|
|
351
374
|
_VirtualList.displayName = "VirtualList";
|
|
352
375
|
|
|
353
376
|
const GenericMemo: <T>(c: T) => T = memo;
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* VirtualList component for rendering large lists efficiently using virtualization.
|
|
380
|
+
* It supports horizontal and vertical orientations, custom item heights and widths,
|
|
381
|
+
* and provides a scroller for navigating through the list.
|
|
382
|
+
* It also supports loading more items on scroll and provides a way to hilight items.
|
|
383
|
+
* It can be used with a ref to control scrolling and hilighting of items.
|
|
384
|
+
*
|
|
385
|
+
* @param {VirtualListProps<T>} props - The properties for the VirtualList component.
|
|
386
|
+
* @returns {JSX.Element} The rendered VirtualList component.
|
|
387
|
+
* @template T - The type of items in the list, extending AnyObject.
|
|
388
|
+
*
|
|
389
|
+
* @example
|
|
390
|
+
* ```jsx
|
|
391
|
+
* <VirtualList
|
|
392
|
+
* items={items}
|
|
393
|
+
* children={({ item, index, isLast }) => (
|
|
394
|
+
* <div key={index} className="item">
|
|
395
|
+
* {item.label}
|
|
396
|
+
* </div>
|
|
397
|
+
* )}
|
|
398
|
+
* fullWidth={true}
|
|
399
|
+
* orientation="vertical"
|
|
400
|
+
* defaultHeight={50}
|
|
401
|
+
* onLoadMore={(lastIndex) => console.log("Load more items from index:", lastIndex)}
|
|
402
|
+
* onScroll={(top) => console.log("Scrolled to top:", top)}
|
|
403
|
+
* listRef={virtualListRef}
|
|
404
|
+
* >
|
|
405
|
+
* {({ item, index, isLast }) => (
|
|
406
|
+
* <div className="gallery-item" key={item.id}>
|
|
407
|
+
* <img src={item.image} alt={item.label} />
|
|
408
|
+
* <p>{item.label}</p>
|
|
409
|
+
* </div>
|
|
410
|
+
* )}
|
|
411
|
+
* </VirtualList>
|
|
412
|
+
* ```
|
|
413
|
+
*/
|
|
354
414
|
export const VirtualList = GenericMemo(_VirtualList);
|
package/src/virtual/Scroller.tsx
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
import { Button, ButtonGroup
|
|
24
|
+
import { Button, ButtonGroup } from "@react-fabric/core";
|
|
25
25
|
|
|
26
26
|
export const Scroller = ({
|
|
27
27
|
vertical = true,
|
|
@@ -41,38 +41,54 @@ export const Scroller = ({
|
|
|
41
41
|
return (
|
|
42
42
|
<ButtonGroup vertical={vertical} variant="link" className="outline">
|
|
43
43
|
<Button
|
|
44
|
-
rtlFlip
|
|
45
44
|
size="sm"
|
|
46
45
|
variant="link"
|
|
47
46
|
aria-label="scroll to top"
|
|
48
|
-
icon={
|
|
47
|
+
icon={{
|
|
48
|
+
icon: vertical
|
|
49
|
+
? "icon-[mdi--chevron-double-up]"
|
|
50
|
+
: "icon-[mdi--chevron-double-left]",
|
|
51
|
+
rtlFlip: true,
|
|
52
|
+
}}
|
|
49
53
|
disabled={(scrollActive & 2) === 0}
|
|
50
54
|
onClick={onStart}
|
|
51
55
|
/>
|
|
52
56
|
<Button
|
|
53
|
-
rtlFlip
|
|
54
57
|
size="sm"
|
|
55
58
|
variant="link"
|
|
56
59
|
aria-label="scroll up"
|
|
57
|
-
icon={
|
|
60
|
+
icon={{
|
|
61
|
+
icon: vertical
|
|
62
|
+
? "icon-[mdi--chevron-up]"
|
|
63
|
+
: "icon-[mdi--chevron-left]",
|
|
64
|
+
rtlFlip: true,
|
|
65
|
+
}}
|
|
58
66
|
disabled={(scrollActive & 2) === 0}
|
|
59
67
|
onClick={onPrevious}
|
|
60
68
|
/>
|
|
61
69
|
<Button
|
|
62
|
-
rtlFlip
|
|
63
70
|
size="sm"
|
|
64
71
|
variant="link"
|
|
65
72
|
aria-label="scroll down"
|
|
66
|
-
icon={
|
|
73
|
+
icon={{
|
|
74
|
+
icon: vertical
|
|
75
|
+
? "icon-[mdi--chevron-down]"
|
|
76
|
+
: "icon-[mdi--chevron-right]",
|
|
77
|
+
rtlFlip: true,
|
|
78
|
+
}}
|
|
67
79
|
disabled={scrollActive % 2 === 0}
|
|
68
80
|
onClick={onNext}
|
|
69
81
|
/>
|
|
70
82
|
<Button
|
|
71
|
-
rtlFlip
|
|
72
83
|
size="sm"
|
|
73
84
|
variant="link"
|
|
74
85
|
aria-label="scroll to bottom"
|
|
75
|
-
icon={
|
|
86
|
+
icon={{
|
|
87
|
+
icon: vertical
|
|
88
|
+
? "icon-[mdi--chevron-double-down]"
|
|
89
|
+
: "icon-[mdi--chevron-double-right]",
|
|
90
|
+
rtlFlip: true,
|
|
91
|
+
}}
|
|
76
92
|
disabled={scrollActive % 2 === 0}
|
|
77
93
|
onClick={onEnd}
|
|
78
94
|
/>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
@layer comp{.histogram{.histoLabel{--tw-gradient-from:var(--color) var(--tw-gradient-from-position);--tw-gradient-to:color-mix(in lab,var(--color) 20%,var(--fabric-bg)) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to);background-color:color(from var(--color) srgb r g b/5%);&:hover{background-color:color(from var(--color) srgb r g b/15%)}&[data-disabled=true]{--color:theme(colors.tint.300);color:theme(colors.tint.600)}}.histoBar{box-shadow:-2px 0 0 var(--color) inset;:global([dir=rtl]) &{box-shadow:2px 0 0 var(--color) inset}}.histoBadge{background-color:color(from var(--color) srgb r g b/15%)}}}
|
package/dist/cjs/tree/types.js
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/*
|
|
4
|
-
* React Fabric
|
|
5
|
-
* @version: 1.0.0
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* The MIT License (MIT)
|
|
9
|
-
* Copyright (c) 2024 Adarsh Pastakia
|
|
10
|
-
*
|
|
11
|
-
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
|
12
|
-
* and associated documentation files (the "Software"), to deal in the Software without restriction,
|
|
13
|
-
* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
14
|
-
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
|
|
15
|
-
* subject to the following conditions:
|
|
16
|
-
*
|
|
17
|
-
* The above copyright notice and this permission notice shall be included in all copies or substantial
|
|
18
|
-
* portions of the Software.
|
|
19
|
-
*
|
|
20
|
-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
|
21
|
-
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
22
|
-
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
23
|
-
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
var iconExpandAll = "M12.984 9v6h3l-3.984 3.984-3.984-3.984h3v-6h-3l3.984-3.984 3.984 3.984h-3zM3.984 2.016h16.031v1.969h-16.031v-1.969zM3.984 20.016h16.031v1.969h-16.031v-1.969z";
|
|
27
|
-
var iconCollapseAll = "M3.984 11.016h16.031v1.969h-16.031v-1.969zM15.984 5.016l-3.984 3.984-3.984-3.984h3v-4.031h1.969v4.031h3zM8.016 18.984l3.984-3.984 3.984 3.984h-3v4.031h-1.969v-4.031h-3z";
|
|
28
|
-
|
|
29
|
-
exports.iconCollapseAll = iconCollapseAll;
|
|
30
|
-
exports.iconExpandAll = iconExpandAll;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
@layer comp{.histogram{.histoLabel{--tw-gradient-from:var(--color) var(--tw-gradient-from-position);--tw-gradient-to:color-mix(in lab,var(--color) 20%,var(--fabric-bg)) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to);background-color:color(from var(--color) srgb r g b/5%);&:hover{background-color:color(from var(--color) srgb r g b/15%)}&[data-disabled=true]{--color:theme(colors.tint.300);color:theme(colors.tint.600)}}.histoBar{box-shadow:-2px 0 0 var(--color) inset;:global([dir=rtl]) &{box-shadow:2px 0 0 var(--color) inset}}.histoBadge{background-color:color(from var(--color) srgb r g b/15%)}}}
|