@rowsncolumns/grid 9.0.3 → 13.2.32
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/LICENSE.md +100 -0
- package/Readme.md +53 -59
- package/dist/cjs/Cell.d.ts +35 -0
- package/dist/cjs/Cell.d.ts.map +1 -0
- package/dist/cjs/Cell.js +29 -0
- package/dist/{CellOverlay.d.ts → cjs/CellOverlay.d.ts} +5 -5
- package/dist/cjs/CellOverlay.d.ts.map +1 -0
- package/dist/cjs/CellOverlay.js +126 -0
- package/dist/cjs/Context.d.ts +8 -0
- package/dist/cjs/Context.d.ts.map +1 -0
- package/dist/cjs/Context.js +13 -0
- package/dist/{FillHandle.d.ts → cjs/FillHandle.d.ts} +2 -1
- package/dist/cjs/FillHandle.d.ts.map +1 -0
- package/dist/cjs/FillHandle.js +26 -0
- package/dist/cjs/Grid.d.ts +558 -0
- package/dist/cjs/Grid.d.ts.map +1 -0
- package/dist/cjs/Grid.js +2446 -0
- package/dist/{GridLine.d.ts → cjs/GridLine.d.ts} +2 -1
- package/dist/cjs/GridLine.d.ts.map +1 -0
- package/dist/cjs/GridLine.js +10 -0
- package/dist/cjs/Image.d.ts +11 -0
- package/dist/cjs/Image.d.ts.map +1 -0
- package/dist/cjs/Image.js +57 -0
- package/dist/cjs/MemoizedGrid.d.ts +49 -0
- package/dist/cjs/MemoizedGrid.d.ts.map +1 -0
- package/dist/cjs/MemoizedGrid.js +105 -0
- package/dist/cjs/Selection.d.ts +5 -0
- package/dist/cjs/Selection.d.ts.map +1 -0
- package/dist/cjs/Selection.js +10 -0
- package/dist/{helpers.d.ts → cjs/helpers.d.ts} +59 -39
- package/dist/cjs/helpers.d.ts.map +1 -0
- package/dist/cjs/helpers.js +1409 -0
- package/dist/{hooks → cjs/hooks}/useCopyPaste.d.ts +3 -2
- package/dist/cjs/hooks/useCopyPaste.d.ts.map +1 -0
- package/dist/cjs/hooks/useCopyPaste.js +230 -0
- package/dist/{hooks → cjs/hooks}/useEditable.d.ts +21 -8
- package/dist/cjs/hooks/useEditable.d.ts.map +1 -0
- package/dist/cjs/hooks/useEditable.js +491 -0
- package/dist/{hooks → cjs/hooks}/useFilter.d.ts +3 -2
- package/dist/cjs/hooks/useFilter.d.ts.map +1 -0
- package/dist/cjs/hooks/useFilter.js +97 -0
- package/dist/{hooks → cjs/hooks}/useImage.d.ts +1 -0
- package/dist/cjs/hooks/useImage.d.ts.map +1 -0
- package/dist/cjs/hooks/useImage.js +80 -0
- package/dist/{hooks → cjs/hooks}/usePagination.d.ts +3 -2
- package/dist/cjs/hooks/usePagination.d.ts.map +1 -0
- package/dist/cjs/hooks/usePagination.js +53 -0
- package/dist/{hooks → cjs/hooks}/useSizer.d.ts +28 -29
- package/dist/cjs/hooks/useSizer.d.ts.map +1 -0
- package/dist/cjs/hooks/useSizer.js +176 -0
- package/dist/{hooks → cjs/hooks}/useTooltip.d.ts +2 -1
- package/dist/cjs/hooks/useTooltip.d.ts.map +1 -0
- package/dist/cjs/hooks/useTooltip.js +82 -0
- package/dist/{hooks → cjs/hooks}/useUndo.d.ts +2 -1
- package/dist/cjs/hooks/useUndo.d.ts.map +1 -0
- package/dist/cjs/hooks/useUndo.js +87 -0
- package/dist/cjs/index.d.ts +19 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +29 -0
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -0
- package/dist/{types.d.ts → cjs/types.d.ts} +8 -3
- package/dist/cjs/types.d.ts.map +1 -0
- package/dist/{types.js → cjs/types.js} +7 -8
- package/dist/cjs/utils.d.ts +9 -0
- package/dist/cjs/utils.d.ts.map +1 -0
- package/dist/cjs/utils.js +58 -0
- package/dist/cjs/vitest.config.d.ts +3 -0
- package/dist/cjs/vitest.config.d.ts.map +1 -0
- package/dist/cjs/vitest.config.js +24 -0
- package/dist/esm/Cell.d.ts +35 -0
- package/dist/esm/Cell.d.ts.map +1 -0
- package/dist/esm/Cell.js +24 -0
- package/dist/esm/CellOverlay.d.ts +21 -0
- package/dist/esm/CellOverlay.d.ts.map +1 -0
- package/dist/esm/CellOverlay.js +120 -0
- package/dist/esm/Context.d.ts +8 -0
- package/dist/esm/Context.d.ts.map +1 -0
- package/dist/esm/Context.js +9 -0
- package/dist/esm/FillHandle.d.ts +8 -0
- package/dist/esm/FillHandle.d.ts.map +1 -0
- package/dist/{FillHandle.js → esm/FillHandle.js} +5 -11
- package/dist/esm/Grid.d.ts +558 -0
- package/dist/esm/Grid.d.ts.map +1 -0
- package/dist/esm/Grid.js +2451 -0
- package/dist/esm/GridLine.d.ts +5 -0
- package/dist/esm/GridLine.d.ts.map +1 -0
- package/dist/esm/GridLine.js +7 -0
- package/dist/esm/Image.d.ts +11 -0
- package/dist/esm/Image.d.ts.map +1 -0
- package/dist/esm/Image.js +53 -0
- package/dist/esm/MemoizedGrid.d.ts +49 -0
- package/dist/esm/MemoizedGrid.d.ts.map +1 -0
- package/dist/esm/MemoizedGrid.js +98 -0
- package/dist/esm/Selection.d.ts +5 -0
- package/dist/esm/Selection.d.ts.map +1 -0
- package/dist/esm/Selection.js +7 -0
- package/dist/esm/helpers.d.ts +281 -0
- package/dist/esm/helpers.d.ts.map +1 -0
- package/dist/esm/helpers.js +1260 -0
- package/dist/esm/hooks/useCopyPaste.d.ts +53 -0
- package/dist/esm/hooks/useCopyPaste.d.ts.map +1 -0
- package/dist/{hooks → esm/hooks}/useCopyPaste.js +40 -46
- package/dist/esm/hooks/useEditable.d.ts +253 -0
- package/dist/esm/hooks/useEditable.d.ts.map +1 -0
- package/dist/esm/hooks/useEditable.js +482 -0
- package/dist/esm/hooks/useFilter.d.ts +69 -0
- package/dist/esm/hooks/useFilter.d.ts.map +1 -0
- package/dist/{hooks → esm/hooks}/useFilter.js +22 -42
- package/dist/esm/hooks/useImage.d.ts +13 -0
- package/dist/esm/hooks/useImage.d.ts.map +1 -0
- package/dist/esm/hooks/useImage.js +76 -0
- package/dist/esm/hooks/usePagination.d.ts +69 -0
- package/dist/esm/hooks/usePagination.d.ts.map +1 -0
- package/dist/esm/hooks/usePagination.js +47 -0
- package/dist/esm/hooks/useSizer.d.ts +114 -0
- package/dist/esm/hooks/useSizer.d.ts.map +1 -0
- package/dist/esm/hooks/useSizer.js +176 -0
- package/dist/esm/hooks/useTooltip.d.ts +43 -0
- package/dist/esm/hooks/useTooltip.d.ts.map +1 -0
- package/dist/esm/hooks/useTooltip.js +80 -0
- package/dist/esm/hooks/useUndo.d.ts +36 -0
- package/dist/esm/hooks/useUndo.d.ts.map +1 -0
- package/dist/{hooks → esm/hooks}/useUndo.js +16 -25
- package/dist/esm/index.d.ts +19 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +17 -0
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -0
- package/dist/esm/types.d.ts +70 -0
- package/dist/esm/types.d.ts.map +1 -0
- package/dist/esm/types.js +68 -0
- package/dist/esm/utils.d.ts +9 -0
- package/dist/esm/utils.d.ts.map +1 -0
- package/dist/esm/utils.js +104 -0
- package/dist/esm/vitest.config.d.ts +3 -0
- package/dist/esm/vitest.config.d.ts.map +1 -0
- package/dist/esm/vitest.config.js +22 -0
- package/package.json +28 -29
- package/dist/Cell.d.ts +0 -23
- package/dist/Cell.js +0 -55
- package/dist/Cell.js.map +0 -1
- package/dist/CellOverlay.js +0 -116
- package/dist/CellOverlay.js.map +0 -1
- package/dist/FillHandle.js.map +0 -1
- package/dist/Grid.d.ts +0 -385
- package/dist/Grid.js +0 -1684
- package/dist/Grid.js.map +0 -1
- package/dist/GridLine.js +0 -13
- package/dist/GridLine.js.map +0 -1
- package/dist/Image.d.ts +0 -8
- package/dist/Image.js +0 -50
- package/dist/Image.js.map +0 -1
- package/dist/Selection.d.ts +0 -4
- package/dist/Selection.js +0 -9
- package/dist/Selection.js.map +0 -1
- package/dist/helpers.js +0 -1033
- package/dist/helpers.js.map +0 -1
- package/dist/hooks/useCopyPaste.js.map +0 -1
- package/dist/hooks/useEditable.js +0 -477
- package/dist/hooks/useEditable.js.map +0 -1
- package/dist/hooks/useFilter.js.map +0 -1
- package/dist/hooks/useImage.js +0 -42
- package/dist/hooks/useImage.js.map +0 -1
- package/dist/hooks/usePagination.js +0 -73
- package/dist/hooks/usePagination.js.map +0 -1
- package/dist/hooks/useSelection.d.ts +0 -175
- package/dist/hooks/useSelection.js +0 -980
- package/dist/hooks/useSelection.js.map +0 -1
- package/dist/hooks/useSizer.js +0 -150
- package/dist/hooks/useSizer.js.map +0 -1
- package/dist/hooks/useTooltip.js +0 -106
- package/dist/hooks/useTooltip.js.map +0 -1
- package/dist/hooks/useTouch.d.ts +0 -21
- package/dist/hooks/useTouch.js +0 -73
- package/dist/hooks/useTouch.js.map +0 -1
- package/dist/hooks/useUndo.js.map +0 -1
- package/dist/index.d.ts +0 -25
- package/dist/index.js +0 -61
- package/dist/index.js.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/utils.d.ts +0 -8
- package/dist/utils.js +0 -153
- package/dist/utils.js.map +0 -1
- package/src/Cell.tsx +0 -100
- package/src/CellOverlay.tsx +0 -191
- package/src/FillHandle.tsx +0 -37
- package/src/Grid.tsx +0 -2677
- package/src/GridLine.tsx +0 -23
- package/src/Image.tsx +0 -43
- package/src/Selection.tsx +0 -9
- package/src/helpers.ts +0 -1455
- package/src/hooks/useCopyPaste.ts +0 -265
- package/src/hooks/useEditable.tsx +0 -945
- package/src/hooks/useFilter.tsx +0 -233
- package/src/hooks/useImage.ts +0 -57
- package/src/hooks/usePagination.tsx +0 -153
- package/src/hooks/useSelection.ts +0 -1437
- package/src/hooks/useSizer.ts +0 -312
- package/src/hooks/useTooltip.tsx +0 -172
- package/src/hooks/useTouch.ts +0 -116
- package/src/hooks/useUndo.ts +0 -134
- package/src/index.tsx +0 -46
- package/src/tests/Grid.spec.tsx +0 -47
- package/src/tests/helpers.test.ts +0 -292
- package/src/tests/usePagination.test.ts +0 -28
- package/src/tests/useSelection.test.ts +0 -27
- package/src/tests/useUndo.test.ts +0 -73
- package/src/types.ts +0 -69
- package/src/utils.tsx +0 -287
package/src/helpers.ts
DELETED
|
@@ -1,1455 +0,0 @@
|
|
|
1
|
-
// Utilities extracted from https://github.com/bvaughn/react-window
|
|
2
|
-
import {
|
|
3
|
-
ItemSizer,
|
|
4
|
-
InstanceInterface,
|
|
5
|
-
AreaProps,
|
|
6
|
-
CellInterface,
|
|
7
|
-
CellMetaData,
|
|
8
|
-
SelectionArea,
|
|
9
|
-
} from "./Grid";
|
|
10
|
-
import { Direction, KeyCodes } from "./types";
|
|
11
|
-
|
|
12
|
-
export enum Align {
|
|
13
|
-
start = "start",
|
|
14
|
-
end = "end",
|
|
15
|
-
center = "center",
|
|
16
|
-
auto = "auto",
|
|
17
|
-
smart = "smart",
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export enum ItemType {
|
|
21
|
-
row = "row",
|
|
22
|
-
column = "column",
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface IItemMetaData {
|
|
26
|
-
itemType: ItemType;
|
|
27
|
-
offset: number;
|
|
28
|
-
index: number;
|
|
29
|
-
rowCount: number;
|
|
30
|
-
columnCount: number;
|
|
31
|
-
rowHeight: ItemSizer;
|
|
32
|
-
columnWidth: ItemSizer;
|
|
33
|
-
instanceProps: InstanceInterface;
|
|
34
|
-
scale: number;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export const getRowStartIndexForOffset = ({
|
|
38
|
-
rowHeight,
|
|
39
|
-
columnWidth,
|
|
40
|
-
rowCount,
|
|
41
|
-
columnCount,
|
|
42
|
-
instanceProps,
|
|
43
|
-
offset,
|
|
44
|
-
scale,
|
|
45
|
-
}: Omit<IItemMetaData, "index" | "itemType">): number => {
|
|
46
|
-
return findNearestItem({
|
|
47
|
-
itemType: ItemType.row,
|
|
48
|
-
rowHeight,
|
|
49
|
-
columnWidth,
|
|
50
|
-
rowCount,
|
|
51
|
-
columnCount,
|
|
52
|
-
instanceProps,
|
|
53
|
-
offset,
|
|
54
|
-
scale,
|
|
55
|
-
});
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
interface IRowStopIndex
|
|
59
|
-
extends Omit<IItemMetaData, "itemType" | "index" | "offset" | "columnCount"> {
|
|
60
|
-
startIndex: number;
|
|
61
|
-
containerHeight: number;
|
|
62
|
-
scrollTop: number;
|
|
63
|
-
}
|
|
64
|
-
export const getRowStopIndexForStartIndex = ({
|
|
65
|
-
startIndex,
|
|
66
|
-
rowCount,
|
|
67
|
-
rowHeight,
|
|
68
|
-
columnWidth,
|
|
69
|
-
scrollTop,
|
|
70
|
-
containerHeight,
|
|
71
|
-
instanceProps,
|
|
72
|
-
scale,
|
|
73
|
-
}: IRowStopIndex): number => {
|
|
74
|
-
const itemMetadata = getItemMetadata({
|
|
75
|
-
itemType: ItemType.row,
|
|
76
|
-
rowHeight,
|
|
77
|
-
columnWidth,
|
|
78
|
-
index: startIndex,
|
|
79
|
-
instanceProps,
|
|
80
|
-
scale,
|
|
81
|
-
});
|
|
82
|
-
const maxOffset = scrollTop + containerHeight;
|
|
83
|
-
|
|
84
|
-
let offset = itemMetadata.offset + itemMetadata.size;
|
|
85
|
-
let stopIndex = startIndex;
|
|
86
|
-
|
|
87
|
-
while (stopIndex < rowCount - 1 && offset < maxOffset) {
|
|
88
|
-
stopIndex++;
|
|
89
|
-
offset += getItemMetadata({
|
|
90
|
-
itemType: ItemType.row,
|
|
91
|
-
rowHeight,
|
|
92
|
-
columnWidth,
|
|
93
|
-
index: stopIndex,
|
|
94
|
-
instanceProps,
|
|
95
|
-
scale,
|
|
96
|
-
}).size;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return stopIndex;
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
export const getColumnStartIndexForOffset = ({
|
|
103
|
-
rowHeight,
|
|
104
|
-
columnWidth,
|
|
105
|
-
rowCount,
|
|
106
|
-
columnCount,
|
|
107
|
-
instanceProps,
|
|
108
|
-
offset,
|
|
109
|
-
scale,
|
|
110
|
-
}: Omit<IItemMetaData, "index" | "itemType">): number => {
|
|
111
|
-
return findNearestItem({
|
|
112
|
-
itemType: ItemType.column,
|
|
113
|
-
rowHeight,
|
|
114
|
-
columnWidth,
|
|
115
|
-
rowCount,
|
|
116
|
-
columnCount,
|
|
117
|
-
instanceProps,
|
|
118
|
-
offset,
|
|
119
|
-
scale,
|
|
120
|
-
});
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
interface IColumnStopIndex
|
|
124
|
-
extends Omit<IItemMetaData, "itemType" | "index" | "offset" | "rowCount"> {
|
|
125
|
-
startIndex: number;
|
|
126
|
-
containerWidth: number;
|
|
127
|
-
scrollLeft: number;
|
|
128
|
-
}
|
|
129
|
-
export const getColumnStopIndexForStartIndex = ({
|
|
130
|
-
startIndex,
|
|
131
|
-
rowHeight,
|
|
132
|
-
columnWidth,
|
|
133
|
-
instanceProps,
|
|
134
|
-
containerWidth,
|
|
135
|
-
scrollLeft,
|
|
136
|
-
columnCount,
|
|
137
|
-
scale,
|
|
138
|
-
}: IColumnStopIndex): number => {
|
|
139
|
-
const itemMetadata = getItemMetadata({
|
|
140
|
-
itemType: ItemType.column,
|
|
141
|
-
index: startIndex,
|
|
142
|
-
rowHeight,
|
|
143
|
-
columnWidth,
|
|
144
|
-
instanceProps,
|
|
145
|
-
scale,
|
|
146
|
-
});
|
|
147
|
-
const maxOffset = scrollLeft + containerWidth;
|
|
148
|
-
|
|
149
|
-
let offset = itemMetadata.offset + itemMetadata.size;
|
|
150
|
-
let stopIndex = startIndex;
|
|
151
|
-
|
|
152
|
-
while (stopIndex < columnCount - 1 && offset < maxOffset) {
|
|
153
|
-
stopIndex++;
|
|
154
|
-
offset += getItemMetadata({
|
|
155
|
-
itemType: ItemType.column,
|
|
156
|
-
rowHeight,
|
|
157
|
-
columnWidth,
|
|
158
|
-
index: stopIndex,
|
|
159
|
-
instanceProps,
|
|
160
|
-
scale,
|
|
161
|
-
}).size;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
return stopIndex;
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
export const getBoundedCells = (area: AreaProps | null | undefined) => {
|
|
168
|
-
const cells = new Set();
|
|
169
|
-
if (!area) return cells;
|
|
170
|
-
const { top, bottom, left, right } = area;
|
|
171
|
-
for (let i = top; i <= bottom; i++) {
|
|
172
|
-
for (let j = left; j <= right; j++) {
|
|
173
|
-
cells.add(cellIdentifier(i, j));
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
return cells;
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
export const itemKey = ({ rowIndex, columnIndex }: CellInterface) =>
|
|
180
|
-
`${rowIndex}:${columnIndex}`;
|
|
181
|
-
|
|
182
|
-
export const getRowOffset = ({
|
|
183
|
-
index,
|
|
184
|
-
rowHeight,
|
|
185
|
-
columnWidth,
|
|
186
|
-
instanceProps,
|
|
187
|
-
scale,
|
|
188
|
-
}: Omit<IGetItemMetadata, "itemType">): number => {
|
|
189
|
-
return getItemMetadata({
|
|
190
|
-
itemType: ItemType.row,
|
|
191
|
-
index,
|
|
192
|
-
rowHeight,
|
|
193
|
-
columnWidth,
|
|
194
|
-
instanceProps,
|
|
195
|
-
scale,
|
|
196
|
-
}).offset;
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
export const getColumnOffset = ({
|
|
200
|
-
index,
|
|
201
|
-
rowHeight,
|
|
202
|
-
columnWidth,
|
|
203
|
-
instanceProps,
|
|
204
|
-
scale,
|
|
205
|
-
}: Omit<IGetItemMetadata, "itemType">): number => {
|
|
206
|
-
return getItemMetadata({
|
|
207
|
-
itemType: ItemType.column,
|
|
208
|
-
index,
|
|
209
|
-
rowHeight,
|
|
210
|
-
columnWidth,
|
|
211
|
-
instanceProps,
|
|
212
|
-
scale,
|
|
213
|
-
}).offset;
|
|
214
|
-
};
|
|
215
|
-
|
|
216
|
-
export const getRowHeight = (
|
|
217
|
-
index: number,
|
|
218
|
-
instanceProps: InstanceInterface
|
|
219
|
-
) => {
|
|
220
|
-
return instanceProps.rowMetadataMap[index].size;
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
export const getColumnWidth = (
|
|
224
|
-
index: number,
|
|
225
|
-
instanceProps: InstanceInterface
|
|
226
|
-
) => {
|
|
227
|
-
return instanceProps.columnMetadataMap[index].size;
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
interface IGetItemMetadata
|
|
231
|
-
extends Pick<
|
|
232
|
-
IItemMetaData,
|
|
233
|
-
| "itemType"
|
|
234
|
-
| "index"
|
|
235
|
-
| "rowHeight"
|
|
236
|
-
| "columnWidth"
|
|
237
|
-
| "instanceProps"
|
|
238
|
-
| "scale"
|
|
239
|
-
> {}
|
|
240
|
-
export const getItemMetadata = ({
|
|
241
|
-
itemType,
|
|
242
|
-
index,
|
|
243
|
-
rowHeight,
|
|
244
|
-
columnWidth,
|
|
245
|
-
instanceProps,
|
|
246
|
-
scale = 2,
|
|
247
|
-
}: IGetItemMetadata): CellMetaData => {
|
|
248
|
-
let itemMetadataMap, itemSize, lastMeasuredIndex, recalcIndices: number[];
|
|
249
|
-
if (itemType === "column") {
|
|
250
|
-
itemMetadataMap = instanceProps.columnMetadataMap;
|
|
251
|
-
itemSize = columnWidth;
|
|
252
|
-
lastMeasuredIndex = instanceProps.lastMeasuredColumnIndex;
|
|
253
|
-
recalcIndices = instanceProps.recalcColumnIndices;
|
|
254
|
-
} else {
|
|
255
|
-
itemMetadataMap = instanceProps.rowMetadataMap;
|
|
256
|
-
itemSize = rowHeight;
|
|
257
|
-
lastMeasuredIndex = instanceProps.lastMeasuredRowIndex;
|
|
258
|
-
recalcIndices = instanceProps.recalcRowIndices;
|
|
259
|
-
}
|
|
260
|
-
const recalcWithinBoundsOnly = recalcIndices.length > 0;
|
|
261
|
-
if (index > lastMeasuredIndex) {
|
|
262
|
-
let offset = 0;
|
|
263
|
-
if (lastMeasuredIndex >= 0) {
|
|
264
|
-
const itemMetadata = itemMetadataMap[lastMeasuredIndex];
|
|
265
|
-
offset = itemMetadata.offset + itemMetadata.size;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
for (let i = lastMeasuredIndex + 1; i <= index; i++) {
|
|
269
|
-
// Only recalculates specified columns
|
|
270
|
-
let size = recalcWithinBoundsOnly
|
|
271
|
-
? recalcIndices.includes(i)
|
|
272
|
-
? itemSize(i) * scale
|
|
273
|
-
: itemMetadataMap[i]?.size || itemSize(i) * scale
|
|
274
|
-
: itemSize(i) * scale;
|
|
275
|
-
|
|
276
|
-
itemMetadataMap[i] = {
|
|
277
|
-
offset,
|
|
278
|
-
size,
|
|
279
|
-
};
|
|
280
|
-
|
|
281
|
-
offset += size;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
if (itemType === "column") {
|
|
285
|
-
instanceProps.lastMeasuredColumnIndex = index;
|
|
286
|
-
} else {
|
|
287
|
-
instanceProps.lastMeasuredRowIndex = index;
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
return itemMetadataMap[index];
|
|
292
|
-
};
|
|
293
|
-
|
|
294
|
-
const findNearestItem = ({
|
|
295
|
-
itemType,
|
|
296
|
-
rowHeight,
|
|
297
|
-
columnWidth,
|
|
298
|
-
rowCount,
|
|
299
|
-
columnCount,
|
|
300
|
-
instanceProps,
|
|
301
|
-
offset,
|
|
302
|
-
scale,
|
|
303
|
-
}: Omit<IItemMetaData, "index">): number => {
|
|
304
|
-
let itemMetadataMap, lastMeasuredIndex;
|
|
305
|
-
if (itemType === "column") {
|
|
306
|
-
itemMetadataMap = instanceProps.columnMetadataMap;
|
|
307
|
-
lastMeasuredIndex = instanceProps.lastMeasuredColumnIndex;
|
|
308
|
-
} else {
|
|
309
|
-
itemMetadataMap = instanceProps.rowMetadataMap;
|
|
310
|
-
lastMeasuredIndex = instanceProps.lastMeasuredRowIndex;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
const lastMeasuredItemOffset =
|
|
314
|
-
lastMeasuredIndex > 0 ? itemMetadataMap[lastMeasuredIndex].offset : 0;
|
|
315
|
-
if (lastMeasuredItemOffset >= offset) {
|
|
316
|
-
// If we've already measured items within this range just use a binary search as it's faster.
|
|
317
|
-
return findNearestItemBinarySearch({
|
|
318
|
-
itemType,
|
|
319
|
-
rowHeight,
|
|
320
|
-
columnWidth,
|
|
321
|
-
instanceProps,
|
|
322
|
-
high: lastMeasuredIndex,
|
|
323
|
-
low: 0,
|
|
324
|
-
offset,
|
|
325
|
-
scale,
|
|
326
|
-
});
|
|
327
|
-
} else {
|
|
328
|
-
// If we haven't yet measured this high, fallback to an exponential search with an inner binary search.
|
|
329
|
-
// The exponential search avoids pre-computing sizes for the full set of items as a binary search would.
|
|
330
|
-
// The overall complexity for this approach is O(log n).
|
|
331
|
-
return findNearestItemExponentialSearch({
|
|
332
|
-
itemType,
|
|
333
|
-
rowHeight,
|
|
334
|
-
rowCount,
|
|
335
|
-
columnCount,
|
|
336
|
-
columnWidth,
|
|
337
|
-
instanceProps,
|
|
338
|
-
index: Math.max(0, lastMeasuredIndex),
|
|
339
|
-
offset,
|
|
340
|
-
scale,
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
};
|
|
344
|
-
|
|
345
|
-
interface IBinarySearchArgs
|
|
346
|
-
extends Omit<IItemMetaData, "index" | "rowCount" | "columnCount"> {
|
|
347
|
-
high: number;
|
|
348
|
-
low: number;
|
|
349
|
-
}
|
|
350
|
-
const findNearestItemBinarySearch = ({
|
|
351
|
-
itemType,
|
|
352
|
-
rowHeight,
|
|
353
|
-
columnWidth,
|
|
354
|
-
instanceProps,
|
|
355
|
-
high,
|
|
356
|
-
low,
|
|
357
|
-
offset,
|
|
358
|
-
scale,
|
|
359
|
-
}: IBinarySearchArgs): number => {
|
|
360
|
-
while (low <= high) {
|
|
361
|
-
const middle = low + Math.floor((high - low) / 2);
|
|
362
|
-
const currentOffset = getItemMetadata({
|
|
363
|
-
itemType,
|
|
364
|
-
rowHeight,
|
|
365
|
-
columnWidth,
|
|
366
|
-
index: middle,
|
|
367
|
-
instanceProps,
|
|
368
|
-
scale,
|
|
369
|
-
}).offset;
|
|
370
|
-
|
|
371
|
-
if (currentOffset === offset) {
|
|
372
|
-
return middle;
|
|
373
|
-
} else if (currentOffset < offset) {
|
|
374
|
-
low = middle + 1;
|
|
375
|
-
} else if (currentOffset > offset) {
|
|
376
|
-
high = middle - 1;
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
if (low > 0) {
|
|
381
|
-
return low - 1;
|
|
382
|
-
} else {
|
|
383
|
-
return 0;
|
|
384
|
-
}
|
|
385
|
-
};
|
|
386
|
-
|
|
387
|
-
const findNearestItemExponentialSearch = ({
|
|
388
|
-
itemType,
|
|
389
|
-
rowHeight,
|
|
390
|
-
columnWidth,
|
|
391
|
-
rowCount,
|
|
392
|
-
columnCount,
|
|
393
|
-
instanceProps,
|
|
394
|
-
index,
|
|
395
|
-
offset,
|
|
396
|
-
scale,
|
|
397
|
-
}: IItemMetaData) => {
|
|
398
|
-
const itemCount = itemType === "column" ? columnCount : rowCount;
|
|
399
|
-
let interval = 1;
|
|
400
|
-
|
|
401
|
-
while (
|
|
402
|
-
index < itemCount &&
|
|
403
|
-
getItemMetadata({
|
|
404
|
-
itemType,
|
|
405
|
-
rowHeight,
|
|
406
|
-
columnWidth,
|
|
407
|
-
index,
|
|
408
|
-
instanceProps,
|
|
409
|
-
scale,
|
|
410
|
-
}).offset < offset
|
|
411
|
-
) {
|
|
412
|
-
index += interval;
|
|
413
|
-
interval *= 2;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
return findNearestItemBinarySearch({
|
|
417
|
-
itemType,
|
|
418
|
-
rowHeight,
|
|
419
|
-
columnWidth,
|
|
420
|
-
instanceProps,
|
|
421
|
-
high: Math.min(index, itemCount - 1),
|
|
422
|
-
low: Math.floor(index / 2),
|
|
423
|
-
offset,
|
|
424
|
-
scale,
|
|
425
|
-
});
|
|
426
|
-
};
|
|
427
|
-
|
|
428
|
-
export const getEstimatedTotalHeight = (
|
|
429
|
-
rowCount: number,
|
|
430
|
-
instanceProps: InstanceInterface
|
|
431
|
-
) => {
|
|
432
|
-
const { estimatedRowHeight } = instanceProps;
|
|
433
|
-
let totalSizeOfMeasuredRows = 0;
|
|
434
|
-
let { lastMeasuredRowIndex, rowMetadataMap } = instanceProps;
|
|
435
|
-
|
|
436
|
-
// Edge case check for when the number of items decreases while a scroll is in progress.
|
|
437
|
-
// https://github.com/bvaughn/react-window/pull/138
|
|
438
|
-
if (lastMeasuredRowIndex >= rowCount) {
|
|
439
|
-
lastMeasuredRowIndex = rowCount - 1;
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
if (lastMeasuredRowIndex >= 0) {
|
|
443
|
-
const itemMetadata = rowMetadataMap[lastMeasuredRowIndex];
|
|
444
|
-
totalSizeOfMeasuredRows = itemMetadata.offset + itemMetadata.size;
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
const numUnmeasuredItems = rowCount - lastMeasuredRowIndex - 1;
|
|
448
|
-
const totalSizeOfUnmeasuredItems = numUnmeasuredItems * estimatedRowHeight;
|
|
449
|
-
|
|
450
|
-
return totalSizeOfMeasuredRows + totalSizeOfUnmeasuredItems;
|
|
451
|
-
};
|
|
452
|
-
|
|
453
|
-
export const getEstimatedTotalWidth = (
|
|
454
|
-
columnCount: number,
|
|
455
|
-
instanceProps: InstanceInterface
|
|
456
|
-
) => {
|
|
457
|
-
const { estimatedColumnWidth } = instanceProps;
|
|
458
|
-
let totalSizeOfMeasuredRows = 0;
|
|
459
|
-
let { lastMeasuredColumnIndex, columnMetadataMap } = instanceProps;
|
|
460
|
-
// Edge case check for when the number of items decreases while a scroll is in progress.
|
|
461
|
-
// https://github.com/bvaughn/react-window/pull/138
|
|
462
|
-
if (lastMeasuredColumnIndex >= columnCount) {
|
|
463
|
-
lastMeasuredColumnIndex = columnCount - 1;
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
if (lastMeasuredColumnIndex >= 0) {
|
|
467
|
-
const itemMetadata = columnMetadataMap[lastMeasuredColumnIndex];
|
|
468
|
-
totalSizeOfMeasuredRows = itemMetadata.offset + itemMetadata.size;
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
const numUnmeasuredItems = columnCount - lastMeasuredColumnIndex - 1;
|
|
472
|
-
const totalSizeOfUnmeasuredItems = numUnmeasuredItems * estimatedColumnWidth;
|
|
473
|
-
|
|
474
|
-
return totalSizeOfMeasuredRows + totalSizeOfUnmeasuredItems;
|
|
475
|
-
};
|
|
476
|
-
|
|
477
|
-
/* Create a stringified cell identifier */
|
|
478
|
-
export const cellIdentifier = (rowIndex: number, columnIndex: number): string =>
|
|
479
|
-
`${rowIndex},${columnIndex}`;
|
|
480
|
-
|
|
481
|
-
/**
|
|
482
|
-
* @desc Throttle fn
|
|
483
|
-
* @param func function
|
|
484
|
-
* @param limit Delay in milliseconds
|
|
485
|
-
*/
|
|
486
|
-
export function throttle<T extends Function>(func: T, limit: number) {
|
|
487
|
-
var lastEventTimestamp: number | null = null;
|
|
488
|
-
let callable = (...args: any) => {
|
|
489
|
-
const now = Date.now();
|
|
490
|
-
if (!lastEventTimestamp || now - lastEventTimestamp >= limit) {
|
|
491
|
-
lastEventTimestamp = now;
|
|
492
|
-
func(...args);
|
|
493
|
-
}
|
|
494
|
-
};
|
|
495
|
-
return <T>(<any>callable);
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
export function debounce<T extends Function>(cb: T, wait = 300) {
|
|
499
|
-
let h = 0;
|
|
500
|
-
let callable = (...args: any) => {
|
|
501
|
-
clearTimeout(h);
|
|
502
|
-
h = window.setTimeout(() => cb(...args), wait);
|
|
503
|
-
};
|
|
504
|
-
return <T>(<any>callable);
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
export function rafThrottle(callback: Function) {
|
|
508
|
-
var active = false; // a simple flag
|
|
509
|
-
var evt: any; // to keep track of the last event
|
|
510
|
-
var handler = function () {
|
|
511
|
-
// fired only when screen has refreshed
|
|
512
|
-
active = false; // release our flag
|
|
513
|
-
callback(evt);
|
|
514
|
-
};
|
|
515
|
-
return function handleEvent(e: any) {
|
|
516
|
-
// the actual event handler
|
|
517
|
-
evt = e; // save our event at each call
|
|
518
|
-
evt && evt.persist && evt.persist();
|
|
519
|
-
if (!active) {
|
|
520
|
-
// only if we weren't already doing it
|
|
521
|
-
active = true; // raise the flag
|
|
522
|
-
requestAnimationFrame(handler); // wait for next screen refresh
|
|
523
|
-
}
|
|
524
|
-
};
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
export interface AlignmentProps extends Omit<IItemMetaData, "offset"> {
|
|
528
|
-
containerHeight: number;
|
|
529
|
-
containerWidth: number;
|
|
530
|
-
align?: Align;
|
|
531
|
-
scrollOffset: number;
|
|
532
|
-
scrollbarSize: number;
|
|
533
|
-
frozenOffset: number;
|
|
534
|
-
estimatedTotalHeight: number;
|
|
535
|
-
estimatedTotalWidth: number;
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
export const getOffsetForIndexAndAlignment = ({
|
|
539
|
-
itemType,
|
|
540
|
-
containerHeight,
|
|
541
|
-
containerWidth,
|
|
542
|
-
rowHeight,
|
|
543
|
-
columnWidth,
|
|
544
|
-
columnCount,
|
|
545
|
-
rowCount,
|
|
546
|
-
index,
|
|
547
|
-
align = Align.smart,
|
|
548
|
-
scrollOffset,
|
|
549
|
-
instanceProps,
|
|
550
|
-
scrollbarSize,
|
|
551
|
-
frozenOffset = 0,
|
|
552
|
-
scale,
|
|
553
|
-
estimatedTotalHeight,
|
|
554
|
-
estimatedTotalWidth,
|
|
555
|
-
}: AlignmentProps): number => {
|
|
556
|
-
const size = itemType === "column" ? containerWidth : containerHeight;
|
|
557
|
-
const itemMetadata = getItemMetadata({
|
|
558
|
-
itemType,
|
|
559
|
-
rowHeight,
|
|
560
|
-
columnWidth,
|
|
561
|
-
index,
|
|
562
|
-
instanceProps,
|
|
563
|
-
scale,
|
|
564
|
-
});
|
|
565
|
-
|
|
566
|
-
// Get estimated total size after ItemMetadata is computed,
|
|
567
|
-
// To ensure it reflects actual measurements instead of just estimates.
|
|
568
|
-
const estimatedTotalSize =
|
|
569
|
-
itemType === "column" ? estimatedTotalWidth : estimatedTotalHeight;
|
|
570
|
-
|
|
571
|
-
const maxOffset = Math.max(
|
|
572
|
-
0,
|
|
573
|
-
Math.min(estimatedTotalSize - size, itemMetadata.offset - frozenOffset)
|
|
574
|
-
);
|
|
575
|
-
const minOffset = Math.max(
|
|
576
|
-
0,
|
|
577
|
-
itemMetadata.offset - size + scrollbarSize + itemMetadata.size
|
|
578
|
-
);
|
|
579
|
-
|
|
580
|
-
if (align === Align.smart) {
|
|
581
|
-
if (scrollOffset >= minOffset - size && scrollOffset <= maxOffset + size) {
|
|
582
|
-
align = Align.auto;
|
|
583
|
-
} else {
|
|
584
|
-
align = Align.center;
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
switch (align) {
|
|
589
|
-
case Align.start:
|
|
590
|
-
return maxOffset;
|
|
591
|
-
case Align.end:
|
|
592
|
-
return minOffset;
|
|
593
|
-
case Align.center:
|
|
594
|
-
return Math.round(minOffset + (maxOffset - minOffset) / 2);
|
|
595
|
-
case Align.auto:
|
|
596
|
-
default:
|
|
597
|
-
if (scrollOffset >= minOffset && scrollOffset <= maxOffset) {
|
|
598
|
-
return scrollOffset;
|
|
599
|
-
} else if (minOffset > maxOffset) {
|
|
600
|
-
// Because we only take into account the scrollbar size when calculating minOffset
|
|
601
|
-
// this value can be larger than maxOffset when at the end of the list
|
|
602
|
-
return minOffset;
|
|
603
|
-
} else if (scrollOffset < minOffset) {
|
|
604
|
-
return minOffset;
|
|
605
|
-
} else {
|
|
606
|
-
return maxOffset;
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
};
|
|
610
|
-
|
|
611
|
-
export const getOffsetForColumnAndAlignment = (
|
|
612
|
-
props: Omit<AlignmentProps, "itemType">
|
|
613
|
-
) => {
|
|
614
|
-
return getOffsetForIndexAndAlignment({
|
|
615
|
-
itemType: ItemType.column,
|
|
616
|
-
...props,
|
|
617
|
-
});
|
|
618
|
-
};
|
|
619
|
-
|
|
620
|
-
export const getOffsetForRowAndAlignment = (
|
|
621
|
-
props: Omit<AlignmentProps, "itemType">
|
|
622
|
-
) => {
|
|
623
|
-
return getOffsetForIndexAndAlignment({
|
|
624
|
-
itemType: ItemType.row,
|
|
625
|
-
...props,
|
|
626
|
-
});
|
|
627
|
-
};
|
|
628
|
-
|
|
629
|
-
// Animation frame based implementation of setTimeout.
|
|
630
|
-
// Inspired by Joe Lambert, https://gist.github.com/joelambert/1002116#file-requesttimeout-js
|
|
631
|
-
|
|
632
|
-
const hasNativePerformanceNow =
|
|
633
|
-
typeof performance === "object" && typeof performance.now === "function";
|
|
634
|
-
|
|
635
|
-
const now = hasNativePerformanceNow
|
|
636
|
-
? () => performance.now()
|
|
637
|
-
: () => Date.now();
|
|
638
|
-
|
|
639
|
-
export type TimeoutID = {
|
|
640
|
-
id: number;
|
|
641
|
-
};
|
|
642
|
-
|
|
643
|
-
export function cancelTimeout(timeoutID: TimeoutID) {
|
|
644
|
-
cancelAnimationFrame(timeoutID.id);
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
/**
|
|
648
|
-
* Create a throttler based on RAF
|
|
649
|
-
* @param callback
|
|
650
|
-
* @param delay
|
|
651
|
-
*/
|
|
652
|
-
export function requestTimeout(callback: Function, delay: number): TimeoutID {
|
|
653
|
-
const start = now();
|
|
654
|
-
|
|
655
|
-
function tick() {
|
|
656
|
-
if (now() - start >= delay) {
|
|
657
|
-
callback.call(null);
|
|
658
|
-
} else {
|
|
659
|
-
timeoutID.id = requestAnimationFrame(tick);
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
const timeoutID: TimeoutID = {
|
|
664
|
-
id: requestAnimationFrame(tick),
|
|
665
|
-
};
|
|
666
|
-
|
|
667
|
-
return timeoutID;
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
export const selectionFromActiveCell = (
|
|
671
|
-
activeCell: CellInterface | null
|
|
672
|
-
): SelectionArea[] => {
|
|
673
|
-
if (!activeCell) return [];
|
|
674
|
-
return [
|
|
675
|
-
{
|
|
676
|
-
bounds: {
|
|
677
|
-
top: activeCell.rowIndex,
|
|
678
|
-
left: activeCell.columnIndex,
|
|
679
|
-
bottom: activeCell.rowIndex,
|
|
680
|
-
right: activeCell.columnIndex,
|
|
681
|
-
},
|
|
682
|
-
},
|
|
683
|
-
];
|
|
684
|
-
};
|
|
685
|
-
|
|
686
|
-
/**
|
|
687
|
-
* Check if a selection are spans multiple cells
|
|
688
|
-
* @param sel
|
|
689
|
-
*/
|
|
690
|
-
export const selectionSpansCells = (sel: AreaProps | undefined) => {
|
|
691
|
-
if (!sel) return false;
|
|
692
|
-
return sel.bottom !== sel.top || sel.left !== sel.right;
|
|
693
|
-
};
|
|
694
|
-
|
|
695
|
-
/**
|
|
696
|
-
* When user tries to drag a selection
|
|
697
|
-
* @param initialSelection
|
|
698
|
-
* @param from
|
|
699
|
-
* @param to
|
|
700
|
-
*/
|
|
701
|
-
export const newSelectionFromDrag = (
|
|
702
|
-
initialSelection: SelectionArea,
|
|
703
|
-
from: CellInterface,
|
|
704
|
-
to: CellInterface,
|
|
705
|
-
topBound: number = 0,
|
|
706
|
-
leftBound: number = 0,
|
|
707
|
-
rowCount: number,
|
|
708
|
-
columnCount: number
|
|
709
|
-
) => {
|
|
710
|
-
const currentBounds = initialSelection.bounds;
|
|
711
|
-
const top = Math.max(
|
|
712
|
-
topBound,
|
|
713
|
-
Math.min(rowCount, to.rowIndex + currentBounds.top - from.rowIndex)
|
|
714
|
-
);
|
|
715
|
-
const left = Math.max(
|
|
716
|
-
leftBound,
|
|
717
|
-
Math.min(
|
|
718
|
-
columnCount,
|
|
719
|
-
to.columnIndex + currentBounds.left - from.columnIndex
|
|
720
|
-
)
|
|
721
|
-
);
|
|
722
|
-
return {
|
|
723
|
-
bounds: {
|
|
724
|
-
top,
|
|
725
|
-
left,
|
|
726
|
-
bottom: top + (currentBounds.bottom - currentBounds.top),
|
|
727
|
-
right: left + (currentBounds.right - currentBounds.left),
|
|
728
|
-
},
|
|
729
|
-
};
|
|
730
|
-
};
|
|
731
|
-
|
|
732
|
-
/**
|
|
733
|
-
* Clamp cell coordinates to be inside activeCell and selection
|
|
734
|
-
* @param coords
|
|
735
|
-
* @param activeCell
|
|
736
|
-
* @param selection
|
|
737
|
-
*/
|
|
738
|
-
export const clampCellCoords = (
|
|
739
|
-
coords: CellInterface,
|
|
740
|
-
activeCell: CellInterface | undefined,
|
|
741
|
-
selection: SelectionArea | undefined
|
|
742
|
-
) => {
|
|
743
|
-
if (activeCell) {
|
|
744
|
-
coords.rowIndex = Math.max(activeCell.rowIndex, coords.rowIndex);
|
|
745
|
-
coords.columnIndex = Math.min(activeCell.columnIndex, coords.columnIndex);
|
|
746
|
-
}
|
|
747
|
-
if (selection) {
|
|
748
|
-
coords.rowIndex = Math.min(
|
|
749
|
-
selection.bounds.bottom,
|
|
750
|
-
Math.max(selection.bounds.top, coords.rowIndex)
|
|
751
|
-
);
|
|
752
|
-
coords.columnIndex = Math.min(
|
|
753
|
-
selection.bounds.right,
|
|
754
|
-
Math.max(selection.bounds.left, coords.columnIndex)
|
|
755
|
-
);
|
|
756
|
-
}
|
|
757
|
-
return coords;
|
|
758
|
-
};
|
|
759
|
-
|
|
760
|
-
/**
|
|
761
|
-
* Converts a number to alphabet
|
|
762
|
-
* @param i
|
|
763
|
-
*/
|
|
764
|
-
export const numberToAlphabet = (i: number): string => {
|
|
765
|
-
return (
|
|
766
|
-
(i >= 26 ? numberToAlphabet(((i / 26) >> 0) - 1) : "") +
|
|
767
|
-
"abcdefghijklmnopqrstuvwxyz"[i % 26 >> 0]
|
|
768
|
-
).toUpperCase();
|
|
769
|
-
};
|
|
770
|
-
|
|
771
|
-
/**
|
|
772
|
-
* Convert selections to html and csv data
|
|
773
|
-
* @param rows
|
|
774
|
-
*/
|
|
775
|
-
export const prepareClipboardData = (
|
|
776
|
-
rows: (string | undefined)[][]
|
|
777
|
-
): [string, string] => {
|
|
778
|
-
const html = ["<table>"];
|
|
779
|
-
const csv: string[] = [];
|
|
780
|
-
const sanitizeCell = (value: any) => {
|
|
781
|
-
if (isNull(value)) return "";
|
|
782
|
-
return value;
|
|
783
|
-
};
|
|
784
|
-
rows.forEach((row) => {
|
|
785
|
-
html.push("<tr>");
|
|
786
|
-
const csvRow: string[] = [];
|
|
787
|
-
row.forEach((cell) => {
|
|
788
|
-
html.push(`<td>${sanitizeCell(cell)}</td>`);
|
|
789
|
-
csvRow.push(`${castToString(cell)?.replace(/"/g, '""')}`);
|
|
790
|
-
});
|
|
791
|
-
csv.push(csvRow.join(","));
|
|
792
|
-
html.push("</tr>");
|
|
793
|
-
});
|
|
794
|
-
html.push("</table>");
|
|
795
|
-
return [html.join(""), csv.join("\n")];
|
|
796
|
-
};
|
|
797
|
-
|
|
798
|
-
/**
|
|
799
|
-
* Cycles active cell within selecton bounds
|
|
800
|
-
* @param activeCellBounds
|
|
801
|
-
* @param selectionBounds
|
|
802
|
-
* @param direction
|
|
803
|
-
*/
|
|
804
|
-
export const findNextCellWithinBounds = (
|
|
805
|
-
activeCellBounds: AreaProps,
|
|
806
|
-
selectionBounds: AreaProps,
|
|
807
|
-
direction: Direction = Direction.Right
|
|
808
|
-
): CellInterface | null => {
|
|
809
|
-
const intersects = areaIntersects(activeCellBounds, selectionBounds);
|
|
810
|
-
if (!intersects) return null;
|
|
811
|
-
let rowIndex, columnIndex;
|
|
812
|
-
let nextActiveCell: CellInterface | null = null;
|
|
813
|
-
if (direction === Direction.Right) {
|
|
814
|
-
rowIndex = activeCellBounds.top;
|
|
815
|
-
columnIndex = activeCellBounds.left + 1;
|
|
816
|
-
if (columnIndex > selectionBounds.right) {
|
|
817
|
-
rowIndex = rowIndex + 1;
|
|
818
|
-
columnIndex = selectionBounds.left;
|
|
819
|
-
if (rowIndex > selectionBounds.bottom) {
|
|
820
|
-
rowIndex = selectionBounds.top;
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
nextActiveCell = { rowIndex, columnIndex };
|
|
824
|
-
}
|
|
825
|
-
if (direction === Direction.Left) {
|
|
826
|
-
rowIndex = activeCellBounds.bottom;
|
|
827
|
-
columnIndex = activeCellBounds.left - 1;
|
|
828
|
-
if (columnIndex < selectionBounds.left) {
|
|
829
|
-
rowIndex = rowIndex - 1;
|
|
830
|
-
columnIndex = selectionBounds.right;
|
|
831
|
-
if (rowIndex < selectionBounds.top) {
|
|
832
|
-
rowIndex = selectionBounds.bottom;
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
|
-
nextActiveCell = { rowIndex, columnIndex };
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
if (direction === Direction.Down) {
|
|
839
|
-
rowIndex = activeCellBounds.bottom + 1;
|
|
840
|
-
columnIndex = activeCellBounds.left;
|
|
841
|
-
if (rowIndex > selectionBounds.bottom) {
|
|
842
|
-
columnIndex = activeCellBounds.left + 1;
|
|
843
|
-
rowIndex = selectionBounds.top;
|
|
844
|
-
if (columnIndex > selectionBounds.right) {
|
|
845
|
-
columnIndex = selectionBounds.left;
|
|
846
|
-
}
|
|
847
|
-
}
|
|
848
|
-
nextActiveCell = { rowIndex, columnIndex };
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
if (direction === Direction.Up) {
|
|
852
|
-
rowIndex = activeCellBounds.top - 1;
|
|
853
|
-
columnIndex = activeCellBounds.left;
|
|
854
|
-
if (rowIndex < selectionBounds.top) {
|
|
855
|
-
columnIndex = activeCellBounds.left - 1;
|
|
856
|
-
rowIndex = selectionBounds.bottom;
|
|
857
|
-
if (columnIndex < selectionBounds.left) {
|
|
858
|
-
columnIndex = selectionBounds.right;
|
|
859
|
-
}
|
|
860
|
-
}
|
|
861
|
-
nextActiveCell = { rowIndex, columnIndex };
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
return nextActiveCell;
|
|
865
|
-
};
|
|
866
|
-
|
|
867
|
-
/**
|
|
868
|
-
* Check if 2 areas overlap
|
|
869
|
-
* @param area1
|
|
870
|
-
* @param area2
|
|
871
|
-
*/
|
|
872
|
-
export const areaIntersects = (area1: AreaProps, area2: AreaProps): boolean => {
|
|
873
|
-
if (area1.left > area2.right || area2.left > area1.right) {
|
|
874
|
-
return false;
|
|
875
|
-
}
|
|
876
|
-
if (area1.top > area2.bottom || area2.top > area1.bottom) {
|
|
877
|
-
return false;
|
|
878
|
-
}
|
|
879
|
-
return true;
|
|
880
|
-
};
|
|
881
|
-
|
|
882
|
-
/**
|
|
883
|
-
* Check if area is inside another area
|
|
884
|
-
* @param needle
|
|
885
|
-
* @param haystack
|
|
886
|
-
*/
|
|
887
|
-
export const areaInsideArea = (needle: AreaProps, haystack: AreaProps) => {
|
|
888
|
-
return (
|
|
889
|
-
needle.top >= haystack.top &&
|
|
890
|
-
needle.bottom <= haystack.bottom &&
|
|
891
|
-
needle.left >= haystack.left &&
|
|
892
|
-
needle.right <= haystack.right
|
|
893
|
-
);
|
|
894
|
-
};
|
|
895
|
-
|
|
896
|
-
/**
|
|
897
|
-
* Check if two areas are equal
|
|
898
|
-
* @param area1
|
|
899
|
-
* @param area2
|
|
900
|
-
*/
|
|
901
|
-
export const isAreasEqual = (
|
|
902
|
-
area1: AreaProps | undefined,
|
|
903
|
-
area2: AreaProps | undefined
|
|
904
|
-
) => {
|
|
905
|
-
if (area1 === void 0 || area2 === void 0) {
|
|
906
|
-
return false;
|
|
907
|
-
}
|
|
908
|
-
return (
|
|
909
|
-
area1.bottom === area2.bottom &&
|
|
910
|
-
area1.top === area2.top &&
|
|
911
|
-
area1.left === area2.left &&
|
|
912
|
-
area1.right === area2.right
|
|
913
|
-
);
|
|
914
|
-
};
|
|
915
|
-
|
|
916
|
-
/**
|
|
917
|
-
* Get maximum bound of an area, caters to merged cells
|
|
918
|
-
* @param area
|
|
919
|
-
* @param boundGetter
|
|
920
|
-
*/
|
|
921
|
-
export const extendAreaToMergedCells = (
|
|
922
|
-
_area: AreaProps,
|
|
923
|
-
mergedCells: AreaProps[]
|
|
924
|
-
): AreaProps => {
|
|
925
|
-
const area = { ..._area };
|
|
926
|
-
for (const bounds of mergedCells) {
|
|
927
|
-
if (areaIntersects(area, bounds)) {
|
|
928
|
-
area.top = Math.min(area.top, bounds.top);
|
|
929
|
-
area.bottom = Math.max(area.bottom, bounds.bottom);
|
|
930
|
-
area.right = Math.max(area.right, bounds.right);
|
|
931
|
-
area.left = Math.min(area.left, bounds.left);
|
|
932
|
-
}
|
|
933
|
-
}
|
|
934
|
-
return area;
|
|
935
|
-
};
|
|
936
|
-
|
|
937
|
-
/**
|
|
938
|
-
* Convert 2 cells to bounds
|
|
939
|
-
* @param start
|
|
940
|
-
* @param end
|
|
941
|
-
* @returns
|
|
942
|
-
*
|
|
943
|
-
* 2 loops O(n)
|
|
944
|
-
*/
|
|
945
|
-
export const cellRangeToBounds = (
|
|
946
|
-
start: CellInterface,
|
|
947
|
-
end: CellInterface,
|
|
948
|
-
spanMerges: boolean = true,
|
|
949
|
-
getCellBounds: (cell: CellInterface) => AreaProps
|
|
950
|
-
) => {
|
|
951
|
-
let top = Math.min(start.rowIndex, end.rowIndex);
|
|
952
|
-
let bottom = Math.max(start.rowIndex, end.rowIndex);
|
|
953
|
-
let left = Math.min(start.columnIndex, end.columnIndex);
|
|
954
|
-
let right = Math.max(start.columnIndex, end.columnIndex);
|
|
955
|
-
/**
|
|
956
|
-
* The idea is that
|
|
957
|
-
* We do 2 loops >
|
|
958
|
-
* Left to Right and then top to bottom
|
|
959
|
-
* => Find top cell and bottom cell and check
|
|
960
|
-
* if there are any merged cells at the edge
|
|
961
|
-
* Then keep extending our top and bottom bounds accordingly
|
|
962
|
-
*
|
|
963
|
-
* Same goes for Top to bottom
|
|
964
|
-
* => Find left most and right most cells
|
|
965
|
-
*/
|
|
966
|
-
|
|
967
|
-
if (spanMerges) {
|
|
968
|
-
for (let columnIndex = left; columnIndex <= right; columnIndex++) {
|
|
969
|
-
const topCell = getCellBounds({ rowIndex: top, columnIndex });
|
|
970
|
-
const bottomCell = getCellBounds({ rowIndex: bottom, columnIndex });
|
|
971
|
-
bottom = Math.max(topCell.bottom, bottomCell.bottom, bottom);
|
|
972
|
-
top = Math.min(topCell.top, bottomCell.top, top);
|
|
973
|
-
}
|
|
974
|
-
for (let rowIndex = top; rowIndex <= bottom; rowIndex++) {
|
|
975
|
-
const topCell = getCellBounds({ rowIndex, columnIndex: left });
|
|
976
|
-
const bottomCell = getCellBounds({ rowIndex, columnIndex: right });
|
|
977
|
-
right = Math.max(topCell.right, bottomCell.right, right);
|
|
978
|
-
left = Math.min(topCell.left, bottomCell.left, left);
|
|
979
|
-
}
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
return {
|
|
983
|
-
top,
|
|
984
|
-
left,
|
|
985
|
-
right,
|
|
986
|
-
bottom,
|
|
987
|
-
};
|
|
988
|
-
};
|
|
989
|
-
|
|
990
|
-
/**
|
|
991
|
-
* Check if its being rendered in Browser or SSR
|
|
992
|
-
*/
|
|
993
|
-
export const canUseDOM = !!(
|
|
994
|
-
typeof window !== "undefined" &&
|
|
995
|
-
window.document &&
|
|
996
|
-
window.document.createElement
|
|
997
|
-
);
|
|
998
|
-
|
|
999
|
-
/**
|
|
1000
|
-
* Simple Canvas element to measure text size
|
|
1001
|
-
*
|
|
1002
|
-
* Usage
|
|
1003
|
-
*
|
|
1004
|
-
* ```
|
|
1005
|
-
* const textSizer = new AutoSizer()
|
|
1006
|
-
* textSizer.measureText('Hello world').width
|
|
1007
|
-
* ```
|
|
1008
|
-
*/
|
|
1009
|
-
interface AutoSizerProps {
|
|
1010
|
-
fontFamily?: string;
|
|
1011
|
-
fontSize?: number;
|
|
1012
|
-
fontWeight?: string;
|
|
1013
|
-
fontStyle?: string;
|
|
1014
|
-
lineHeight?: number;
|
|
1015
|
-
scale?: number;
|
|
1016
|
-
}
|
|
1017
|
-
|
|
1018
|
-
type IOptions = {
|
|
1019
|
-
[key: string]: any;
|
|
1020
|
-
};
|
|
1021
|
-
|
|
1022
|
-
export const AutoSizerCanvas = (defaults: AutoSizerProps = {}) => {
|
|
1023
|
-
const {
|
|
1024
|
-
fontFamily = "Arial",
|
|
1025
|
-
fontSize = 12,
|
|
1026
|
-
fontWeight = "",
|
|
1027
|
-
fontStyle = "",
|
|
1028
|
-
lineHeight = 16,
|
|
1029
|
-
scale = 1,
|
|
1030
|
-
} = defaults;
|
|
1031
|
-
var o: IOptions = {
|
|
1032
|
-
fontFamily,
|
|
1033
|
-
fontSize,
|
|
1034
|
-
fontWeight,
|
|
1035
|
-
fontStyle,
|
|
1036
|
-
lineHeight,
|
|
1037
|
-
scale,
|
|
1038
|
-
};
|
|
1039
|
-
const canvas =
|
|
1040
|
-
canUseDOM && <HTMLCanvasElement>document.createElement("canvas");
|
|
1041
|
-
const context = canvas ? canvas.getContext("2d") : null;
|
|
1042
|
-
|
|
1043
|
-
const setFont = (options: IOptions = {}) => {
|
|
1044
|
-
for (const key in options) {
|
|
1045
|
-
o[key] = options[key] ?? o[key];
|
|
1046
|
-
}
|
|
1047
|
-
if (context) {
|
|
1048
|
-
context.font = `${o.fontStyle} ${o.fontWeight} ${
|
|
1049
|
-
o.fontSize * o.scale
|
|
1050
|
-
}px ${o.fontFamily}`;
|
|
1051
|
-
}
|
|
1052
|
-
};
|
|
1053
|
-
const getWidthOfLongestText = (text: string | undefined) => {
|
|
1054
|
-
let width = 0;
|
|
1055
|
-
let height = 0;
|
|
1056
|
-
if (text === void 0) return { width, height };
|
|
1057
|
-
const lines = text.split("\n");
|
|
1058
|
-
for (let i = 0; i < lines.length; i++) {
|
|
1059
|
-
const line = lines[i];
|
|
1060
|
-
const lineWidth = context?.measureText(line).width ?? 0;
|
|
1061
|
-
width = Math.max(width, lineWidth);
|
|
1062
|
-
height += o.fontSize * 1.2 * o.scale;
|
|
1063
|
-
}
|
|
1064
|
-
return { width: Math.ceil(width), height: Math.ceil(height) };
|
|
1065
|
-
};
|
|
1066
|
-
const measureText = (text: string | number) =>
|
|
1067
|
-
getWidthOfLongestText(castToString(text));
|
|
1068
|
-
const reset = () => setFont(defaults);
|
|
1069
|
-
/* Set font in constructor */
|
|
1070
|
-
setFont(o);
|
|
1071
|
-
|
|
1072
|
-
return {
|
|
1073
|
-
context,
|
|
1074
|
-
measureText,
|
|
1075
|
-
setFont,
|
|
1076
|
-
reset,
|
|
1077
|
-
};
|
|
1078
|
-
};
|
|
1079
|
-
|
|
1080
|
-
/* Export a singleton */
|
|
1081
|
-
export const autoSizerCanvas = AutoSizerCanvas();
|
|
1082
|
-
|
|
1083
|
-
/* Check if a value is null */
|
|
1084
|
-
export const isNull = (value: any) =>
|
|
1085
|
-
value === void 0 || value === null || value === "";
|
|
1086
|
-
|
|
1087
|
-
export const isEqualCells = (
|
|
1088
|
-
a: CellInterface | null,
|
|
1089
|
-
b: CellInterface | null
|
|
1090
|
-
) => {
|
|
1091
|
-
if (isNull(a) || isNull(b) || a === null || b === null) return false;
|
|
1092
|
-
return a.rowIndex === b.rowIndex && a.columnIndex === b.columnIndex;
|
|
1093
|
-
};
|
|
1094
|
-
|
|
1095
|
-
/**
|
|
1096
|
-
* Simple utility function to check if cell is within bounds
|
|
1097
|
-
* @param cell
|
|
1098
|
-
* @param bounds
|
|
1099
|
-
*/
|
|
1100
|
-
export const isCellWithinBounds = (cell: CellInterface, bounds: AreaProps) => {
|
|
1101
|
-
if (cell.rowIndex < bounds.top || cell.rowIndex > bounds.bottom) {
|
|
1102
|
-
return false;
|
|
1103
|
-
}
|
|
1104
|
-
if (cell.columnIndex < bounds.left || cell.columnIndex > bounds.right) {
|
|
1105
|
-
return false;
|
|
1106
|
-
}
|
|
1107
|
-
return true;
|
|
1108
|
-
};
|
|
1109
|
-
|
|
1110
|
-
/**
|
|
1111
|
-
* Find next row Index
|
|
1112
|
-
* @param rowIndex
|
|
1113
|
-
* @param direction
|
|
1114
|
-
*/
|
|
1115
|
-
export type HiddenType = (i: number) => boolean;
|
|
1116
|
-
export const clampIndex = (
|
|
1117
|
-
index: number,
|
|
1118
|
-
isHidden: HiddenType | undefined,
|
|
1119
|
-
direction: Direction
|
|
1120
|
-
) => {
|
|
1121
|
-
switch (direction) {
|
|
1122
|
-
case Direction.Right:
|
|
1123
|
-
case Direction.Down:
|
|
1124
|
-
let hidden = isHidden?.(index);
|
|
1125
|
-
while (hidden === true) {
|
|
1126
|
-
hidden = isHidden?.(++index);
|
|
1127
|
-
}
|
|
1128
|
-
break;
|
|
1129
|
-
|
|
1130
|
-
case Direction.Left:
|
|
1131
|
-
case Direction.Up: {
|
|
1132
|
-
let hidden = isHidden?.(index);
|
|
1133
|
-
while (hidden === true) {
|
|
1134
|
-
hidden = isHidden?.(--index);
|
|
1135
|
-
}
|
|
1136
|
-
break;
|
|
1137
|
-
}
|
|
1138
|
-
}
|
|
1139
|
-
return index;
|
|
1140
|
-
};
|
|
1141
|
-
|
|
1142
|
-
type ValueGetter = (cell: CellInterface) => string | number | undefined;
|
|
1143
|
-
|
|
1144
|
-
/**
|
|
1145
|
-
* Find a cell with content if the current cell is out of the current dataregion
|
|
1146
|
-
* [
|
|
1147
|
-
* 1, 2, 3,
|
|
1148
|
-
* x, x, x
|
|
1149
|
-
* 7, 8, 9
|
|
1150
|
-
* ]
|
|
1151
|
-
* activeCel = 2
|
|
1152
|
-
* direction = Down
|
|
1153
|
-
* New Cell = 8
|
|
1154
|
-
*
|
|
1155
|
-
* @param activeCell
|
|
1156
|
-
* @param getValue
|
|
1157
|
-
* @param isHidden
|
|
1158
|
-
* @param direction
|
|
1159
|
-
* @param limit
|
|
1160
|
-
*/
|
|
1161
|
-
export const findNextContentfulCell = (
|
|
1162
|
-
activeCell: CellInterface,
|
|
1163
|
-
getValue: ValueGetter,
|
|
1164
|
-
isHidden: HiddenType | undefined,
|
|
1165
|
-
direction: Direction,
|
|
1166
|
-
limit: number
|
|
1167
|
-
) => {
|
|
1168
|
-
var { rowIndex, columnIndex } = activeCell;
|
|
1169
|
-
switch (direction) {
|
|
1170
|
-
case Direction.Down: {
|
|
1171
|
-
rowIndex = clampIndex(Math.min(rowIndex + 1, limit), isHidden, direction);
|
|
1172
|
-
let value = getValue({ rowIndex, columnIndex });
|
|
1173
|
-
while (isNull(value) && rowIndex < limit) {
|
|
1174
|
-
rowIndex = clampIndex(Math.min(++rowIndex, limit), isHidden, direction);
|
|
1175
|
-
value = getValue({ rowIndex, columnIndex });
|
|
1176
|
-
}
|
|
1177
|
-
return { rowIndex, columnIndex };
|
|
1178
|
-
}
|
|
1179
|
-
|
|
1180
|
-
case Direction.Up: {
|
|
1181
|
-
rowIndex = clampIndex(Math.max(rowIndex - 1, limit), isHidden, direction);
|
|
1182
|
-
let value = getValue({ rowIndex, columnIndex });
|
|
1183
|
-
while (isNull(value) && rowIndex > limit) {
|
|
1184
|
-
rowIndex = clampIndex(Math.max(--rowIndex, limit), isHidden, direction);
|
|
1185
|
-
value = getValue({ rowIndex, columnIndex });
|
|
1186
|
-
}
|
|
1187
|
-
return { rowIndex, columnIndex };
|
|
1188
|
-
}
|
|
1189
|
-
|
|
1190
|
-
case Direction.Right: {
|
|
1191
|
-
columnIndex = clampIndex(
|
|
1192
|
-
Math.min(columnIndex + 1, limit),
|
|
1193
|
-
isHidden,
|
|
1194
|
-
direction
|
|
1195
|
-
);
|
|
1196
|
-
let value = getValue({ rowIndex, columnIndex });
|
|
1197
|
-
while (isNull(value) && columnIndex < limit) {
|
|
1198
|
-
columnIndex = clampIndex(
|
|
1199
|
-
Math.min(++columnIndex, limit),
|
|
1200
|
-
isHidden,
|
|
1201
|
-
direction
|
|
1202
|
-
);
|
|
1203
|
-
value = getValue({ rowIndex, columnIndex });
|
|
1204
|
-
}
|
|
1205
|
-
return { rowIndex, columnIndex };
|
|
1206
|
-
}
|
|
1207
|
-
|
|
1208
|
-
case Direction.Left: {
|
|
1209
|
-
columnIndex = clampIndex(
|
|
1210
|
-
Math.max(columnIndex - 1, limit),
|
|
1211
|
-
isHidden,
|
|
1212
|
-
direction
|
|
1213
|
-
);
|
|
1214
|
-
let value = getValue({ rowIndex, columnIndex });
|
|
1215
|
-
while (isNull(value) && columnIndex > limit) {
|
|
1216
|
-
columnIndex = clampIndex(
|
|
1217
|
-
Math.max(--columnIndex, limit),
|
|
1218
|
-
isHidden,
|
|
1219
|
-
direction
|
|
1220
|
-
);
|
|
1221
|
-
value = getValue({ rowIndex, columnIndex });
|
|
1222
|
-
}
|
|
1223
|
-
return { rowIndex, columnIndex };
|
|
1224
|
-
}
|
|
1225
|
-
|
|
1226
|
-
default:
|
|
1227
|
-
return activeCell;
|
|
1228
|
-
}
|
|
1229
|
-
};
|
|
1230
|
-
|
|
1231
|
-
/**
|
|
1232
|
-
* Find the next cell
|
|
1233
|
-
* @param activeCell
|
|
1234
|
-
* @param getValue
|
|
1235
|
-
* @param isHidden
|
|
1236
|
-
* @param direction
|
|
1237
|
-
* @param limit
|
|
1238
|
-
*/
|
|
1239
|
-
export const findLastContentfulCell = (
|
|
1240
|
-
activeCell: CellInterface,
|
|
1241
|
-
getValue: ValueGetter,
|
|
1242
|
-
isHidden: HiddenType | undefined,
|
|
1243
|
-
direction: Direction,
|
|
1244
|
-
limit: number
|
|
1245
|
-
): CellInterface => {
|
|
1246
|
-
var { rowIndex, columnIndex } = activeCell;
|
|
1247
|
-
switch (direction) {
|
|
1248
|
-
case Direction.Down: {
|
|
1249
|
-
rowIndex = clampIndex(Math.min(rowIndex + 1, limit), isHidden, direction);
|
|
1250
|
-
let value = getValue({ rowIndex, columnIndex });
|
|
1251
|
-
while (!isNull(value) && rowIndex < limit) {
|
|
1252
|
-
rowIndex = clampIndex(Math.min(++rowIndex, limit), isHidden, direction);
|
|
1253
|
-
value = getValue({ rowIndex, columnIndex });
|
|
1254
|
-
}
|
|
1255
|
-
return {
|
|
1256
|
-
columnIndex,
|
|
1257
|
-
rowIndex: isNull(getValue({ columnIndex, rowIndex }))
|
|
1258
|
-
? rowIndex - 1
|
|
1259
|
-
: rowIndex,
|
|
1260
|
-
};
|
|
1261
|
-
}
|
|
1262
|
-
case Direction.Up: {
|
|
1263
|
-
rowIndex = clampIndex(Math.max(rowIndex - 1, limit), isHidden, direction);
|
|
1264
|
-
let value = getValue({ rowIndex, columnIndex });
|
|
1265
|
-
while (!isNull(value) && rowIndex > limit) {
|
|
1266
|
-
rowIndex = clampIndex(Math.max(--rowIndex, limit), isHidden, direction);
|
|
1267
|
-
value = getValue({ rowIndex, columnIndex });
|
|
1268
|
-
}
|
|
1269
|
-
return {
|
|
1270
|
-
columnIndex,
|
|
1271
|
-
rowIndex: isNull(getValue({ columnIndex, rowIndex }))
|
|
1272
|
-
? rowIndex + 1
|
|
1273
|
-
: rowIndex,
|
|
1274
|
-
};
|
|
1275
|
-
}
|
|
1276
|
-
case Direction.Right: {
|
|
1277
|
-
columnIndex = clampIndex(
|
|
1278
|
-
Math.min(columnIndex + 1, limit),
|
|
1279
|
-
isHidden,
|
|
1280
|
-
direction
|
|
1281
|
-
);
|
|
1282
|
-
let value = getValue({ rowIndex, columnIndex });
|
|
1283
|
-
while (!isNull(value) && columnIndex < limit) {
|
|
1284
|
-
columnIndex = clampIndex(
|
|
1285
|
-
Math.min(++columnIndex, limit),
|
|
1286
|
-
isHidden,
|
|
1287
|
-
direction
|
|
1288
|
-
);
|
|
1289
|
-
value = getValue({ rowIndex, columnIndex });
|
|
1290
|
-
}
|
|
1291
|
-
return {
|
|
1292
|
-
rowIndex,
|
|
1293
|
-
columnIndex: isNull(getValue({ columnIndex, rowIndex }))
|
|
1294
|
-
? columnIndex - 1
|
|
1295
|
-
: columnIndex,
|
|
1296
|
-
};
|
|
1297
|
-
}
|
|
1298
|
-
|
|
1299
|
-
case Direction.Left: {
|
|
1300
|
-
columnIndex = clampIndex(
|
|
1301
|
-
Math.max(columnIndex - 1, limit),
|
|
1302
|
-
isHidden,
|
|
1303
|
-
direction
|
|
1304
|
-
);
|
|
1305
|
-
let value = getValue({ rowIndex, columnIndex });
|
|
1306
|
-
while (!isNull(value) && columnIndex > limit) {
|
|
1307
|
-
columnIndex = clampIndex(
|
|
1308
|
-
Math.max(--columnIndex, limit),
|
|
1309
|
-
isHidden,
|
|
1310
|
-
direction
|
|
1311
|
-
);
|
|
1312
|
-
value = getValue({ rowIndex, columnIndex });
|
|
1313
|
-
}
|
|
1314
|
-
return {
|
|
1315
|
-
rowIndex,
|
|
1316
|
-
columnIndex: isNull(getValue({ columnIndex, rowIndex }))
|
|
1317
|
-
? columnIndex + 1
|
|
1318
|
-
: columnIndex,
|
|
1319
|
-
};
|
|
1320
|
-
}
|
|
1321
|
-
|
|
1322
|
-
default:
|
|
1323
|
-
return activeCell;
|
|
1324
|
-
}
|
|
1325
|
-
};
|
|
1326
|
-
|
|
1327
|
-
/**
|
|
1328
|
-
* Ex
|
|
1329
|
-
*/
|
|
1330
|
-
export const findNextCellInDataRegion = (
|
|
1331
|
-
activeCell: CellInterface,
|
|
1332
|
-
getValue: ValueGetter,
|
|
1333
|
-
isHidden: HiddenType | undefined,
|
|
1334
|
-
direction: Direction,
|
|
1335
|
-
limit: number
|
|
1336
|
-
): number => {
|
|
1337
|
-
var { rowIndex, columnIndex } = activeCell;
|
|
1338
|
-
const isCurrentCellEmpty = isNull(getValue(activeCell));
|
|
1339
|
-
const didWeReachTheEdge = (cur: boolean, next: boolean): boolean => {
|
|
1340
|
-
return (cur && next) || (cur && !next) || (!cur && next);
|
|
1341
|
-
};
|
|
1342
|
-
switch (direction) {
|
|
1343
|
-
case Direction.Down: {
|
|
1344
|
-
const nextCellValue = getValue({ rowIndex: rowIndex + 1, columnIndex });
|
|
1345
|
-
const isNextCellEmpty = isNull(nextCellValue);
|
|
1346
|
-
const isEdge = didWeReachTheEdge(isCurrentCellEmpty, isNextCellEmpty);
|
|
1347
|
-
const nextCell = isEdge
|
|
1348
|
-
? findNextContentfulCell(
|
|
1349
|
-
activeCell,
|
|
1350
|
-
getValue,
|
|
1351
|
-
isHidden,
|
|
1352
|
-
direction,
|
|
1353
|
-
limit
|
|
1354
|
-
)
|
|
1355
|
-
: findLastContentfulCell(
|
|
1356
|
-
activeCell,
|
|
1357
|
-
getValue,
|
|
1358
|
-
isHidden,
|
|
1359
|
-
direction,
|
|
1360
|
-
limit
|
|
1361
|
-
);
|
|
1362
|
-
return nextCell?.rowIndex;
|
|
1363
|
-
}
|
|
1364
|
-
|
|
1365
|
-
case Direction.Up: {
|
|
1366
|
-
const nextCellValue = getValue({ rowIndex: rowIndex - 1, columnIndex });
|
|
1367
|
-
const isNextCellEmpty = isNull(nextCellValue);
|
|
1368
|
-
const isEdge = didWeReachTheEdge(isCurrentCellEmpty, isNextCellEmpty);
|
|
1369
|
-
const nextCell = isEdge
|
|
1370
|
-
? findNextContentfulCell(
|
|
1371
|
-
activeCell,
|
|
1372
|
-
getValue,
|
|
1373
|
-
isHidden,
|
|
1374
|
-
direction,
|
|
1375
|
-
limit
|
|
1376
|
-
)
|
|
1377
|
-
: findLastContentfulCell(
|
|
1378
|
-
activeCell,
|
|
1379
|
-
getValue,
|
|
1380
|
-
isHidden,
|
|
1381
|
-
direction,
|
|
1382
|
-
limit
|
|
1383
|
-
);
|
|
1384
|
-
return nextCell?.rowIndex;
|
|
1385
|
-
}
|
|
1386
|
-
|
|
1387
|
-
case Direction.Right: {
|
|
1388
|
-
const nextCellValue = getValue({
|
|
1389
|
-
rowIndex,
|
|
1390
|
-
columnIndex: columnIndex + 1,
|
|
1391
|
-
});
|
|
1392
|
-
const isNextCellEmpty = isNull(nextCellValue);
|
|
1393
|
-
const isEdge = didWeReachTheEdge(isCurrentCellEmpty, isNextCellEmpty);
|
|
1394
|
-
const nextCell = isEdge
|
|
1395
|
-
? findNextContentfulCell(
|
|
1396
|
-
activeCell,
|
|
1397
|
-
getValue,
|
|
1398
|
-
isHidden,
|
|
1399
|
-
direction,
|
|
1400
|
-
limit
|
|
1401
|
-
)
|
|
1402
|
-
: findLastContentfulCell(
|
|
1403
|
-
activeCell,
|
|
1404
|
-
getValue,
|
|
1405
|
-
isHidden,
|
|
1406
|
-
direction,
|
|
1407
|
-
limit
|
|
1408
|
-
);
|
|
1409
|
-
return nextCell?.columnIndex;
|
|
1410
|
-
}
|
|
1411
|
-
|
|
1412
|
-
case Direction.Left: {
|
|
1413
|
-
const nextCellValue = getValue({
|
|
1414
|
-
rowIndex,
|
|
1415
|
-
columnIndex: columnIndex - 1,
|
|
1416
|
-
});
|
|
1417
|
-
const isNextCellEmpty = isNull(nextCellValue);
|
|
1418
|
-
const isEdge = didWeReachTheEdge(isCurrentCellEmpty, isNextCellEmpty);
|
|
1419
|
-
const nextCell = isEdge
|
|
1420
|
-
? findNextContentfulCell(
|
|
1421
|
-
activeCell,
|
|
1422
|
-
getValue,
|
|
1423
|
-
isHidden,
|
|
1424
|
-
direction,
|
|
1425
|
-
limit
|
|
1426
|
-
)
|
|
1427
|
-
: findLastContentfulCell(
|
|
1428
|
-
activeCell,
|
|
1429
|
-
getValue,
|
|
1430
|
-
isHidden,
|
|
1431
|
-
direction,
|
|
1432
|
-
limit
|
|
1433
|
-
);
|
|
1434
|
-
return nextCell?.columnIndex;
|
|
1435
|
-
}
|
|
1436
|
-
}
|
|
1437
|
-
};
|
|
1438
|
-
|
|
1439
|
-
/* Focusable node names */
|
|
1440
|
-
export const focusableNodeNames = new Set(["INPUT", "TEXTAREA", "SELECT"]);
|
|
1441
|
-
|
|
1442
|
-
/**
|
|
1443
|
-
* Converts a value to string
|
|
1444
|
-
* @param value
|
|
1445
|
-
*/
|
|
1446
|
-
export const castToString = (value: any): string | undefined => {
|
|
1447
|
-
if (value === null || value === void 0) return void 0;
|
|
1448
|
-
return typeof value !== "string" ? "" + value : value;
|
|
1449
|
-
};
|
|
1450
|
-
|
|
1451
|
-
export const isArrowKey = (keyCode: number) => {
|
|
1452
|
-
return [KeyCodes.Up, KeyCodes.Down, KeyCodes.Left, KeyCodes.Right].includes(
|
|
1453
|
-
keyCode
|
|
1454
|
-
);
|
|
1455
|
-
};
|