@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.
Files changed (208) hide show
  1. package/LICENSE.md +100 -0
  2. package/Readme.md +53 -59
  3. package/dist/cjs/Cell.d.ts +35 -0
  4. package/dist/cjs/Cell.d.ts.map +1 -0
  5. package/dist/cjs/Cell.js +29 -0
  6. package/dist/{CellOverlay.d.ts → cjs/CellOverlay.d.ts} +5 -5
  7. package/dist/cjs/CellOverlay.d.ts.map +1 -0
  8. package/dist/cjs/CellOverlay.js +126 -0
  9. package/dist/cjs/Context.d.ts +8 -0
  10. package/dist/cjs/Context.d.ts.map +1 -0
  11. package/dist/cjs/Context.js +13 -0
  12. package/dist/{FillHandle.d.ts → cjs/FillHandle.d.ts} +2 -1
  13. package/dist/cjs/FillHandle.d.ts.map +1 -0
  14. package/dist/cjs/FillHandle.js +26 -0
  15. package/dist/cjs/Grid.d.ts +558 -0
  16. package/dist/cjs/Grid.d.ts.map +1 -0
  17. package/dist/cjs/Grid.js +2446 -0
  18. package/dist/{GridLine.d.ts → cjs/GridLine.d.ts} +2 -1
  19. package/dist/cjs/GridLine.d.ts.map +1 -0
  20. package/dist/cjs/GridLine.js +10 -0
  21. package/dist/cjs/Image.d.ts +11 -0
  22. package/dist/cjs/Image.d.ts.map +1 -0
  23. package/dist/cjs/Image.js +57 -0
  24. package/dist/cjs/MemoizedGrid.d.ts +49 -0
  25. package/dist/cjs/MemoizedGrid.d.ts.map +1 -0
  26. package/dist/cjs/MemoizedGrid.js +105 -0
  27. package/dist/cjs/Selection.d.ts +5 -0
  28. package/dist/cjs/Selection.d.ts.map +1 -0
  29. package/dist/cjs/Selection.js +10 -0
  30. package/dist/{helpers.d.ts → cjs/helpers.d.ts} +59 -39
  31. package/dist/cjs/helpers.d.ts.map +1 -0
  32. package/dist/cjs/helpers.js +1409 -0
  33. package/dist/{hooks → cjs/hooks}/useCopyPaste.d.ts +3 -2
  34. package/dist/cjs/hooks/useCopyPaste.d.ts.map +1 -0
  35. package/dist/cjs/hooks/useCopyPaste.js +230 -0
  36. package/dist/{hooks → cjs/hooks}/useEditable.d.ts +21 -8
  37. package/dist/cjs/hooks/useEditable.d.ts.map +1 -0
  38. package/dist/cjs/hooks/useEditable.js +491 -0
  39. package/dist/{hooks → cjs/hooks}/useFilter.d.ts +3 -2
  40. package/dist/cjs/hooks/useFilter.d.ts.map +1 -0
  41. package/dist/cjs/hooks/useFilter.js +97 -0
  42. package/dist/{hooks → cjs/hooks}/useImage.d.ts +1 -0
  43. package/dist/cjs/hooks/useImage.d.ts.map +1 -0
  44. package/dist/cjs/hooks/useImage.js +80 -0
  45. package/dist/{hooks → cjs/hooks}/usePagination.d.ts +3 -2
  46. package/dist/cjs/hooks/usePagination.d.ts.map +1 -0
  47. package/dist/cjs/hooks/usePagination.js +53 -0
  48. package/dist/{hooks → cjs/hooks}/useSizer.d.ts +28 -29
  49. package/dist/cjs/hooks/useSizer.d.ts.map +1 -0
  50. package/dist/cjs/hooks/useSizer.js +176 -0
  51. package/dist/{hooks → cjs/hooks}/useTooltip.d.ts +2 -1
  52. package/dist/cjs/hooks/useTooltip.d.ts.map +1 -0
  53. package/dist/cjs/hooks/useTooltip.js +82 -0
  54. package/dist/{hooks → cjs/hooks}/useUndo.d.ts +2 -1
  55. package/dist/cjs/hooks/useUndo.d.ts.map +1 -0
  56. package/dist/cjs/hooks/useUndo.js +87 -0
  57. package/dist/cjs/index.d.ts +19 -0
  58. package/dist/cjs/index.d.ts.map +1 -0
  59. package/dist/cjs/index.js +29 -0
  60. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -0
  61. package/dist/{types.d.ts → cjs/types.d.ts} +8 -3
  62. package/dist/cjs/types.d.ts.map +1 -0
  63. package/dist/{types.js → cjs/types.js} +7 -8
  64. package/dist/cjs/utils.d.ts +9 -0
  65. package/dist/cjs/utils.d.ts.map +1 -0
  66. package/dist/cjs/utils.js +58 -0
  67. package/dist/cjs/vitest.config.d.ts +3 -0
  68. package/dist/cjs/vitest.config.d.ts.map +1 -0
  69. package/dist/cjs/vitest.config.js +24 -0
  70. package/dist/esm/Cell.d.ts +35 -0
  71. package/dist/esm/Cell.d.ts.map +1 -0
  72. package/dist/esm/Cell.js +24 -0
  73. package/dist/esm/CellOverlay.d.ts +21 -0
  74. package/dist/esm/CellOverlay.d.ts.map +1 -0
  75. package/dist/esm/CellOverlay.js +120 -0
  76. package/dist/esm/Context.d.ts +8 -0
  77. package/dist/esm/Context.d.ts.map +1 -0
  78. package/dist/esm/Context.js +9 -0
  79. package/dist/esm/FillHandle.d.ts +8 -0
  80. package/dist/esm/FillHandle.d.ts.map +1 -0
  81. package/dist/{FillHandle.js → esm/FillHandle.js} +5 -11
  82. package/dist/esm/Grid.d.ts +558 -0
  83. package/dist/esm/Grid.d.ts.map +1 -0
  84. package/dist/esm/Grid.js +2451 -0
  85. package/dist/esm/GridLine.d.ts +5 -0
  86. package/dist/esm/GridLine.d.ts.map +1 -0
  87. package/dist/esm/GridLine.js +7 -0
  88. package/dist/esm/Image.d.ts +11 -0
  89. package/dist/esm/Image.d.ts.map +1 -0
  90. package/dist/esm/Image.js +53 -0
  91. package/dist/esm/MemoizedGrid.d.ts +49 -0
  92. package/dist/esm/MemoizedGrid.d.ts.map +1 -0
  93. package/dist/esm/MemoizedGrid.js +98 -0
  94. package/dist/esm/Selection.d.ts +5 -0
  95. package/dist/esm/Selection.d.ts.map +1 -0
  96. package/dist/esm/Selection.js +7 -0
  97. package/dist/esm/helpers.d.ts +281 -0
  98. package/dist/esm/helpers.d.ts.map +1 -0
  99. package/dist/esm/helpers.js +1260 -0
  100. package/dist/esm/hooks/useCopyPaste.d.ts +53 -0
  101. package/dist/esm/hooks/useCopyPaste.d.ts.map +1 -0
  102. package/dist/{hooks → esm/hooks}/useCopyPaste.js +40 -46
  103. package/dist/esm/hooks/useEditable.d.ts +253 -0
  104. package/dist/esm/hooks/useEditable.d.ts.map +1 -0
  105. package/dist/esm/hooks/useEditable.js +482 -0
  106. package/dist/esm/hooks/useFilter.d.ts +69 -0
  107. package/dist/esm/hooks/useFilter.d.ts.map +1 -0
  108. package/dist/{hooks → esm/hooks}/useFilter.js +22 -42
  109. package/dist/esm/hooks/useImage.d.ts +13 -0
  110. package/dist/esm/hooks/useImage.d.ts.map +1 -0
  111. package/dist/esm/hooks/useImage.js +76 -0
  112. package/dist/esm/hooks/usePagination.d.ts +69 -0
  113. package/dist/esm/hooks/usePagination.d.ts.map +1 -0
  114. package/dist/esm/hooks/usePagination.js +47 -0
  115. package/dist/esm/hooks/useSizer.d.ts +114 -0
  116. package/dist/esm/hooks/useSizer.d.ts.map +1 -0
  117. package/dist/esm/hooks/useSizer.js +176 -0
  118. package/dist/esm/hooks/useTooltip.d.ts +43 -0
  119. package/dist/esm/hooks/useTooltip.d.ts.map +1 -0
  120. package/dist/esm/hooks/useTooltip.js +80 -0
  121. package/dist/esm/hooks/useUndo.d.ts +36 -0
  122. package/dist/esm/hooks/useUndo.d.ts.map +1 -0
  123. package/dist/{hooks → esm/hooks}/useUndo.js +16 -25
  124. package/dist/esm/index.d.ts +19 -0
  125. package/dist/esm/index.d.ts.map +1 -0
  126. package/dist/esm/index.js +17 -0
  127. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -0
  128. package/dist/esm/types.d.ts +70 -0
  129. package/dist/esm/types.d.ts.map +1 -0
  130. package/dist/esm/types.js +68 -0
  131. package/dist/esm/utils.d.ts +9 -0
  132. package/dist/esm/utils.d.ts.map +1 -0
  133. package/dist/esm/utils.js +104 -0
  134. package/dist/esm/vitest.config.d.ts +3 -0
  135. package/dist/esm/vitest.config.d.ts.map +1 -0
  136. package/dist/esm/vitest.config.js +22 -0
  137. package/package.json +28 -29
  138. package/dist/Cell.d.ts +0 -23
  139. package/dist/Cell.js +0 -55
  140. package/dist/Cell.js.map +0 -1
  141. package/dist/CellOverlay.js +0 -116
  142. package/dist/CellOverlay.js.map +0 -1
  143. package/dist/FillHandle.js.map +0 -1
  144. package/dist/Grid.d.ts +0 -385
  145. package/dist/Grid.js +0 -1684
  146. package/dist/Grid.js.map +0 -1
  147. package/dist/GridLine.js +0 -13
  148. package/dist/GridLine.js.map +0 -1
  149. package/dist/Image.d.ts +0 -8
  150. package/dist/Image.js +0 -50
  151. package/dist/Image.js.map +0 -1
  152. package/dist/Selection.d.ts +0 -4
  153. package/dist/Selection.js +0 -9
  154. package/dist/Selection.js.map +0 -1
  155. package/dist/helpers.js +0 -1033
  156. package/dist/helpers.js.map +0 -1
  157. package/dist/hooks/useCopyPaste.js.map +0 -1
  158. package/dist/hooks/useEditable.js +0 -477
  159. package/dist/hooks/useEditable.js.map +0 -1
  160. package/dist/hooks/useFilter.js.map +0 -1
  161. package/dist/hooks/useImage.js +0 -42
  162. package/dist/hooks/useImage.js.map +0 -1
  163. package/dist/hooks/usePagination.js +0 -73
  164. package/dist/hooks/usePagination.js.map +0 -1
  165. package/dist/hooks/useSelection.d.ts +0 -175
  166. package/dist/hooks/useSelection.js +0 -980
  167. package/dist/hooks/useSelection.js.map +0 -1
  168. package/dist/hooks/useSizer.js +0 -150
  169. package/dist/hooks/useSizer.js.map +0 -1
  170. package/dist/hooks/useTooltip.js +0 -106
  171. package/dist/hooks/useTooltip.js.map +0 -1
  172. package/dist/hooks/useTouch.d.ts +0 -21
  173. package/dist/hooks/useTouch.js +0 -73
  174. package/dist/hooks/useTouch.js.map +0 -1
  175. package/dist/hooks/useUndo.js.map +0 -1
  176. package/dist/index.d.ts +0 -25
  177. package/dist/index.js +0 -61
  178. package/dist/index.js.map +0 -1
  179. package/dist/types.js.map +0 -1
  180. package/dist/utils.d.ts +0 -8
  181. package/dist/utils.js +0 -153
  182. package/dist/utils.js.map +0 -1
  183. package/src/Cell.tsx +0 -100
  184. package/src/CellOverlay.tsx +0 -191
  185. package/src/FillHandle.tsx +0 -37
  186. package/src/Grid.tsx +0 -2677
  187. package/src/GridLine.tsx +0 -23
  188. package/src/Image.tsx +0 -43
  189. package/src/Selection.tsx +0 -9
  190. package/src/helpers.ts +0 -1455
  191. package/src/hooks/useCopyPaste.ts +0 -265
  192. package/src/hooks/useEditable.tsx +0 -945
  193. package/src/hooks/useFilter.tsx +0 -233
  194. package/src/hooks/useImage.ts +0 -57
  195. package/src/hooks/usePagination.tsx +0 -153
  196. package/src/hooks/useSelection.ts +0 -1437
  197. package/src/hooks/useSizer.ts +0 -312
  198. package/src/hooks/useTooltip.tsx +0 -172
  199. package/src/hooks/useTouch.ts +0 -116
  200. package/src/hooks/useUndo.ts +0 -134
  201. package/src/index.tsx +0 -46
  202. package/src/tests/Grid.spec.tsx +0 -47
  203. package/src/tests/helpers.test.ts +0 -292
  204. package/src/tests/usePagination.test.ts +0 -28
  205. package/src/tests/useSelection.test.ts +0 -27
  206. package/src/tests/useUndo.test.ts +0 -73
  207. package/src/types.ts +0 -69
  208. 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
- };