@react-fabric/data 1.0.0-beta.8 → 1.0.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/{src/histogram/Histogram.module.css → css/_histogram.css} +16 -13
- package/css/styles.css +24 -0
- package/dist/cjs/checklist/CheckList.js +59 -33
- package/dist/cjs/histogram/Histogram.js +41 -26
- package/dist/cjs/hooks/useFilteredList.js +100 -17
- package/dist/cjs/hooks/usePagination.js +29 -8
- package/dist/cjs/hooks/useSelectableList.js +30 -5
- package/dist/cjs/i18n/{ar.json.js → ar/data.json.js} +8 -1
- package/dist/cjs/i18n/{en.json.js → en/data.json.js} +8 -1
- package/dist/cjs/i18n/index.js +4 -4
- package/dist/cjs/index.js +30 -0
- package/dist/cjs/json/JsonViewer.js +80 -43
- package/dist/cjs/pagination/Pagination.js +24 -15
- package/dist/cjs/table/AddColumn.js +5 -5
- package/dist/cjs/table/BodyCell.js +20 -13
- package/dist/cjs/table/BodyRow.js +41 -28
- package/dist/cjs/table/CheckboxCell.js +10 -9
- package/dist/cjs/table/ColumnFilters.js +100 -0
- package/dist/cjs/table/EmptyCell.js +4 -4
- package/dist/cjs/table/HeaderCell.js +79 -53
- package/dist/cjs/table/HtmlTable.js +84 -0
- package/dist/cjs/table/{Context.js → Provider.js} +8 -10
- package/dist/cjs/table/Table.js +99 -33
- package/dist/cjs/{tree/types.js → table/context.js} +9 -5
- package/dist/cjs/table/types.js +29 -0
- package/dist/cjs/table/useResizer.js +10 -10
- package/dist/cjs/table/useTableColumns.js +4 -4
- package/dist/cjs/table/useVirtualTable.js +129 -22
- package/dist/cjs/tree/TreeNode.js +66 -46
- package/dist/cjs/tree/TreePanel.js +60 -24
- package/dist/cjs/tree/useTree.js +212 -47
- package/dist/cjs/tree/utils.js +63 -34
- package/dist/cjs/utils/getColor.js +58 -0
- package/dist/cjs/virtual/Gallery.js +119 -61
- package/dist/cjs/virtual/List.js +116 -55
- package/dist/cjs/virtual/Scroller.js +16 -8
- package/dist/esm/checklist/CheckList.js +61 -35
- package/dist/esm/histogram/Histogram.js +43 -28
- package/dist/esm/hooks/useFilteredList.js +101 -18
- package/dist/esm/hooks/usePagination.js +29 -8
- package/dist/esm/hooks/useSelectableList.js +30 -5
- package/dist/esm/i18n/{ar.json.js → ar/data.json.js} +8 -2
- package/dist/esm/i18n/{en.json.js → en/data.json.js} +8 -2
- package/dist/esm/i18n/index.js +2 -2
- package/dist/esm/index.js +31 -0
- package/dist/esm/json/JsonViewer.js +82 -45
- package/dist/esm/pagination/Pagination.js +25 -16
- package/dist/esm/table/AddColumn.js +6 -6
- package/dist/esm/table/BodyCell.js +21 -14
- package/dist/esm/table/BodyRow.js +41 -28
- package/dist/esm/table/CheckboxCell.js +11 -10
- package/dist/esm/table/ColumnFilters.js +100 -0
- package/dist/esm/table/EmptyCell.js +4 -4
- package/dist/esm/table/HeaderCell.js +80 -54
- package/dist/esm/table/HtmlTable.js +84 -0
- package/dist/esm/table/{Context.js → Provider.js} +11 -12
- package/dist/esm/table/Table.js +100 -34
- package/dist/esm/{tree/types.js → table/context.js} +8 -4
- package/dist/esm/table/types.js +29 -0
- package/dist/esm/table/useResizer.js +10 -10
- package/dist/esm/table/useTableColumns.js +4 -4
- package/dist/esm/table/useVirtualTable.js +131 -24
- package/dist/esm/tree/TreeNode.js +69 -49
- package/dist/esm/tree/TreePanel.js +62 -26
- package/dist/esm/tree/useTree.js +215 -50
- package/dist/esm/tree/utils.js +64 -35
- package/dist/esm/utils/getColor.js +58 -0
- package/dist/esm/virtual/Gallery.js +121 -63
- package/dist/esm/virtual/List.js +118 -57
- package/dist/esm/virtual/Scroller.js +17 -9
- package/dist/types/checklist/CheckList.d.ts +25 -6
- package/dist/types/histogram/Histogram.d.ts +28 -6
- package/dist/types/hooks/useFilteredList.d.ts +62 -5
- package/dist/types/hooks/usePagination.d.ts +18 -2
- package/dist/types/hooks/useSelectableList.d.ts +24 -1
- package/dist/types/index.d.ts +27 -0
- package/dist/types/json/JsonViewer.d.ts +23 -3
- package/dist/types/pagination/Pagination.d.ts +8 -2
- package/dist/types/table/AddColumn.d.ts +2 -2
- package/dist/types/table/BodyCell.d.ts +2 -2
- package/dist/types/table/BodyRow.d.ts +9 -9
- package/dist/types/table/CheckboxCell.d.ts +3 -3
- package/dist/types/table/ColumnFilters.d.ts +12 -0
- package/dist/types/table/EmptyCell.d.ts +1 -1
- package/dist/types/table/HeaderCell.d.ts +1 -1
- package/dist/types/table/HtmlTable.d.ts +21 -0
- package/dist/types/table/Provider.d.ts +2 -0
- package/dist/types/table/Table.d.ts +10 -1
- package/dist/types/table/context.d.ts +7 -0
- package/dist/types/table/types.d.ts +52 -10
- package/dist/types/table/useResizer.d.ts +1 -1
- package/dist/types/table/useTableColumns.d.ts +1 -1
- package/dist/types/table/useVirtualTable.d.ts +31 -12
- package/dist/types/tree/TreeNode.d.ts +1 -1
- package/dist/types/tree/TreePanel.d.ts +36 -1
- package/dist/types/tree/types.d.ts +75 -28
- package/dist/types/tree/useTree.d.ts +8 -12
- package/dist/types/tree/utils.d.ts +15 -8
- package/dist/types/utils/getColor.d.ts +43 -0
- package/dist/types/virtual/Gallery.d.ts +37 -6
- package/dist/types/virtual/List.d.ts +38 -6
- package/dist/types/virtual/Scroller.d.ts +1 -1
- package/package.json +17 -19
- package/src/checklist/CheckList.tsx +68 -71
- package/src/histogram/Histogram.tsx +47 -48
- package/src/hooks/useFilteredList.tsx +111 -28
- package/src/hooks/usePagination.tsx +39 -39
- package/src/hooks/useSelectableList.ts +45 -33
- package/src/i18n/{ar.json → ar/data.json} +5 -0
- package/src/i18n/{en.json → en/data.json} +5 -0
- package/src/i18n/index.ts +2 -2
- package/src/index.ts +23 -17
- package/src/json/JsonViewer.tsx +89 -120
- package/src/pagination/Pagination.tsx +104 -105
- package/src/table/AddColumn.tsx +6 -15
- package/src/table/BodyCell.tsx +27 -36
- package/src/table/BodyRow.tsx +36 -59
- package/src/table/CheckboxCell.tsx +12 -15
- package/src/table/ColumnFilters.tsx +93 -0
- package/src/table/EmptyCell.tsx +5 -18
- package/src/table/HeaderCell.tsx +72 -53
- package/src/table/HtmlTable.tsx +106 -0
- package/src/table/{Context.tsx → Provider.tsx} +11 -28
- package/src/table/Table.tsx +98 -83
- package/src/table/context.ts +33 -0
- package/src/table/types.ts +53 -27
- package/src/table/useResizer.ts +5 -9
- package/src/table/useTableColumns.ts +4 -6
- package/src/table/useVirtualTable.ts +136 -37
- package/src/tree/TreeNode.tsx +91 -86
- package/src/tree/TreePanel.tsx +87 -51
- package/src/tree/types.ts +85 -46
- package/src/tree/useTree.ts +210 -65
- package/src/tree/utils.ts +81 -62
- package/src/utils/getColor.ts +55 -0
- package/src/virtual/Gallery.tsx +129 -120
- package/src/virtual/List.tsx +124 -91
- package/src/virtual/Scroller.tsx +25 -9
- package/dist/cjs/histogram/Histogram.module.css +0 -1
- package/dist/esm/histogram/Histogram.module.css +0 -1
- package/dist/types/table/Context.d.ts +0 -8
package/src/virtual/Gallery.tsx
CHANGED
|
@@ -23,9 +23,8 @@
|
|
|
23
23
|
|
|
24
24
|
import { Skeleton, useDebounce, useEffectDebugger } from "@react-fabric/core";
|
|
25
25
|
import { type TestProps } from "@react-fabric/core/dist/types/types";
|
|
26
|
-
import { debounce } from "@react-fabric/utilities";
|
|
26
|
+
import { cn, debounce } from "@react-fabric/utilities";
|
|
27
27
|
import { useVirtualizer } from "@tanstack/react-virtual";
|
|
28
|
-
import classNames from "classnames";
|
|
29
28
|
import memoize from "memoize-one";
|
|
30
29
|
import {
|
|
31
30
|
memo,
|
|
@@ -49,11 +48,7 @@ export interface VirtualGalleryRef {
|
|
|
49
48
|
}
|
|
50
49
|
|
|
51
50
|
export interface VirtualGalleryProps<T> extends TestProps {
|
|
52
|
-
children: (props: {
|
|
53
|
-
data: T;
|
|
54
|
-
index: number;
|
|
55
|
-
isLast: boolean;
|
|
56
|
-
}) => ReactElement;
|
|
51
|
+
children: (props: { item: T; index: number; isLast: boolean }) => ReactElement;
|
|
57
52
|
galleryRef?: Ref<VirtualGalleryRef | undefined>;
|
|
58
53
|
/**
|
|
59
54
|
* fixed columns
|
|
@@ -67,6 +62,12 @@ export interface VirtualGalleryProps<T> extends TestProps {
|
|
|
67
62
|
* default item width
|
|
68
63
|
*/
|
|
69
64
|
defaultWidth?: number;
|
|
65
|
+
/**
|
|
66
|
+
* full width list
|
|
67
|
+
*/
|
|
68
|
+
fullWidth?: boolean;
|
|
69
|
+
maxWidth?: string | number;
|
|
70
|
+
minWidth?: string | number;
|
|
70
71
|
/**
|
|
71
72
|
* padding
|
|
72
73
|
*/
|
|
@@ -75,6 +76,7 @@ export interface VirtualGalleryProps<T> extends TestProps {
|
|
|
75
76
|
* data list
|
|
76
77
|
*/
|
|
77
78
|
items: T[];
|
|
79
|
+
total?: number;
|
|
78
80
|
/**
|
|
79
81
|
* loading state
|
|
80
82
|
*/
|
|
@@ -87,19 +89,23 @@ export interface VirtualGalleryProps<T> extends TestProps {
|
|
|
87
89
|
/**
|
|
88
90
|
* load more callback
|
|
89
91
|
*/
|
|
90
|
-
onLoadMore?: () => void;
|
|
92
|
+
onLoadMore?: (lastIndex?: number) => void;
|
|
91
93
|
/**
|
|
92
94
|
* scroll handler
|
|
93
95
|
*/
|
|
94
96
|
onScroll?: (top: number) => void;
|
|
95
97
|
}
|
|
96
98
|
|
|
97
|
-
const createItemList = memoize((items) => items);
|
|
99
|
+
const createItemList = memoize((items) => items as never);
|
|
98
100
|
|
|
99
|
-
const
|
|
101
|
+
const VirtualGalleryComponent = <T extends AnyObject>({
|
|
100
102
|
items,
|
|
101
103
|
children,
|
|
102
104
|
columns,
|
|
105
|
+
fullWidth,
|
|
106
|
+
maxWidth,
|
|
107
|
+
minWidth,
|
|
108
|
+
total = 0,
|
|
103
109
|
padding = "md",
|
|
104
110
|
defaultHeight: height = 48,
|
|
105
111
|
defaultWidth: width = 48,
|
|
@@ -112,36 +118,33 @@ const _VirtualGallery = <T extends KeyValue>({
|
|
|
112
118
|
...aria
|
|
113
119
|
}: VirtualGalleryProps<T>) => {
|
|
114
120
|
const count = useDeferredValue(items.length);
|
|
115
|
-
const extraSize = useMemo(() => (onLoadMore ?
|
|
121
|
+
const extraSize = useMemo(() => (onLoadMore ? 8 : 0), [onLoadMore]);
|
|
116
122
|
const itemList: Array<{
|
|
117
123
|
index: number;
|
|
118
|
-
|
|
124
|
+
item: T;
|
|
119
125
|
isLast: boolean;
|
|
120
126
|
isSticky: boolean;
|
|
121
127
|
}> = createItemList(
|
|
122
|
-
items.map((item
|
|
128
|
+
items.map((item, index) => {
|
|
123
129
|
return {
|
|
124
|
-
index
|
|
125
|
-
|
|
126
|
-
isLast:
|
|
130
|
+
index,
|
|
131
|
+
item,
|
|
132
|
+
isLast: index + 1 === items.length,
|
|
127
133
|
};
|
|
128
134
|
}),
|
|
129
135
|
);
|
|
130
136
|
|
|
131
137
|
const scrollerRef = useRef<HTMLDivElement>(null);
|
|
132
138
|
const [columnCount, setColumnCount] = useState(1);
|
|
133
|
-
const columnMap = useMemo(
|
|
134
|
-
() => new Array(columnCount).fill(0).map((_, idx) => idx),
|
|
135
|
-
[columnCount],
|
|
136
|
-
);
|
|
139
|
+
const columnMap = useMemo(() => new Array(columnCount).fill(0).map((_, idx) => idx), [columnCount]);
|
|
137
140
|
useEffectDebugger(
|
|
138
141
|
() => {
|
|
139
142
|
if (scrollerRef.current) {
|
|
140
143
|
const el = scrollerRef.current;
|
|
141
144
|
const handler = () => {
|
|
145
|
+
const pad = parseInt(getComputedStyle(el.firstElementChild?.firstElementChild as HTMLElement).padding ?? 8);
|
|
142
146
|
setColumnCount(
|
|
143
|
-
columns ??
|
|
144
|
-
Math.max(1, Math.floor(((el?.offsetWidth ?? 800) - 32) / width)),
|
|
147
|
+
columns ?? Math.max(1, Math.floor(((el?.offsetWidth ?? 800) - 32 - (isNaN(pad) ? 8 : pad) * 2) / width)),
|
|
145
148
|
);
|
|
146
149
|
};
|
|
147
150
|
const ob = new ResizeObserver(handler);
|
|
@@ -161,183 +164,162 @@ const _VirtualGallery = <T extends KeyValue>({
|
|
|
161
164
|
count: Math.ceil(count / columnCount),
|
|
162
165
|
getScrollElement: () => scrollerRef.current,
|
|
163
166
|
estimateSize: () => height,
|
|
167
|
+
overscan: 0,
|
|
164
168
|
});
|
|
165
169
|
|
|
166
170
|
const virtualItems = virtualizer.getVirtualItems();
|
|
167
171
|
|
|
168
|
-
const
|
|
172
|
+
const currentIndexRef = useRef(0);
|
|
169
173
|
useEffect(() => {
|
|
170
|
-
|
|
171
|
-
if (
|
|
172
|
-
|
|
173
|
-
virtualItems[0] &&
|
|
174
|
-
scrollerRef.current.scrollTop < virtualItems[0].size
|
|
175
|
-
)
|
|
176
|
-
currentIndex.current = 0;
|
|
174
|
+
currentIndexRef.current = virtualItems[0]?.index ?? 0;
|
|
175
|
+
if (scrollerRef.current && virtualItems[0] && scrollerRef.current.scrollTop < virtualItems[0].size)
|
|
176
|
+
currentIndexRef.current = 0;
|
|
177
177
|
}, [virtualItems]);
|
|
178
178
|
|
|
179
179
|
const calculateActiveScroll = useCallback(() => {
|
|
180
|
-
const {
|
|
181
|
-
scrollTop = 0,
|
|
182
|
-
scrollHeight = 0,
|
|
183
|
-
offsetHeight = 0,
|
|
184
|
-
} = (scrollerRef.current ?? {}) as HTMLElement;
|
|
180
|
+
const { scrollTop = 0, scrollHeight = 0, offsetHeight = 0 } = (scrollerRef.current ?? {}) as HTMLElement;
|
|
185
181
|
if (scrollHeight === offsetHeight) return 0;
|
|
186
182
|
|
|
187
183
|
if (scrollTop === 0) return 1;
|
|
188
184
|
if (scrollTop >= scrollHeight - offsetHeight - extraSize * 2) return 2;
|
|
189
185
|
|
|
190
186
|
return 3;
|
|
187
|
+
// eslint-disable-next-line @eslint-react/exhaustive-deps
|
|
191
188
|
}, []);
|
|
192
189
|
|
|
193
190
|
const [scrollActive, setScrollActive] = useState(0);
|
|
194
|
-
const fireScrollEvent = useDebounce(
|
|
195
|
-
(idx: number) => onScroll?.(currentIndex.current * idx),
|
|
196
|
-
[onScroll],
|
|
197
|
-
);
|
|
191
|
+
const fireScrollEvent = useDebounce((idx: number) => onScroll?.(currentIndexRef.current * idx), [onScroll]);
|
|
198
192
|
const handleScroll = useCallback(() => {
|
|
199
193
|
setScrollActive(calculateActiveScroll());
|
|
200
194
|
fireScrollEvent(columnCount);
|
|
195
|
+
// eslint-disable-next-line @eslint-react/exhaustive-deps
|
|
201
196
|
}, [columnCount, onLoadMore]);
|
|
202
197
|
|
|
203
198
|
useEffect(() => {
|
|
199
|
+
// eslint-disable-next-line @eslint-react/set-state-in-effect
|
|
204
200
|
setScrollActive(calculateActiveScroll());
|
|
205
201
|
}, [count, calculateActiveScroll]);
|
|
206
202
|
|
|
207
203
|
useEffect(() => {
|
|
208
|
-
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
onLoadMore?.();
|
|
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
|
-
) {
|
|
204
|
+
if (onLoadMore) {
|
|
205
|
+
const firstItem = virtualItems[0]?.index;
|
|
206
|
+
const lastItem = virtualItems?.slice(-1).pop()?.index;
|
|
207
|
+
const firstIndex = firstItem * columnCount;
|
|
208
|
+
const lastIndex = (lastItem ?? 0) * columnCount + columnCount;
|
|
209
|
+
const canLoadPrevPage = firstItem !== undefined && items[firstIndex] === null;
|
|
210
|
+
const canLoadNextPage = lastItem !== undefined && lastIndex >= count - 1 && count < total;
|
|
228
211
|
const db = debounce(onLoadMore);
|
|
229
|
-
|
|
212
|
+
if (canLoadPrevPage) {
|
|
213
|
+
db(firstIndex);
|
|
214
|
+
} else if (!loading && canLoadNextPage) {
|
|
215
|
+
db(lastIndex + 1);
|
|
216
|
+
}
|
|
230
217
|
return () => db.cancel();
|
|
231
218
|
}
|
|
232
|
-
|
|
219
|
+
// eslint-disable-next-line @eslint-react/exhaustive-deps
|
|
220
|
+
}, [onLoadMore, virtualItems]);
|
|
233
221
|
|
|
222
|
+
const isRenderedRef = useRef(false);
|
|
234
223
|
useLayoutEffect(() => {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
224
|
+
if (!isRenderedRef.current && itemList.length > 0) {
|
|
225
|
+
isRenderedRef.current = true;
|
|
226
|
+
if (initialScroll) {
|
|
227
|
+
const tmr = setTimeout(() => {
|
|
228
|
+
virtualizer.scrollToIndex(Math.floor(initialScroll / columnCount), {
|
|
229
|
+
align: "start",
|
|
230
|
+
});
|
|
231
|
+
}, 200);
|
|
232
|
+
return () => {
|
|
233
|
+
clearTimeout(tmr);
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
// eslint-disable-next-line @eslint-react/exhaustive-deps
|
|
238
|
+
}, [columnCount, initialScroll, itemList]);
|
|
242
239
|
|
|
243
240
|
useImperativeHandle(
|
|
244
241
|
galleryRef,
|
|
245
242
|
() => ({
|
|
246
243
|
scrollToItem(index) {
|
|
247
244
|
virtualizer.scrollToIndex(Math.floor(index / columnCount), {
|
|
248
|
-
align: "
|
|
245
|
+
align: "auto",
|
|
249
246
|
});
|
|
250
247
|
},
|
|
251
248
|
hilight(index) {
|
|
252
|
-
scrollerRef.current
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
align: "start",
|
|
249
|
+
const el = scrollerRef.current?.querySelector("[data-selected='true']") as HTMLElement;
|
|
250
|
+
if (el) el.dataset.selected = "false";
|
|
251
|
+
virtualizer.scrollToIndex(Math.floor(index), {
|
|
252
|
+
align: "auto",
|
|
257
253
|
});
|
|
258
254
|
setTimeout(() => {
|
|
259
|
-
scrollerRef.current
|
|
260
|
-
|
|
261
|
-
?.classList.add("hilight", "ring-1");
|
|
255
|
+
const el = scrollerRef.current?.querySelector(`[data-index="${index}"]`) as HTMLElement;
|
|
256
|
+
if (el) el.dataset.selected = "true";
|
|
262
257
|
}, 100);
|
|
263
258
|
},
|
|
264
259
|
unhilight() {
|
|
265
|
-
scrollerRef.current
|
|
266
|
-
|
|
267
|
-
?.classList.remove("hilight", "ring-1");
|
|
260
|
+
const el = scrollerRef.current?.querySelector("[data-selected='true']") as HTMLElement;
|
|
261
|
+
if (el) el.dataset.selected = "false";
|
|
268
262
|
},
|
|
269
263
|
}),
|
|
270
264
|
[virtualizer, columnCount],
|
|
271
265
|
);
|
|
272
266
|
|
|
273
267
|
return (
|
|
274
|
-
<div
|
|
275
|
-
|
|
276
|
-
onScroll={handleScroll}
|
|
277
|
-
className={classNames(
|
|
278
|
-
"area-content bg-base overflow-auto contain-strict",
|
|
279
|
-
)}
|
|
280
|
-
{...aria}
|
|
281
|
-
>
|
|
282
|
-
<div
|
|
283
|
-
className={classNames(
|
|
284
|
-
"inline-flex flex-nowrap gap-2 justify-center min-h-full min-w-full",
|
|
285
|
-
)}
|
|
286
|
-
>
|
|
268
|
+
<div ref={scrollerRef} onScroll={handleScroll} className={cn("area-content bg-default overflow-auto")} {...aria}>
|
|
269
|
+
<div className={cn("flex flex-nowrap justify-center gap-2 min-h-full")}>
|
|
287
270
|
<div
|
|
288
|
-
className={
|
|
271
|
+
className={cn(
|
|
289
272
|
"box-content",
|
|
273
|
+
fullWidth && "flex-1",
|
|
290
274
|
padding === "sm" && "p-1",
|
|
291
275
|
padding === "md" && "p-2",
|
|
292
276
|
padding === "lg" && "p-4",
|
|
293
277
|
)}
|
|
278
|
+
style={{
|
|
279
|
+
minWidth,
|
|
280
|
+
maxWidth,
|
|
281
|
+
}}
|
|
294
282
|
>
|
|
295
283
|
<div
|
|
296
284
|
style={{
|
|
297
|
-
height: virtualizer.getTotalSize(),
|
|
285
|
+
height: virtualizer.getTotalSize() + extraSize,
|
|
298
286
|
}}
|
|
299
287
|
>
|
|
300
288
|
<div style={{ height: virtualItems[0]?.start }} />
|
|
301
|
-
{virtualItems.map(({ key, index
|
|
302
|
-
<div
|
|
303
|
-
className="flex"
|
|
304
|
-
key={key}
|
|
305
|
-
data-index={index}
|
|
306
|
-
ref={measureElement}
|
|
307
|
-
>
|
|
289
|
+
{virtualItems.map(({ key, index }) => (
|
|
290
|
+
<div className="flex" key={key} data-index={index} ref={virtualizer.measureElement}>
|
|
308
291
|
{columnMap.map((col) => (
|
|
309
292
|
<div
|
|
310
293
|
key={`${key}:${col}`}
|
|
311
294
|
data-index={index * columnCount + col}
|
|
312
|
-
className={
|
|
295
|
+
className={cn("grid rounded-capped")}
|
|
296
|
+
style={{
|
|
297
|
+
minWidth: width,
|
|
298
|
+
minHeight: height,
|
|
299
|
+
}}
|
|
313
300
|
>
|
|
314
|
-
{itemList[index * columnCount + col]
|
|
315
|
-
children(
|
|
316
|
-
|
|
317
|
-
)}
|
|
301
|
+
{itemList[index * columnCount + col]?.item
|
|
302
|
+
? children(itemList[index * columnCount + col])
|
|
303
|
+
: itemList[index * columnCount + col]?.item === null && <Skeleton className="p-2" />}
|
|
318
304
|
</div>
|
|
319
305
|
))}
|
|
320
306
|
</div>
|
|
321
307
|
))}
|
|
322
|
-
{
|
|
323
|
-
<div
|
|
308
|
+
{loading && (
|
|
309
|
+
<div>
|
|
310
|
+
<Skeleton />
|
|
311
|
+
</div>
|
|
324
312
|
)}
|
|
325
313
|
</div>
|
|
326
314
|
</div>
|
|
327
315
|
{!hideScroller && (
|
|
328
|
-
<div className={
|
|
316
|
+
<div className={cn("self-end sticky bottom-2")}>
|
|
329
317
|
<Scroller
|
|
330
318
|
vertical
|
|
331
319
|
scrollActive={scrollActive}
|
|
332
320
|
onStart={() => virtualizer.scrollToIndex(0)}
|
|
333
|
-
onPrevious={() =>
|
|
334
|
-
|
|
335
|
-
virtualizer.scrollBy(-scrollerRef.current.offsetHeight)
|
|
336
|
-
}
|
|
337
|
-
onNext={() =>
|
|
338
|
-
scrollerRef.current &&
|
|
339
|
-
virtualizer.scrollBy(scrollerRef.current.offsetHeight)
|
|
340
|
-
}
|
|
321
|
+
onPrevious={() => scrollerRef.current && virtualizer.scrollBy(-scrollerRef.current.offsetHeight)}
|
|
322
|
+
onNext={() => scrollerRef.current && virtualizer.scrollBy(scrollerRef.current.offsetHeight)}
|
|
341
323
|
onEnd={() => virtualizer.scrollToIndex(count - 1)}
|
|
342
324
|
/>
|
|
343
325
|
</div>
|
|
@@ -346,7 +328,34 @@ const _VirtualGallery = <T extends KeyValue>({
|
|
|
346
328
|
</div>
|
|
347
329
|
);
|
|
348
330
|
};
|
|
349
|
-
_VirtualGallery.displayName = "VirtualGallery";
|
|
350
331
|
|
|
351
332
|
const GenericMemo: <T>(c: T) => T = memo;
|
|
352
|
-
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* VirtualGallery component provides a virtualized gallery view for displaying a large list of items efficiently.
|
|
336
|
+
* It supports features like fixed columns, loading states, and customizable item rendering.
|
|
337
|
+
* The component uses virtualization to render only the visible items, improving performance for large datasets.
|
|
338
|
+
* It also provides a scroller for easy navigation through the items.
|
|
339
|
+
*
|
|
340
|
+
* @example
|
|
341
|
+
* ```jsx
|
|
342
|
+
* <VirtualGallery
|
|
343
|
+
* items={items}
|
|
344
|
+
* columns={3}
|
|
345
|
+
* defaultHeight={100}
|
|
346
|
+
* defaultWidth={100}
|
|
347
|
+
* fullWidth={true}
|
|
348
|
+
* onLoadMore={(lastIndex) => console.log("Load more items from index:", lastIndex)}
|
|
349
|
+
* onScroll={(top) => console.log("Scrolled to top:", top)}
|
|
350
|
+
* >
|
|
351
|
+
* {({ item, index, isLast }) => (
|
|
352
|
+
* <div className="gallery-item" key={item.id}>
|
|
353
|
+
* <img src={item.image} alt={item.label} />
|
|
354
|
+
* <p>{item.label}</p>
|
|
355
|
+
* </div>
|
|
356
|
+
* )}
|
|
357
|
+
* </VirtualGallery>
|
|
358
|
+
* ```
|
|
359
|
+
* @see {@link https://tanstack.com/docs/react-virtual/latest/overview}
|
|
360
|
+
*/
|
|
361
|
+
export const VirtualGallery = GenericMemo(VirtualGalleryComponent);
|