@lotics/ui 2.6.0 → 3.0.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.
- package/package.json +2 -15
- package/src/format_date.test.ts +64 -0
- package/src/format_date.ts +71 -0
- package/src/react_native.d.ts +2 -2
- package/src/cell_date.tsx +0 -30
- package/src/cell_date_format.test.ts +0 -32
- package/src/cell_date_format.ts +0 -73
- package/src/cell_number.test.ts +0 -42
- package/src/cell_number.tsx +0 -25
- package/src/cell_number_format.ts +0 -42
- package/src/cell_select.tsx +0 -68
- package/src/cell_text.tsx +0 -45
- package/src/grid/data_grid.tsx +0 -2003
- package/src/grid/data_grid_columns.test.ts +0 -72
- package/src/grid/data_grid_columns.ts +0 -30
- package/src/grid/data_grid_context.ts +0 -119
- package/src/grid/dispatch_safely.ts +0 -39
- package/src/grid/engine.module.css +0 -114
- package/src/grid/engine.tsx +0 -1042
- package/src/grid/helpers.ts +0 -205
- package/src/grid/layout.test.ts +0 -515
- package/src/grid/layout.ts +0 -425
- package/src/grid/recycling.test.ts +0 -236
- package/src/grid/recycling.ts +0 -172
- package/src/grid/row_cell.module.css +0 -105
- package/src/grid/row_cell.tsx +0 -313
- package/src/grid/search_highlight.ts +0 -71
- package/src/grid/select_cell.tsx +0 -58
- package/src/grid/select_group_summary_cell.tsx +0 -76
- package/src/grid/select_header_cell.tsx +0 -32
- package/src/grid/skeleton_row.module.css +0 -34
- package/src/grid/skeleton_row.tsx +0 -20
- package/src/grid/use_grid_groups.ts +0 -311
- package/src/grid/use_scroll_to_cell.ts +0 -135
- package/src/grid/use_virtual_grid.ts +0 -383
- package/src/grid/visibility.test.ts +0 -208
- package/src/grid/visibility.ts +0 -77
- package/src/kanban/constants.ts +0 -18
- package/src/kanban/default_renderers.tsx +0 -160
- package/src/kanban/drag_preview.tsx +0 -157
- package/src/kanban/index.ts +0 -13
- package/src/kanban/insert_card_zone.tsx +0 -135
- package/src/kanban/kanban_board.tsx +0 -635
- package/src/kanban/kanban_card.tsx +0 -321
- package/src/kanban/kanban_column.tsx +0 -499
- package/src/kanban/placeholders.tsx +0 -54
- package/src/kanban/types.ts +0 -116
package/src/grid/engine.tsx
DELETED
|
@@ -1,1042 +0,0 @@
|
|
|
1
|
-
/// <reference path="../react_native.d.ts" />
|
|
2
|
-
// Force-load the react-native type augmentation file so consumers picking up
|
|
3
|
-
// the grid via subpath imports (e.g. `@lotics/ui/grid/data_grid`) see the
|
|
4
|
-
// web-only `hovered`/`outline`/`cursor` types — they otherwise wouldn't,
|
|
5
|
-
// because TypeScript doesn't auto-include `.d.ts` files inside dependency
|
|
6
|
-
// directories. The triple-slash reference is the documented mechanism for
|
|
7
|
-
// "include this file as part of the compilation when this one is loaded".
|
|
8
|
-
import styles from "./engine.module.css";
|
|
9
|
-
import React, { memo, useEffect, useImperativeHandle, useMemo, useState } from "react";
|
|
10
|
-
import { GridGroup, GroupPathKey, RowPathKey, groupPathToKey, rowPathToKey } from "./layout";
|
|
11
|
-
import { RecycledColumn } from "./recycling";
|
|
12
|
-
import { useVirtualGrid, GridRef } from "./use_virtual_grid";
|
|
13
|
-
import { SkeletonRow } from "./skeleton_row";
|
|
14
|
-
import { StyleProp, ViewStyle } from "react-native";
|
|
15
|
-
|
|
16
|
-
export interface GridProps<TRow = unknown> {
|
|
17
|
-
selectedRows?: Set<RowPathKey>;
|
|
18
|
-
collapsedRows?: GroupPathKey[] | null;
|
|
19
|
-
/** Editing cell. Only passed if the row is being edited. */
|
|
20
|
-
editingCell?: {
|
|
21
|
-
rowKey: RowPathKey;
|
|
22
|
-
column: number;
|
|
23
|
-
} | null;
|
|
24
|
-
height: number;
|
|
25
|
-
width: number;
|
|
26
|
-
footerHeight?: number;
|
|
27
|
-
renderFooterCell?: (props: GridRenderFooterCellProps) => React.ReactNode;
|
|
28
|
-
renderRowCell: (props: GridRenderRowCellProps<TRow>) => React.ReactNode;
|
|
29
|
-
renderRow: (props: GridRenderRowProps<TRow>) => React.ReactNode;
|
|
30
|
-
renderHeader?: (props: GridRenderHeaderProps) => React.ReactNode;
|
|
31
|
-
renderFooter?: (props: GridRenderFooterProps) => React.ReactNode;
|
|
32
|
-
headerHeight?: number;
|
|
33
|
-
renderHeaderCell?: (props: GridRenderHeaderCellProps) => React.ReactNode;
|
|
34
|
-
leafRowHeight: number;
|
|
35
|
-
groups: GridGroup[];
|
|
36
|
-
groupHeadingHeight?: number;
|
|
37
|
-
groupSummaryHeight?: number;
|
|
38
|
-
spacerHeight?: number;
|
|
39
|
-
renderGroupSummaryCell?: (props: GridRenderGroupSummaryCellProps) => React.ReactNode;
|
|
40
|
-
renderGroupSummaryRow?: (props: GridRenderGroupSummaryRowProps) => React.ReactNode;
|
|
41
|
-
renderGroupHeading?: (props: GridRenderGroupHeadingProps) => React.ReactNode;
|
|
42
|
-
/** Length of the array determines number of columns. Array values correspond to their width. */
|
|
43
|
-
columns: number[];
|
|
44
|
-
/** Number of columns to freeze. Default is 0. */
|
|
45
|
-
frozenColumnCount?: number;
|
|
46
|
-
/** Maximum width for frozen columns. If frozen columns exceed this, they will be scaled down proportionally. */
|
|
47
|
-
maxFrozenColumnsWidth?: number;
|
|
48
|
-
/** Map of rowKey to row data. Used to pass row data to renderRowCell. */
|
|
49
|
-
rowData?: Map<RowPathKey, TRow>;
|
|
50
|
-
/** Map of rowKey to row index for selection bounds checking. */
|
|
51
|
-
rowKeyToIndex?: Map<RowPathKey, number>;
|
|
52
|
-
/** Selection bounds - min row index (primitive for optimization) */
|
|
53
|
-
selectionMinRowIndex?: number | null;
|
|
54
|
-
/** Selection bounds - max row index (primitive for optimization) */
|
|
55
|
-
selectionMaxRowIndex?: number | null;
|
|
56
|
-
/** Selection bounds - min column (primitive for optimization) */
|
|
57
|
-
selectionMinColumn?: number | null;
|
|
58
|
-
/** Selection bounds - max column (primitive for optimization) */
|
|
59
|
-
selectionMaxColumn?: number | null;
|
|
60
|
-
/** Active cell row index (primitive for optimization) */
|
|
61
|
-
selectionActiveRowIndex?: number | null;
|
|
62
|
-
/** Active cell column (primitive for optimization) */
|
|
63
|
-
selectionActiveColumn?: number | null;
|
|
64
|
-
/** Fill drag source row index (for fill handle) */
|
|
65
|
-
fillSourceRowIndex?: number | null;
|
|
66
|
-
/** Fill drag target row index (for fill handle) */
|
|
67
|
-
fillTargetRowIndex?: number | null;
|
|
68
|
-
/** Fill drag column (for fill handle) */
|
|
69
|
-
fillColumn?: number | null;
|
|
70
|
-
/** Optional function to get background color for a row (used for conditional row coloring) */
|
|
71
|
-
rowBackgroundColorGetter?: (row: TRow) => string | undefined;
|
|
72
|
-
/** Fires when scroll reaches near the bottom of the content. Used for infinite scroll pagination. */
|
|
73
|
-
onEndReached?: () => void;
|
|
74
|
-
/** Fires when the visible row range changes. Used for viewport-driven page loading. */
|
|
75
|
-
onVisibleRangeChange?: (rowRange: [number, number]) => void;
|
|
76
|
-
/** Number of rows to render outside the visible area for smoother scrolling. Default is 0. */
|
|
77
|
-
rowOverscan?: number;
|
|
78
|
-
/** Number of columns to render outside the visible area for smoother scrolling. Default is 0. */
|
|
79
|
-
columnOverscan?: number;
|
|
80
|
-
style?: StyleProp<ViewStyle>;
|
|
81
|
-
ref?: React.Ref<GridRef>;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export interface GridRenderGroupSummaryCellProps {
|
|
85
|
-
groupKey: GroupPathKey;
|
|
86
|
-
column: number;
|
|
87
|
-
width: number;
|
|
88
|
-
height: number;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export interface GridRenderGroupSummaryRowProps {
|
|
92
|
-
groupKey: GroupPathKey;
|
|
93
|
-
collapsed: boolean;
|
|
94
|
-
children: React.ReactNode;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export interface GridRenderHeaderCellProps {
|
|
98
|
-
column: number;
|
|
99
|
-
width: number;
|
|
100
|
-
height: number;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export interface GridRenderFooterCellProps {
|
|
104
|
-
column: number;
|
|
105
|
-
width: number;
|
|
106
|
-
height: number;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export interface GridRenderRowCellProps<TRow = unknown> {
|
|
110
|
-
rowKey: RowPathKey;
|
|
111
|
-
row: TRow;
|
|
112
|
-
column: number;
|
|
113
|
-
frozen: boolean;
|
|
114
|
-
editing: boolean;
|
|
115
|
-
active: boolean;
|
|
116
|
-
selected: boolean;
|
|
117
|
-
inSelectedRow: boolean;
|
|
118
|
-
/** Row index for fill operations */
|
|
119
|
-
rowIndex: number;
|
|
120
|
-
/** Whether this cell is the source of a fill operation */
|
|
121
|
-
isFillSource: boolean;
|
|
122
|
-
/** Whether this cell is in the fill range (target cells during drag) */
|
|
123
|
-
inFillRange: boolean;
|
|
124
|
-
/** Optional background color for the row (from conditional coloring) */
|
|
125
|
-
rowBackgroundColor?: string;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
export interface GridRenderGroupHeadingProps {
|
|
129
|
-
groupKey: GroupPathKey;
|
|
130
|
-
collapsed: boolean;
|
|
131
|
-
children: React.ReactNode;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
export interface GridRenderRowProps<TRow = unknown> {
|
|
135
|
-
rowKey: RowPathKey;
|
|
136
|
-
row: TRow;
|
|
137
|
-
selected: boolean;
|
|
138
|
-
children: React.ReactNode;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
export interface GridRenderHeaderProps {
|
|
142
|
-
children: React.ReactNode;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
export interface GridRenderFooterProps {
|
|
146
|
-
children: React.ReactNode;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
export const Grid = memo(function Grid<TRow = unknown>(props: GridProps<TRow>) {
|
|
150
|
-
const {
|
|
151
|
-
selectedRows,
|
|
152
|
-
collapsedRows = null,
|
|
153
|
-
columns,
|
|
154
|
-
frozenColumnCount = 0,
|
|
155
|
-
maxFrozenColumnsWidth,
|
|
156
|
-
groups,
|
|
157
|
-
spacerHeight = 0,
|
|
158
|
-
groupHeadingHeight = 0,
|
|
159
|
-
groupSummaryHeight = 0,
|
|
160
|
-
footerHeight = 0,
|
|
161
|
-
headerHeight = 0,
|
|
162
|
-
leafRowHeight,
|
|
163
|
-
height,
|
|
164
|
-
width,
|
|
165
|
-
renderFooterCell,
|
|
166
|
-
renderGroupHeading,
|
|
167
|
-
renderGroupSummaryCell,
|
|
168
|
-
renderGroupSummaryRow,
|
|
169
|
-
renderRowCell,
|
|
170
|
-
renderRow,
|
|
171
|
-
renderFooter,
|
|
172
|
-
renderHeader,
|
|
173
|
-
renderHeaderCell,
|
|
174
|
-
rowData,
|
|
175
|
-
rowKeyToIndex,
|
|
176
|
-
editingCell,
|
|
177
|
-
selectionMinRowIndex = null,
|
|
178
|
-
selectionMaxRowIndex = null,
|
|
179
|
-
selectionMinColumn = null,
|
|
180
|
-
selectionMaxColumn = null,
|
|
181
|
-
selectionActiveRowIndex = null,
|
|
182
|
-
selectionActiveColumn = null,
|
|
183
|
-
fillSourceRowIndex = null,
|
|
184
|
-
fillTargetRowIndex = null,
|
|
185
|
-
fillColumn = null,
|
|
186
|
-
rowBackgroundColorGetter,
|
|
187
|
-
onEndReached,
|
|
188
|
-
onVisibleRangeChange,
|
|
189
|
-
rowOverscan = 5,
|
|
190
|
-
columnOverscan = 2,
|
|
191
|
-
ref,
|
|
192
|
-
style,
|
|
193
|
-
} = props;
|
|
194
|
-
|
|
195
|
-
const {
|
|
196
|
-
api,
|
|
197
|
-
frozenColumns,
|
|
198
|
-
scrollableColumns,
|
|
199
|
-
recycledRows,
|
|
200
|
-
layout,
|
|
201
|
-
scrollViewRef,
|
|
202
|
-
handleScroll,
|
|
203
|
-
isGroupCollapsed,
|
|
204
|
-
isScrolledX,
|
|
205
|
-
} = useVirtualGrid({
|
|
206
|
-
columns,
|
|
207
|
-
frozenColumnCount,
|
|
208
|
-
maxFrozenColumnsWidth,
|
|
209
|
-
groups,
|
|
210
|
-
groupHeadingHeight,
|
|
211
|
-
groupSummaryHeight,
|
|
212
|
-
leafRowHeight,
|
|
213
|
-
spacerHeight,
|
|
214
|
-
collapsedRows,
|
|
215
|
-
width,
|
|
216
|
-
height,
|
|
217
|
-
headerHeight,
|
|
218
|
-
footerHeight,
|
|
219
|
-
onEndReached,
|
|
220
|
-
onVisibleRangeChange,
|
|
221
|
-
rowOverscan,
|
|
222
|
-
columnOverscan,
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
useImperativeHandle(ref, () => api, [api]);
|
|
226
|
-
|
|
227
|
-
const [scrollbarHeight, setScrollbarHeight] = useState(0);
|
|
228
|
-
|
|
229
|
-
useEffect(() => {
|
|
230
|
-
const element = scrollViewRef.current;
|
|
231
|
-
if (!element) return;
|
|
232
|
-
|
|
233
|
-
const measureScrollbar = () => {
|
|
234
|
-
const height = element.offsetHeight - element.clientHeight;
|
|
235
|
-
setScrollbarHeight(height);
|
|
236
|
-
};
|
|
237
|
-
|
|
238
|
-
measureScrollbar();
|
|
239
|
-
|
|
240
|
-
const resizeObserver = new ResizeObserver(measureScrollbar);
|
|
241
|
-
resizeObserver.observe(element);
|
|
242
|
-
|
|
243
|
-
return () => resizeObserver.disconnect();
|
|
244
|
-
}, [scrollViewRef]);
|
|
245
|
-
|
|
246
|
-
const footerOffset = useMemo(
|
|
247
|
-
() => layout.scrollViewHeight + layout.headerHeight - scrollbarHeight,
|
|
248
|
-
[layout.scrollViewHeight, layout.headerHeight, scrollbarHeight],
|
|
249
|
-
);
|
|
250
|
-
|
|
251
|
-
return (
|
|
252
|
-
<div
|
|
253
|
-
ref={scrollViewRef}
|
|
254
|
-
className={styles.root}
|
|
255
|
-
onScroll={handleScroll}
|
|
256
|
-
data-testid="grid-scroll-container"
|
|
257
|
-
data-scrolled-x={isScrolledX || undefined}
|
|
258
|
-
style={style as React.CSSProperties}
|
|
259
|
-
>
|
|
260
|
-
<div
|
|
261
|
-
className={styles.content}
|
|
262
|
-
style={{ width: layout.contentWidth, height: layout.totalHeight }}
|
|
263
|
-
>
|
|
264
|
-
{headerHeight > 0 && renderHeaderCell !== undefined && renderHeader !== undefined && (
|
|
265
|
-
<Header
|
|
266
|
-
height={headerHeight}
|
|
267
|
-
width={layout.contentWidth}
|
|
268
|
-
frozenColumns={frozenColumns.columns}
|
|
269
|
-
frozenColumnsWidth={frozenColumns.width}
|
|
270
|
-
scrollableColumns={scrollableColumns.columns}
|
|
271
|
-
scrollableColumnsWidth={scrollableColumns.width}
|
|
272
|
-
renderHeader={renderHeader}
|
|
273
|
-
renderHeaderCell={renderHeaderCell}
|
|
274
|
-
/>
|
|
275
|
-
)}
|
|
276
|
-
<div className={styles.rows_wrapper} style={{ top: headerHeight }}>
|
|
277
|
-
{recycledRows.map((recycledRow) => {
|
|
278
|
-
switch (recycledRow.type) {
|
|
279
|
-
case "row": {
|
|
280
|
-
const rowKey = rowPathToKey(recycledRow.rowPath);
|
|
281
|
-
const row = rowData?.get(rowKey);
|
|
282
|
-
if (row === undefined) {
|
|
283
|
-
return (
|
|
284
|
-
<SkeletonRow
|
|
285
|
-
key={recycledRow.key}
|
|
286
|
-
y={recycledRow.y}
|
|
287
|
-
height={recycledRow.height}
|
|
288
|
-
width={layout.contentWidth}
|
|
289
|
-
/>
|
|
290
|
-
);
|
|
291
|
-
}
|
|
292
|
-
const rowIndex = rowKeyToIndex?.get(rowKey);
|
|
293
|
-
const selected = selectedRows?.has(rowKey) ?? false;
|
|
294
|
-
|
|
295
|
-
// Check if this row is within selection bounds
|
|
296
|
-
const rowInSelection =
|
|
297
|
-
rowIndex !== undefined &&
|
|
298
|
-
selectionMinRowIndex !== null &&
|
|
299
|
-
selectionMaxRowIndex !== null &&
|
|
300
|
-
rowIndex >= selectionMinRowIndex &&
|
|
301
|
-
rowIndex <= selectionMaxRowIndex;
|
|
302
|
-
|
|
303
|
-
// Check if this row contains the active cell
|
|
304
|
-
const isActiveRow =
|
|
305
|
-
rowIndex !== undefined &&
|
|
306
|
-
selectionActiveRowIndex !== null &&
|
|
307
|
-
rowIndex === selectionActiveRowIndex;
|
|
308
|
-
|
|
309
|
-
// Only pass column selection props if this row needs them
|
|
310
|
-
// This prevents re-renders of rows outside selection when selection changes
|
|
311
|
-
const needsSelectionProps = rowInSelection || isActiveRow;
|
|
312
|
-
|
|
313
|
-
// Compute fill range to check if this row is affected
|
|
314
|
-
const fillMinRow =
|
|
315
|
-
fillSourceRowIndex !== null && fillTargetRowIndex !== null
|
|
316
|
-
? Math.min(fillSourceRowIndex, fillTargetRowIndex)
|
|
317
|
-
: null;
|
|
318
|
-
const fillMaxRow =
|
|
319
|
-
fillSourceRowIndex !== null && fillTargetRowIndex !== null
|
|
320
|
-
? Math.max(fillSourceRowIndex, fillTargetRowIndex)
|
|
321
|
-
: null;
|
|
322
|
-
|
|
323
|
-
// Check if this row needs fill props
|
|
324
|
-
const needsFillProps =
|
|
325
|
-
rowIndex !== undefined &&
|
|
326
|
-
fillMinRow !== null &&
|
|
327
|
-
fillMaxRow !== null &&
|
|
328
|
-
rowIndex >= fillMinRow &&
|
|
329
|
-
rowIndex <= fillMaxRow;
|
|
330
|
-
|
|
331
|
-
return (
|
|
332
|
-
<Row
|
|
333
|
-
key={recycledRow.key}
|
|
334
|
-
y={recycledRow.y}
|
|
335
|
-
rowKey={rowKey}
|
|
336
|
-
row={row}
|
|
337
|
-
rowIndex={rowIndex ?? -1}
|
|
338
|
-
selected={selected}
|
|
339
|
-
editingCell={editingCell?.rowKey === rowKey ? editingCell : null}
|
|
340
|
-
height={recycledRow.height}
|
|
341
|
-
width={layout.contentWidth}
|
|
342
|
-
frozenColumns={frozenColumns.columns}
|
|
343
|
-
frozenColumnsWidth={frozenColumns.width}
|
|
344
|
-
scrollableColumns={scrollableColumns.columns}
|
|
345
|
-
scrollableColumnsWidth={scrollableColumns.width}
|
|
346
|
-
rowInSelection={rowInSelection}
|
|
347
|
-
isActiveRow={isActiveRow}
|
|
348
|
-
selectionMinColumn={needsSelectionProps ? selectionMinColumn : null}
|
|
349
|
-
selectionMaxColumn={needsSelectionProps ? selectionMaxColumn : null}
|
|
350
|
-
selectionActiveColumn={needsSelectionProps ? selectionActiveColumn : null}
|
|
351
|
-
fillSourceRowIndex={needsFillProps ? fillSourceRowIndex : null}
|
|
352
|
-
fillTargetRowIndex={needsFillProps ? fillTargetRowIndex : null}
|
|
353
|
-
fillColumn={needsFillProps ? fillColumn : null}
|
|
354
|
-
rowBackgroundColor={row ? rowBackgroundColorGetter?.(row) : undefined}
|
|
355
|
-
renderRow={renderRow}
|
|
356
|
-
renderRowCell={renderRowCell}
|
|
357
|
-
/>
|
|
358
|
-
);
|
|
359
|
-
}
|
|
360
|
-
case "group_heading": {
|
|
361
|
-
if (renderGroupHeading === undefined) {
|
|
362
|
-
return null;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
return (
|
|
366
|
-
<GroupHeadingRow
|
|
367
|
-
key={recycledRow.key}
|
|
368
|
-
y={recycledRow.y}
|
|
369
|
-
groupKey={groupPathToKey(recycledRow.groupPath)}
|
|
370
|
-
height={recycledRow.height}
|
|
371
|
-
width={layout.contentWidth}
|
|
372
|
-
renderGroupHeading={renderGroupHeading}
|
|
373
|
-
collapsed={isGroupCollapsed(groupPathToKey(recycledRow.groupPath))}
|
|
374
|
-
/>
|
|
375
|
-
);
|
|
376
|
-
}
|
|
377
|
-
case "group_summary": {
|
|
378
|
-
if (renderGroupSummaryCell === undefined) {
|
|
379
|
-
return null;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
const groupKey = groupPathToKey(recycledRow.groupPath);
|
|
383
|
-
|
|
384
|
-
return (
|
|
385
|
-
<GroupSummaryRow
|
|
386
|
-
key={recycledRow.key}
|
|
387
|
-
y={recycledRow.y}
|
|
388
|
-
groupKey={groupKey}
|
|
389
|
-
height={recycledRow.height}
|
|
390
|
-
width={layout.contentWidth}
|
|
391
|
-
frozenColumns={frozenColumns.columns}
|
|
392
|
-
frozenColumnsWidth={frozenColumns.width}
|
|
393
|
-
scrollableColumns={scrollableColumns.columns}
|
|
394
|
-
scrollableColumnsWidth={scrollableColumns.width}
|
|
395
|
-
renderGroupSummaryCell={renderGroupSummaryCell}
|
|
396
|
-
renderGroupSummaryRow={renderGroupSummaryRow}
|
|
397
|
-
collapsed={isGroupCollapsed(groupKey)}
|
|
398
|
-
/>
|
|
399
|
-
);
|
|
400
|
-
}
|
|
401
|
-
case "spacer":
|
|
402
|
-
return (
|
|
403
|
-
<SpacerRow key={recycledRow.key} y={recycledRow.y} height={recycledRow.height} />
|
|
404
|
-
);
|
|
405
|
-
}
|
|
406
|
-
})}
|
|
407
|
-
</div>
|
|
408
|
-
{footerHeight > 0 && renderFooterCell !== undefined && renderFooter !== undefined && (
|
|
409
|
-
<Footer
|
|
410
|
-
y={footerOffset}
|
|
411
|
-
height={footerHeight}
|
|
412
|
-
width={layout.contentWidth}
|
|
413
|
-
frozenColumns={frozenColumns.columns}
|
|
414
|
-
frozenColumnsWidth={frozenColumns.width}
|
|
415
|
-
scrollableColumns={scrollableColumns.columns}
|
|
416
|
-
scrollableColumnsWidth={scrollableColumns.width}
|
|
417
|
-
renderFooter={renderFooter}
|
|
418
|
-
renderFooterCell={renderFooterCell}
|
|
419
|
-
/>
|
|
420
|
-
)}
|
|
421
|
-
</div>
|
|
422
|
-
</div>
|
|
423
|
-
);
|
|
424
|
-
}) as <TRow = unknown>(props: GridProps<TRow>) => React.ReactNode;
|
|
425
|
-
|
|
426
|
-
interface HeaderProps {
|
|
427
|
-
height: number;
|
|
428
|
-
width: number;
|
|
429
|
-
frozenColumns: RecycledColumn[];
|
|
430
|
-
frozenColumnsWidth: number;
|
|
431
|
-
scrollableColumns: RecycledColumn[];
|
|
432
|
-
scrollableColumnsWidth: number;
|
|
433
|
-
renderHeader: (props: GridRenderHeaderProps) => React.ReactNode;
|
|
434
|
-
renderHeaderCell: (props: GridRenderHeaderCellProps) => React.ReactNode;
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
const Header = memo(function Header(props: HeaderProps) {
|
|
438
|
-
const {
|
|
439
|
-
height,
|
|
440
|
-
width,
|
|
441
|
-
frozenColumns,
|
|
442
|
-
frozenColumnsWidth,
|
|
443
|
-
scrollableColumns,
|
|
444
|
-
scrollableColumnsWidth,
|
|
445
|
-
renderHeader,
|
|
446
|
-
renderHeaderCell,
|
|
447
|
-
} = props;
|
|
448
|
-
|
|
449
|
-
const children = useMemo(
|
|
450
|
-
() => (
|
|
451
|
-
<>
|
|
452
|
-
<div className={styles.frozen_cells} style={{ width: frozenColumnsWidth, height }}>
|
|
453
|
-
{frozenColumns.map((columnData) => (
|
|
454
|
-
<Cell key={columnData.key} height={height} width={columnData.width}>
|
|
455
|
-
{renderHeaderCell({
|
|
456
|
-
column: columnData.column,
|
|
457
|
-
width: columnData.width,
|
|
458
|
-
height,
|
|
459
|
-
})}
|
|
460
|
-
</Cell>
|
|
461
|
-
))}
|
|
462
|
-
</div>
|
|
463
|
-
<div className={styles.scrollable_cells} style={{ width: scrollableColumnsWidth, height }}>
|
|
464
|
-
{scrollableColumns.map((columnData) => (
|
|
465
|
-
<ScrollableCell
|
|
466
|
-
key={columnData.key}
|
|
467
|
-
x={columnData.x}
|
|
468
|
-
height={height}
|
|
469
|
-
width={columnData.width}
|
|
470
|
-
>
|
|
471
|
-
{renderHeaderCell({
|
|
472
|
-
column: columnData.column,
|
|
473
|
-
width: columnData.width,
|
|
474
|
-
height,
|
|
475
|
-
})}
|
|
476
|
-
</ScrollableCell>
|
|
477
|
-
))}
|
|
478
|
-
</div>
|
|
479
|
-
</>
|
|
480
|
-
),
|
|
481
|
-
[
|
|
482
|
-
frozenColumns,
|
|
483
|
-
frozenColumnsWidth,
|
|
484
|
-
scrollableColumns,
|
|
485
|
-
scrollableColumnsWidth,
|
|
486
|
-
renderHeaderCell,
|
|
487
|
-
height,
|
|
488
|
-
],
|
|
489
|
-
);
|
|
490
|
-
|
|
491
|
-
return (
|
|
492
|
-
<div className={styles.header_wrapper} style={{ width, height }}>
|
|
493
|
-
{renderHeader({ children })}
|
|
494
|
-
</div>
|
|
495
|
-
);
|
|
496
|
-
});
|
|
497
|
-
|
|
498
|
-
interface FooterProps {
|
|
499
|
-
height: number;
|
|
500
|
-
width: number;
|
|
501
|
-
y: number;
|
|
502
|
-
frozenColumns: RecycledColumn[];
|
|
503
|
-
frozenColumnsWidth: number;
|
|
504
|
-
scrollableColumns: RecycledColumn[];
|
|
505
|
-
scrollableColumnsWidth: number;
|
|
506
|
-
renderFooter: (props: GridRenderFooterProps) => React.ReactNode;
|
|
507
|
-
renderFooterCell: (props: GridRenderFooterCellProps) => React.ReactNode;
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
const Footer = memo(function Footer(props: FooterProps) {
|
|
511
|
-
const {
|
|
512
|
-
y,
|
|
513
|
-
height,
|
|
514
|
-
width,
|
|
515
|
-
frozenColumns,
|
|
516
|
-
frozenColumnsWidth,
|
|
517
|
-
scrollableColumns,
|
|
518
|
-
scrollableColumnsWidth,
|
|
519
|
-
renderFooterCell,
|
|
520
|
-
renderFooter,
|
|
521
|
-
} = props;
|
|
522
|
-
|
|
523
|
-
const children = useMemo(
|
|
524
|
-
() => (
|
|
525
|
-
<>
|
|
526
|
-
<div className={styles.frozen_cells} style={{ width: frozenColumnsWidth, height }}>
|
|
527
|
-
{frozenColumns.map((columnData) => (
|
|
528
|
-
<Cell key={columnData.key} height={height} width={columnData.width}>
|
|
529
|
-
{renderFooterCell({
|
|
530
|
-
column: columnData.column,
|
|
531
|
-
width: columnData.width,
|
|
532
|
-
height,
|
|
533
|
-
})}
|
|
534
|
-
</Cell>
|
|
535
|
-
))}
|
|
536
|
-
</div>
|
|
537
|
-
<div className={styles.scrollable_cells} style={{ width: scrollableColumnsWidth, height }}>
|
|
538
|
-
{scrollableColumns.map((columnData) => (
|
|
539
|
-
<ScrollableCell
|
|
540
|
-
key={columnData.key}
|
|
541
|
-
x={columnData.x}
|
|
542
|
-
height={height}
|
|
543
|
-
width={columnData.width}
|
|
544
|
-
>
|
|
545
|
-
{renderFooterCell({
|
|
546
|
-
column: columnData.column,
|
|
547
|
-
width: columnData.width,
|
|
548
|
-
height,
|
|
549
|
-
})}
|
|
550
|
-
</ScrollableCell>
|
|
551
|
-
))}
|
|
552
|
-
</div>
|
|
553
|
-
</>
|
|
554
|
-
),
|
|
555
|
-
[
|
|
556
|
-
frozenColumns,
|
|
557
|
-
frozenColumnsWidth,
|
|
558
|
-
scrollableColumns,
|
|
559
|
-
scrollableColumnsWidth,
|
|
560
|
-
renderFooterCell,
|
|
561
|
-
height,
|
|
562
|
-
],
|
|
563
|
-
);
|
|
564
|
-
|
|
565
|
-
return (
|
|
566
|
-
<div data-testid="footer" className={styles.footer_wrapper} style={{ top: y, width, height }}>
|
|
567
|
-
{renderFooter({ children })}
|
|
568
|
-
</div>
|
|
569
|
-
);
|
|
570
|
-
});
|
|
571
|
-
|
|
572
|
-
interface RowProps<TRow = unknown> {
|
|
573
|
-
height: number;
|
|
574
|
-
width: number;
|
|
575
|
-
y: number;
|
|
576
|
-
rowKey: RowPathKey;
|
|
577
|
-
row: TRow | undefined;
|
|
578
|
-
rowIndex: number;
|
|
579
|
-
selected: boolean;
|
|
580
|
-
/** Editing cell. Only passed if the row is being edited. */
|
|
581
|
-
editingCell?: {
|
|
582
|
-
rowKey: RowPathKey;
|
|
583
|
-
column: number;
|
|
584
|
-
} | null;
|
|
585
|
-
frozenColumns: RecycledColumn[];
|
|
586
|
-
frozenColumnsWidth: number;
|
|
587
|
-
scrollableColumns: RecycledColumn[];
|
|
588
|
-
scrollableColumnsWidth: number;
|
|
589
|
-
/** Whether this row is within the selection range */
|
|
590
|
-
rowInSelection: boolean;
|
|
591
|
-
/** Whether this row contains the active cell */
|
|
592
|
-
isActiveRow: boolean;
|
|
593
|
-
/** Selection min column (for computing cell selection). Only passed if row is in selection. */
|
|
594
|
-
selectionMinColumn: number | null;
|
|
595
|
-
/** Selection max column (for computing cell selection). Only passed if row is in selection. */
|
|
596
|
-
selectionMaxColumn: number | null;
|
|
597
|
-
/** Active cell column. Only passed if row contains active cell. */
|
|
598
|
-
selectionActiveColumn: number | null;
|
|
599
|
-
/** Fill source row index. Only passed if this row is affected by fill. */
|
|
600
|
-
fillSourceRowIndex: number | null;
|
|
601
|
-
/** Fill target row index. Only passed if this row is affected by fill. */
|
|
602
|
-
fillTargetRowIndex: number | null;
|
|
603
|
-
/** Fill column. Only passed if this row is affected by fill. */
|
|
604
|
-
fillColumn: number | null;
|
|
605
|
-
/** Background color for the row (from conditional coloring) */
|
|
606
|
-
rowBackgroundColor?: string;
|
|
607
|
-
renderRowCell: (props: GridRenderRowCellProps<TRow>) => React.ReactNode;
|
|
608
|
-
renderRow: (props: GridRenderRowProps<TRow>) => React.ReactNode;
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
const Row = memo(function Row<TRow = unknown>(props: RowProps<TRow>) {
|
|
612
|
-
const {
|
|
613
|
-
width,
|
|
614
|
-
height,
|
|
615
|
-
y,
|
|
616
|
-
rowKey,
|
|
617
|
-
row,
|
|
618
|
-
rowIndex,
|
|
619
|
-
selected,
|
|
620
|
-
editingCell,
|
|
621
|
-
frozenColumns,
|
|
622
|
-
frozenColumnsWidth,
|
|
623
|
-
scrollableColumns,
|
|
624
|
-
scrollableColumnsWidth,
|
|
625
|
-
rowInSelection,
|
|
626
|
-
isActiveRow,
|
|
627
|
-
selectionMinColumn,
|
|
628
|
-
selectionMaxColumn,
|
|
629
|
-
selectionActiveColumn,
|
|
630
|
-
fillSourceRowIndex,
|
|
631
|
-
fillTargetRowIndex,
|
|
632
|
-
fillColumn,
|
|
633
|
-
rowBackgroundColor,
|
|
634
|
-
renderRow,
|
|
635
|
-
renderRowCell,
|
|
636
|
-
} = props;
|
|
637
|
-
|
|
638
|
-
// Compute fill range for this row
|
|
639
|
-
const fillMinRow =
|
|
640
|
-
fillSourceRowIndex !== null && fillTargetRowIndex !== null
|
|
641
|
-
? Math.min(fillSourceRowIndex, fillTargetRowIndex)
|
|
642
|
-
: null;
|
|
643
|
-
const fillMaxRow =
|
|
644
|
-
fillSourceRowIndex !== null && fillTargetRowIndex !== null
|
|
645
|
-
? Math.max(fillSourceRowIndex, fillTargetRowIndex)
|
|
646
|
-
: null;
|
|
647
|
-
|
|
648
|
-
const children = (
|
|
649
|
-
<>
|
|
650
|
-
<div className={styles.frozen_cells} style={{ width: frozenColumnsWidth, height }}>
|
|
651
|
-
{frozenColumns.map((columnData) => {
|
|
652
|
-
const col = columnData.column;
|
|
653
|
-
const selectedCell =
|
|
654
|
-
rowInSelection &&
|
|
655
|
-
selectionMinColumn !== null &&
|
|
656
|
-
selectionMaxColumn !== null &&
|
|
657
|
-
col >= selectionMinColumn &&
|
|
658
|
-
col <= selectionMaxColumn;
|
|
659
|
-
const active = isActiveRow && selectionActiveColumn === col;
|
|
660
|
-
|
|
661
|
-
const isEditing = editingCell?.rowKey === rowKey && editingCell.column === col;
|
|
662
|
-
|
|
663
|
-
// Fill source: this cell is the origin of the fill
|
|
664
|
-
const isFillSource = fillSourceRowIndex === rowIndex && fillColumn === col;
|
|
665
|
-
|
|
666
|
-
// In fill range: this cell is being filled (excluding source)
|
|
667
|
-
const inFillRange =
|
|
668
|
-
fillColumn === col &&
|
|
669
|
-
fillMinRow !== null &&
|
|
670
|
-
fillMaxRow !== null &&
|
|
671
|
-
rowIndex >= fillMinRow &&
|
|
672
|
-
rowIndex <= fillMaxRow &&
|
|
673
|
-
rowIndex !== fillSourceRowIndex;
|
|
674
|
-
|
|
675
|
-
return (
|
|
676
|
-
<FrozenCellWrapper
|
|
677
|
-
key={columnData.key}
|
|
678
|
-
rowKey={rowKey}
|
|
679
|
-
row={row}
|
|
680
|
-
rowIndex={rowIndex}
|
|
681
|
-
column={col}
|
|
682
|
-
editing={isEditing}
|
|
683
|
-
cellWidth={columnData.width}
|
|
684
|
-
cellHeight={height}
|
|
685
|
-
active={active}
|
|
686
|
-
selected={selectedCell}
|
|
687
|
-
inSelectedRow={selected}
|
|
688
|
-
isFillSource={isFillSource}
|
|
689
|
-
inFillRange={inFillRange}
|
|
690
|
-
rowBackgroundColor={rowBackgroundColor}
|
|
691
|
-
renderRowCell={renderRowCell}
|
|
692
|
-
/>
|
|
693
|
-
);
|
|
694
|
-
})}
|
|
695
|
-
</div>
|
|
696
|
-
<div className={styles.scrollable_cells} style={{ width: scrollableColumnsWidth, height }}>
|
|
697
|
-
{scrollableColumns.map((columnData) => {
|
|
698
|
-
const col = columnData.column;
|
|
699
|
-
const selectedCell =
|
|
700
|
-
rowInSelection &&
|
|
701
|
-
selectionMinColumn !== null &&
|
|
702
|
-
selectionMaxColumn !== null &&
|
|
703
|
-
col >= selectionMinColumn &&
|
|
704
|
-
col <= selectionMaxColumn;
|
|
705
|
-
const active = isActiveRow && selectionActiveColumn === col;
|
|
706
|
-
|
|
707
|
-
const isEditing = editingCell?.rowKey === rowKey && editingCell.column === col;
|
|
708
|
-
|
|
709
|
-
// Fill source: this cell is the origin of the fill
|
|
710
|
-
const isFillSource = fillSourceRowIndex === rowIndex && fillColumn === col;
|
|
711
|
-
|
|
712
|
-
// In fill range: this cell is being filled (excluding source)
|
|
713
|
-
const inFillRange =
|
|
714
|
-
fillColumn === col &&
|
|
715
|
-
fillMinRow !== null &&
|
|
716
|
-
fillMaxRow !== null &&
|
|
717
|
-
rowIndex >= fillMinRow &&
|
|
718
|
-
rowIndex <= fillMaxRow &&
|
|
719
|
-
rowIndex !== fillSourceRowIndex;
|
|
720
|
-
|
|
721
|
-
return (
|
|
722
|
-
<ScrollableCellWrapper
|
|
723
|
-
key={columnData.key}
|
|
724
|
-
rowKey={rowKey}
|
|
725
|
-
row={row}
|
|
726
|
-
rowIndex={rowIndex}
|
|
727
|
-
column={col}
|
|
728
|
-
editing={isEditing}
|
|
729
|
-
x={columnData.x}
|
|
730
|
-
cellWidth={columnData.width}
|
|
731
|
-
cellHeight={height}
|
|
732
|
-
active={active}
|
|
733
|
-
selected={selectedCell}
|
|
734
|
-
inSelectedRow={selected}
|
|
735
|
-
isFillSource={isFillSource}
|
|
736
|
-
inFillRange={inFillRange}
|
|
737
|
-
rowBackgroundColor={rowBackgroundColor}
|
|
738
|
-
renderRowCell={renderRowCell}
|
|
739
|
-
/>
|
|
740
|
-
);
|
|
741
|
-
})}
|
|
742
|
-
</div>
|
|
743
|
-
</>
|
|
744
|
-
);
|
|
745
|
-
|
|
746
|
-
return (
|
|
747
|
-
<div className={styles.row_wrapper} style={{ height, width, transform: `translateY(${y}px)` }}>
|
|
748
|
-
{renderRow({ children, rowKey, row: row as TRow, selected })}
|
|
749
|
-
</div>
|
|
750
|
-
);
|
|
751
|
-
}) as <TRow = unknown>(props: RowProps<TRow>) => React.ReactNode;
|
|
752
|
-
|
|
753
|
-
/** Memoized cell wrapper - only re-renders when active/selected change */
|
|
754
|
-
interface FrozenCellWrapperProps<TRow = unknown> {
|
|
755
|
-
rowKey: RowPathKey;
|
|
756
|
-
row: TRow | undefined;
|
|
757
|
-
rowIndex: number;
|
|
758
|
-
column: number;
|
|
759
|
-
cellWidth: number;
|
|
760
|
-
cellHeight: number;
|
|
761
|
-
active: boolean;
|
|
762
|
-
selected: boolean;
|
|
763
|
-
inSelectedRow: boolean;
|
|
764
|
-
editing: boolean;
|
|
765
|
-
isFillSource: boolean;
|
|
766
|
-
inFillRange: boolean;
|
|
767
|
-
rowBackgroundColor?: string;
|
|
768
|
-
renderRowCell: (props: GridRenderRowCellProps<TRow>) => React.ReactNode;
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
const FrozenCellWrapper = memo(function FrozenCellWrapper<TRow = unknown>(
|
|
772
|
-
props: FrozenCellWrapperProps<TRow>,
|
|
773
|
-
) {
|
|
774
|
-
const {
|
|
775
|
-
rowKey,
|
|
776
|
-
row,
|
|
777
|
-
rowIndex,
|
|
778
|
-
column,
|
|
779
|
-
cellWidth,
|
|
780
|
-
cellHeight,
|
|
781
|
-
active,
|
|
782
|
-
selected,
|
|
783
|
-
inSelectedRow,
|
|
784
|
-
editing,
|
|
785
|
-
isFillSource,
|
|
786
|
-
inFillRange,
|
|
787
|
-
rowBackgroundColor,
|
|
788
|
-
renderRowCell,
|
|
789
|
-
} = props;
|
|
790
|
-
|
|
791
|
-
return (
|
|
792
|
-
<Cell height={cellHeight} width={cellWidth}>
|
|
793
|
-
{renderRowCell({
|
|
794
|
-
rowKey,
|
|
795
|
-
row: row as TRow,
|
|
796
|
-
rowIndex,
|
|
797
|
-
column,
|
|
798
|
-
frozen: true,
|
|
799
|
-
inSelectedRow,
|
|
800
|
-
active,
|
|
801
|
-
selected,
|
|
802
|
-
editing,
|
|
803
|
-
isFillSource,
|
|
804
|
-
inFillRange,
|
|
805
|
-
rowBackgroundColor,
|
|
806
|
-
})}
|
|
807
|
-
</Cell>
|
|
808
|
-
);
|
|
809
|
-
}) as <TRow = unknown>(props: FrozenCellWrapperProps<TRow>) => React.ReactNode;
|
|
810
|
-
|
|
811
|
-
interface ScrollableCellWrapperProps<TRow = unknown> {
|
|
812
|
-
rowKey: RowPathKey;
|
|
813
|
-
row: TRow | undefined;
|
|
814
|
-
rowIndex: number;
|
|
815
|
-
column: number;
|
|
816
|
-
x: number;
|
|
817
|
-
cellWidth: number;
|
|
818
|
-
cellHeight: number;
|
|
819
|
-
active: boolean;
|
|
820
|
-
selected: boolean;
|
|
821
|
-
inSelectedRow: boolean;
|
|
822
|
-
editing: boolean;
|
|
823
|
-
isFillSource: boolean;
|
|
824
|
-
inFillRange: boolean;
|
|
825
|
-
rowBackgroundColor?: string;
|
|
826
|
-
renderRowCell: (props: GridRenderRowCellProps<TRow>) => React.ReactNode;
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
const ScrollableCellWrapper = memo(function ScrollableCellWrapper<TRow = unknown>(
|
|
830
|
-
props: ScrollableCellWrapperProps<TRow>,
|
|
831
|
-
) {
|
|
832
|
-
const {
|
|
833
|
-
rowKey,
|
|
834
|
-
row,
|
|
835
|
-
rowIndex,
|
|
836
|
-
column,
|
|
837
|
-
x,
|
|
838
|
-
cellWidth,
|
|
839
|
-
cellHeight,
|
|
840
|
-
active,
|
|
841
|
-
selected,
|
|
842
|
-
inSelectedRow,
|
|
843
|
-
editing,
|
|
844
|
-
isFillSource,
|
|
845
|
-
inFillRange,
|
|
846
|
-
rowBackgroundColor,
|
|
847
|
-
renderRowCell,
|
|
848
|
-
} = props;
|
|
849
|
-
|
|
850
|
-
return (
|
|
851
|
-
<ScrollableCell x={x} height={cellHeight} width={cellWidth}>
|
|
852
|
-
{renderRowCell({
|
|
853
|
-
rowKey,
|
|
854
|
-
row: row as TRow,
|
|
855
|
-
rowIndex,
|
|
856
|
-
column,
|
|
857
|
-
frozen: false,
|
|
858
|
-
inSelectedRow,
|
|
859
|
-
active,
|
|
860
|
-
selected,
|
|
861
|
-
editing,
|
|
862
|
-
isFillSource,
|
|
863
|
-
inFillRange,
|
|
864
|
-
rowBackgroundColor,
|
|
865
|
-
})}
|
|
866
|
-
</ScrollableCell>
|
|
867
|
-
);
|
|
868
|
-
}) as <TRow = unknown>(props: ScrollableCellWrapperProps<TRow>) => React.ReactNode;
|
|
869
|
-
|
|
870
|
-
interface GroupHeadingRowProps {
|
|
871
|
-
height: number;
|
|
872
|
-
width: number;
|
|
873
|
-
y: number;
|
|
874
|
-
groupKey: GroupPathKey;
|
|
875
|
-
collapsed: boolean;
|
|
876
|
-
renderGroupHeading: (props: GridRenderGroupHeadingProps) => React.ReactNode;
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
const GroupHeadingRow = memo(function GroupHeadingRow(props: GroupHeadingRowProps) {
|
|
880
|
-
const { width, height, y, groupKey, collapsed, renderGroupHeading } = props;
|
|
881
|
-
|
|
882
|
-
return (
|
|
883
|
-
<div
|
|
884
|
-
className={styles.group_heading_wrapper}
|
|
885
|
-
style={{ height, width, transform: `translateY(${y}px)` }}
|
|
886
|
-
>
|
|
887
|
-
<div className={styles.group_heading_content} style={{ height }}>
|
|
888
|
-
{renderGroupHeading({ groupKey, collapsed, children: null })}
|
|
889
|
-
</div>
|
|
890
|
-
</div>
|
|
891
|
-
);
|
|
892
|
-
}) as (props: GroupHeadingRowProps) => React.ReactNode;
|
|
893
|
-
|
|
894
|
-
interface GroupSummaryRowProps {
|
|
895
|
-
height: number;
|
|
896
|
-
width: number;
|
|
897
|
-
y: number;
|
|
898
|
-
groupKey: GroupPathKey;
|
|
899
|
-
collapsed: boolean;
|
|
900
|
-
frozenColumns: RecycledColumn[];
|
|
901
|
-
frozenColumnsWidth: number;
|
|
902
|
-
scrollableColumns: RecycledColumn[];
|
|
903
|
-
scrollableColumnsWidth: number;
|
|
904
|
-
renderGroupSummaryCell: (props: GridRenderGroupSummaryCellProps) => React.ReactNode;
|
|
905
|
-
renderGroupSummaryRow?: (props: GridRenderGroupSummaryRowProps) => React.ReactNode;
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
const GroupSummaryRow = memo(function GroupSummary(props: GroupSummaryRowProps) {
|
|
909
|
-
const {
|
|
910
|
-
width,
|
|
911
|
-
height,
|
|
912
|
-
y,
|
|
913
|
-
groupKey,
|
|
914
|
-
collapsed,
|
|
915
|
-
frozenColumns,
|
|
916
|
-
frozenColumnsWidth,
|
|
917
|
-
scrollableColumns,
|
|
918
|
-
scrollableColumnsWidth,
|
|
919
|
-
renderGroupSummaryCell,
|
|
920
|
-
renderGroupSummaryRow,
|
|
921
|
-
} = props;
|
|
922
|
-
|
|
923
|
-
const children = useMemo(
|
|
924
|
-
() => (
|
|
925
|
-
<>
|
|
926
|
-
<div className={styles.frozen_cells} style={{ width: frozenColumnsWidth, height }}>
|
|
927
|
-
{frozenColumns.map((columnData) => (
|
|
928
|
-
<Cell key={columnData.key} height={height} width={columnData.width}>
|
|
929
|
-
{renderGroupSummaryCell({
|
|
930
|
-
groupKey,
|
|
931
|
-
column: columnData.column,
|
|
932
|
-
width: columnData.width,
|
|
933
|
-
height,
|
|
934
|
-
})}
|
|
935
|
-
</Cell>
|
|
936
|
-
))}
|
|
937
|
-
</div>
|
|
938
|
-
<div className={styles.scrollable_cells} style={{ width: scrollableColumnsWidth, height }}>
|
|
939
|
-
{scrollableColumns.map((columnData) => (
|
|
940
|
-
<ScrollableCell
|
|
941
|
-
key={columnData.key}
|
|
942
|
-
x={columnData.x}
|
|
943
|
-
height={height}
|
|
944
|
-
width={columnData.width}
|
|
945
|
-
>
|
|
946
|
-
{renderGroupSummaryCell({
|
|
947
|
-
groupKey,
|
|
948
|
-
column: columnData.column,
|
|
949
|
-
width: columnData.width,
|
|
950
|
-
height,
|
|
951
|
-
})}
|
|
952
|
-
</ScrollableCell>
|
|
953
|
-
))}
|
|
954
|
-
</div>
|
|
955
|
-
</>
|
|
956
|
-
),
|
|
957
|
-
[
|
|
958
|
-
frozenColumns,
|
|
959
|
-
frozenColumnsWidth,
|
|
960
|
-
scrollableColumns,
|
|
961
|
-
scrollableColumnsWidth,
|
|
962
|
-
groupKey,
|
|
963
|
-
height,
|
|
964
|
-
renderGroupSummaryCell,
|
|
965
|
-
],
|
|
966
|
-
);
|
|
967
|
-
|
|
968
|
-
const rowContent = renderGroupSummaryRow ? (
|
|
969
|
-
renderGroupSummaryRow({ groupKey, collapsed, children })
|
|
970
|
-
) : (
|
|
971
|
-
<div
|
|
972
|
-
style={{
|
|
973
|
-
display: "flex",
|
|
974
|
-
alignItems: "center",
|
|
975
|
-
height: "100%",
|
|
976
|
-
}}
|
|
977
|
-
>
|
|
978
|
-
{children}
|
|
979
|
-
</div>
|
|
980
|
-
);
|
|
981
|
-
|
|
982
|
-
return (
|
|
983
|
-
<div className={styles.row_wrapper} style={{ height, width, transform: `translateY(${y}px)` }}>
|
|
984
|
-
{rowContent}
|
|
985
|
-
</div>
|
|
986
|
-
);
|
|
987
|
-
}) as (props: GroupSummaryRowProps) => React.ReactNode;
|
|
988
|
-
|
|
989
|
-
interface SpacerRowProps {
|
|
990
|
-
y: number;
|
|
991
|
-
height: number;
|
|
992
|
-
}
|
|
993
|
-
|
|
994
|
-
const SpacerRow = memo(function SpacerRow(props: SpacerRowProps) {
|
|
995
|
-
const { height, y } = props;
|
|
996
|
-
|
|
997
|
-
return <div className={styles.spacer} style={{ height, transform: `translateY(${y}px)` }} />;
|
|
998
|
-
}) as (props: SpacerRowProps) => React.ReactNode;
|
|
999
|
-
|
|
1000
|
-
interface CellProps {
|
|
1001
|
-
width: number;
|
|
1002
|
-
height: number;
|
|
1003
|
-
children: React.ReactNode;
|
|
1004
|
-
}
|
|
1005
|
-
|
|
1006
|
-
const Cell = memo(function Cell(props: CellProps) {
|
|
1007
|
-
const { width, height, children } = props;
|
|
1008
|
-
|
|
1009
|
-
return (
|
|
1010
|
-
<div className={styles.cell} style={{ width, height }}>
|
|
1011
|
-
{children}
|
|
1012
|
-
</div>
|
|
1013
|
-
);
|
|
1014
|
-
});
|
|
1015
|
-
|
|
1016
|
-
interface ScrollableCellProps {
|
|
1017
|
-
x: number;
|
|
1018
|
-
width: number;
|
|
1019
|
-
height: number;
|
|
1020
|
-
children: React.ReactNode;
|
|
1021
|
-
}
|
|
1022
|
-
|
|
1023
|
-
const ScrollableCell = memo(function ScrollableCell(props: ScrollableCellProps) {
|
|
1024
|
-
const { x, width, height, children } = props;
|
|
1025
|
-
|
|
1026
|
-
return (
|
|
1027
|
-
<div
|
|
1028
|
-
className={styles.cell}
|
|
1029
|
-
style={{
|
|
1030
|
-
position: "absolute",
|
|
1031
|
-
top: 0,
|
|
1032
|
-
left: 0,
|
|
1033
|
-
transform: `translateX(${x}px)`,
|
|
1034
|
-
width,
|
|
1035
|
-
height,
|
|
1036
|
-
overflow: "hidden",
|
|
1037
|
-
}}
|
|
1038
|
-
>
|
|
1039
|
-
{children}
|
|
1040
|
-
</div>
|
|
1041
|
-
);
|
|
1042
|
-
});
|