@rowsncolumns/grid 9.0.4 → 13.2.33
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 -982
- 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 -1440
- 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
|
@@ -1,945 +0,0 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
useCallback,
|
|
3
|
-
useEffect,
|
|
4
|
-
useRef,
|
|
5
|
-
useState,
|
|
6
|
-
useMemo,
|
|
7
|
-
} from "react";
|
|
8
|
-
import {
|
|
9
|
-
CellInterface,
|
|
10
|
-
ScrollCoords,
|
|
11
|
-
CellPosition,
|
|
12
|
-
GridRef,
|
|
13
|
-
SelectionArea,
|
|
14
|
-
} from "../Grid";
|
|
15
|
-
import { KeyCodes, Direction } from "./../types";
|
|
16
|
-
import {
|
|
17
|
-
findNextCellWithinBounds,
|
|
18
|
-
isEqualCells,
|
|
19
|
-
clampIndex,
|
|
20
|
-
HiddenType,
|
|
21
|
-
autoSizerCanvas,
|
|
22
|
-
isArrowKey,
|
|
23
|
-
castToString,
|
|
24
|
-
} from "../helpers";
|
|
25
|
-
|
|
26
|
-
export interface UseEditableOptions {
|
|
27
|
-
editorProps?: () => any;
|
|
28
|
-
/**
|
|
29
|
-
* Inject custom editors based on a cell
|
|
30
|
-
*/
|
|
31
|
-
getEditor?: (cell: CellInterface | null) => React.ElementType;
|
|
32
|
-
/**
|
|
33
|
-
* Access grid methods
|
|
34
|
-
*/
|
|
35
|
-
gridRef: React.MutableRefObject<GridRef | null>;
|
|
36
|
-
/**
|
|
37
|
-
* Value getter
|
|
38
|
-
*/
|
|
39
|
-
getValue: (cell: CellInterface) => any;
|
|
40
|
-
/**
|
|
41
|
-
* Callback when user cancels editing
|
|
42
|
-
*/
|
|
43
|
-
onCancel?: (
|
|
44
|
-
e?: React.KeyboardEvent<
|
|
45
|
-
HTMLTextAreaElement | HTMLInputElement | HTMLDivElement
|
|
46
|
-
>
|
|
47
|
-
) => void;
|
|
48
|
-
/**
|
|
49
|
-
* Callback when user changes a value in editor
|
|
50
|
-
*/
|
|
51
|
-
onChange?: (value: string, activeCell: CellInterface) => void;
|
|
52
|
-
/**
|
|
53
|
-
* Callback when user submits a value. Use this to update state
|
|
54
|
-
*/
|
|
55
|
-
onSubmit?: (
|
|
56
|
-
value: string,
|
|
57
|
-
activeCell: CellInterface,
|
|
58
|
-
nextActiveCell?: CellInterface | null
|
|
59
|
-
) => void;
|
|
60
|
-
/**
|
|
61
|
-
* Callback when user selects an area and presses delete key
|
|
62
|
-
*/
|
|
63
|
-
onDelete?: (activeCell: CellInterface, selections: SelectionArea[]) => void;
|
|
64
|
-
/**
|
|
65
|
-
* Currently selected cells, injected by useSelection
|
|
66
|
-
*/
|
|
67
|
-
selections: SelectionArea[];
|
|
68
|
-
/**
|
|
69
|
-
* Active selected cell. This can change, if the user is in formula mode
|
|
70
|
-
*/
|
|
71
|
-
activeCell: CellInterface | null;
|
|
72
|
-
/**
|
|
73
|
-
* Callback fired before editing. Can be used to prevent editing. Do not use it, Can be removed in next release.
|
|
74
|
-
*/
|
|
75
|
-
canEdit?: (coords: CellInterface) => boolean;
|
|
76
|
-
/**
|
|
77
|
-
* Number of frozen columns
|
|
78
|
-
*/
|
|
79
|
-
frozenColumns?: number;
|
|
80
|
-
/**
|
|
81
|
-
* Number of frozen rows
|
|
82
|
-
*/
|
|
83
|
-
frozenRows?: number;
|
|
84
|
-
/**
|
|
85
|
-
* Hide editor on blur
|
|
86
|
-
*/
|
|
87
|
-
hideOnBlur?: boolean;
|
|
88
|
-
/**
|
|
89
|
-
* Hidden rows
|
|
90
|
-
*/
|
|
91
|
-
isHiddenRow: HiddenType;
|
|
92
|
-
/**
|
|
93
|
-
* Hidden columns
|
|
94
|
-
*/
|
|
95
|
-
isHiddenColumn: HiddenType;
|
|
96
|
-
/**
|
|
97
|
-
* No of columns in the grid
|
|
98
|
-
*/
|
|
99
|
-
columnCount: number;
|
|
100
|
-
/**
|
|
101
|
-
* No of rows in the grid
|
|
102
|
-
*/
|
|
103
|
-
rowCount: number;
|
|
104
|
-
/**
|
|
105
|
-
* Top bound of selection
|
|
106
|
-
*/
|
|
107
|
-
selectionTopBound?: number;
|
|
108
|
-
/**
|
|
109
|
-
* Bottom bound
|
|
110
|
-
*/
|
|
111
|
-
selectionBottomBound?: number;
|
|
112
|
-
/**
|
|
113
|
-
* Left bound
|
|
114
|
-
*/
|
|
115
|
-
selectionLeftBound?: number;
|
|
116
|
-
/**
|
|
117
|
-
* Right bound
|
|
118
|
-
*/
|
|
119
|
-
selectionRightBound?: number;
|
|
120
|
-
onKeyDown?: (
|
|
121
|
-
e: React.KeyboardEvent<
|
|
122
|
-
HTMLInputElement | HTMLTextAreaElement | HTMLDivElement
|
|
123
|
-
>
|
|
124
|
-
) => void;
|
|
125
|
-
/**
|
|
126
|
-
* Sync callback before a cell is edited
|
|
127
|
-
*/
|
|
128
|
-
onBeforeEdit?: (coords: CellInterface) => void;
|
|
129
|
-
/**
|
|
130
|
-
* If true, Once the editor is active, it will be always visible.
|
|
131
|
-
* Editor will not scroll with the grid
|
|
132
|
-
*/
|
|
133
|
-
sticky?: boolean;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
export interface EditableResults {
|
|
137
|
-
/**
|
|
138
|
-
* Editor component that can be injected
|
|
139
|
-
*/
|
|
140
|
-
editorComponent: React.ReactNode;
|
|
141
|
-
/**
|
|
142
|
-
* Double click listener, activates the grid
|
|
143
|
-
*/
|
|
144
|
-
onDoubleClick: (e: React.MouseEvent<HTMLDivElement>) => void;
|
|
145
|
-
/**
|
|
146
|
-
* OnScroll listener to align the editor
|
|
147
|
-
*/
|
|
148
|
-
onScroll?: (props: ScrollCoords) => void;
|
|
149
|
-
/**
|
|
150
|
-
* Key down listeners
|
|
151
|
-
*/
|
|
152
|
-
onKeyDown: (e: React.KeyboardEvent<HTMLDivElement>) => void;
|
|
153
|
-
/**
|
|
154
|
-
* Get next focusable cell based on current activeCell and direction user is moving
|
|
155
|
-
*/
|
|
156
|
-
nextFocusableCell: (
|
|
157
|
-
currentCell: CellInterface,
|
|
158
|
-
direction: Direction
|
|
159
|
-
) => CellInterface | null;
|
|
160
|
-
/**
|
|
161
|
-
* Is editing in progress
|
|
162
|
-
*/
|
|
163
|
-
isEditInProgress: boolean;
|
|
164
|
-
/**
|
|
165
|
-
* Currently editing cell
|
|
166
|
-
*/
|
|
167
|
-
editingCell: CellInterface | null;
|
|
168
|
-
/**
|
|
169
|
-
* Make a cell editable
|
|
170
|
-
*/
|
|
171
|
-
makeEditable: (cell: CellInterface, value?: string) => void;
|
|
172
|
-
/**
|
|
173
|
-
* Set editable value imperatively
|
|
174
|
-
*/
|
|
175
|
-
setValue: (
|
|
176
|
-
value: string,
|
|
177
|
-
activeCell: CellInterface,
|
|
178
|
-
previousValue?: string
|
|
179
|
-
) => void;
|
|
180
|
-
/**
|
|
181
|
-
* Hide editor
|
|
182
|
-
*/
|
|
183
|
-
hideEditor: () => void;
|
|
184
|
-
/**
|
|
185
|
-
* Show editor
|
|
186
|
-
*/
|
|
187
|
-
showEditor: () => void;
|
|
188
|
-
/**
|
|
189
|
-
* Bind to mousedown event
|
|
190
|
-
*/
|
|
191
|
-
onMouseDown: (e: React.MouseEvent<HTMLDivElement>) => void;
|
|
192
|
-
/**
|
|
193
|
-
* Imperatively trigger submit
|
|
194
|
-
*/
|
|
195
|
-
submitEditor: (
|
|
196
|
-
value: string,
|
|
197
|
-
activeCell: CellInterface,
|
|
198
|
-
nextActiveCell?: CellInterface | null
|
|
199
|
-
) => void;
|
|
200
|
-
/**
|
|
201
|
-
* Cancels an edit
|
|
202
|
-
*/
|
|
203
|
-
cancelEditor: () => void;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
export interface EditorProps {
|
|
207
|
-
/**
|
|
208
|
-
* Currently selected bounds, useful for fomulas
|
|
209
|
-
*/
|
|
210
|
-
selections?: SelectionArea[];
|
|
211
|
-
/**
|
|
212
|
-
* Initial value of the cell
|
|
213
|
-
*/
|
|
214
|
-
value?: string | number;
|
|
215
|
-
/**
|
|
216
|
-
* Callback when a value has changed.
|
|
217
|
-
*/
|
|
218
|
-
onChange?: (value: string, activeCell: CellInterface) => void;
|
|
219
|
-
/**
|
|
220
|
-
* Callback to submit the value back to data store
|
|
221
|
-
*/
|
|
222
|
-
onSubmit?: (
|
|
223
|
-
value: string | number,
|
|
224
|
-
activeCell: CellInterface,
|
|
225
|
-
nextActiveCell?: CellInterface | null
|
|
226
|
-
) => void;
|
|
227
|
-
/**
|
|
228
|
-
* On Cancel callbacks. Hides the editor
|
|
229
|
-
*/
|
|
230
|
-
onCancel?: (
|
|
231
|
-
e?: React.KeyboardEvent<
|
|
232
|
-
HTMLTextAreaElement | HTMLInputElement | HTMLDivElement
|
|
233
|
-
>
|
|
234
|
-
) => void;
|
|
235
|
-
/**
|
|
236
|
-
* Cell position, x, y, width and height
|
|
237
|
-
*/
|
|
238
|
-
position: CellPosition;
|
|
239
|
-
/**
|
|
240
|
-
* Currently active cell, based on selection
|
|
241
|
-
*/
|
|
242
|
-
activeCell: CellInterface;
|
|
243
|
-
/**
|
|
244
|
-
* Currrently edited cell
|
|
245
|
-
*/
|
|
246
|
-
cell: CellInterface;
|
|
247
|
-
/**
|
|
248
|
-
* Scroll position of the grid
|
|
249
|
-
*/
|
|
250
|
-
scrollPosition: ScrollCoords;
|
|
251
|
-
/**
|
|
252
|
-
* Next cell that should receive focus
|
|
253
|
-
*/
|
|
254
|
-
nextFocusableCell?: (
|
|
255
|
-
activeCell: CellInterface,
|
|
256
|
-
direction?: Direction
|
|
257
|
-
) => CellInterface | null;
|
|
258
|
-
/**
|
|
259
|
-
* Autofocus the editor when open
|
|
260
|
-
*/
|
|
261
|
-
autoFocus?: boolean;
|
|
262
|
-
/**
|
|
263
|
-
* On keydown event
|
|
264
|
-
*/
|
|
265
|
-
onKeyDown?: (
|
|
266
|
-
e: React.KeyboardEvent<
|
|
267
|
-
HTMLInputElement | HTMLTextAreaElement | HTMLDivElement
|
|
268
|
-
>
|
|
269
|
-
) => void;
|
|
270
|
-
/**
|
|
271
|
-
* Max editor width
|
|
272
|
-
*/
|
|
273
|
-
maxWidth?: string | number;
|
|
274
|
-
/**
|
|
275
|
-
* Max editor height
|
|
276
|
-
*/
|
|
277
|
-
maxHeight?: string | number;
|
|
278
|
-
/**
|
|
279
|
-
* Indicates if the cell is part of frozen row
|
|
280
|
-
*/
|
|
281
|
-
isFrozenRow?: boolean;
|
|
282
|
-
/**
|
|
283
|
-
* Indicates if the cell is part of frozen column
|
|
284
|
-
*/
|
|
285
|
-
isFrozenColumn?: boolean;
|
|
286
|
-
/**
|
|
287
|
-
* Frozen row offset
|
|
288
|
-
*/
|
|
289
|
-
frozenRowOffset?: number;
|
|
290
|
-
/**
|
|
291
|
-
* Frozen column offset
|
|
292
|
-
*/
|
|
293
|
-
frozenColumnOffset?: number;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
/**
|
|
297
|
-
* Default cell editor
|
|
298
|
-
* @param props
|
|
299
|
-
*/
|
|
300
|
-
const DefaultEditor: React.FC<EditorProps> = (props) => {
|
|
301
|
-
const {
|
|
302
|
-
onChange,
|
|
303
|
-
onSubmit,
|
|
304
|
-
onCancel,
|
|
305
|
-
position,
|
|
306
|
-
cell,
|
|
307
|
-
nextFocusableCell,
|
|
308
|
-
value = "",
|
|
309
|
-
activeCell,
|
|
310
|
-
autoFocus = true,
|
|
311
|
-
onKeyDown,
|
|
312
|
-
selections,
|
|
313
|
-
scrollPosition,
|
|
314
|
-
maxWidth,
|
|
315
|
-
maxHeight,
|
|
316
|
-
isFrozenRow,
|
|
317
|
-
isFrozenColumn,
|
|
318
|
-
frozenRowOffset,
|
|
319
|
-
frozenColumnOffset,
|
|
320
|
-
...rest
|
|
321
|
-
} = props;
|
|
322
|
-
const borderWidth = 2;
|
|
323
|
-
const padding = 10; /* 2 + 1 + 1 + 2 + 2 */
|
|
324
|
-
const inputRef = useRef<HTMLTextAreaElement | null>(null);
|
|
325
|
-
const { x = 0, y = 0, width = 0, height = 0 } = position;
|
|
326
|
-
const getWidth = useCallback(
|
|
327
|
-
(text: string | number) => {
|
|
328
|
-
const textWidth = autoSizerCanvas.measureText(text)?.width || 0;
|
|
329
|
-
return Math.max(textWidth + padding, width + borderWidth / 2);
|
|
330
|
-
},
|
|
331
|
-
[width]
|
|
332
|
-
);
|
|
333
|
-
useEffect(() => {
|
|
334
|
-
setInputWidth(getWidth(value));
|
|
335
|
-
}, [value]);
|
|
336
|
-
const [inputWidth, setInputWidth] = useState(() => getWidth(value));
|
|
337
|
-
useEffect(() => {
|
|
338
|
-
if (!inputRef.current) return;
|
|
339
|
-
if (autoFocus) inputRef.current.focus();
|
|
340
|
-
/* Focus cursor at the end */
|
|
341
|
-
inputRef.current.selectionStart = castToString(value)?.length ?? 0;
|
|
342
|
-
}, []);
|
|
343
|
-
const inputHeight = height;
|
|
344
|
-
return (
|
|
345
|
-
<div
|
|
346
|
-
style={{
|
|
347
|
-
top: y - borderWidth / 2,
|
|
348
|
-
left: x,
|
|
349
|
-
position: "absolute",
|
|
350
|
-
width: inputWidth,
|
|
351
|
-
height: inputHeight + borderWidth,
|
|
352
|
-
padding: borderWidth,
|
|
353
|
-
boxShadow: "0 2px 6px 2px rgba(60,64,67,.15)",
|
|
354
|
-
border: "2px #1a73e8 solid",
|
|
355
|
-
background: "white",
|
|
356
|
-
}}
|
|
357
|
-
>
|
|
358
|
-
<textarea
|
|
359
|
-
rows={1}
|
|
360
|
-
cols={1}
|
|
361
|
-
ref={inputRef}
|
|
362
|
-
value={value}
|
|
363
|
-
style={{
|
|
364
|
-
font: "12px Arial",
|
|
365
|
-
lineHeight: 1.2,
|
|
366
|
-
width: "100%",
|
|
367
|
-
height: "100%",
|
|
368
|
-
padding: "0 1px",
|
|
369
|
-
margin: 0,
|
|
370
|
-
boxSizing: "border-box",
|
|
371
|
-
borderWidth: 0,
|
|
372
|
-
outline: "none",
|
|
373
|
-
resize: "none",
|
|
374
|
-
overflow: "hidden",
|
|
375
|
-
verticalAlign: "top",
|
|
376
|
-
background: "transparent",
|
|
377
|
-
}}
|
|
378
|
-
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
|
379
|
-
onChange?.(e.target.value, cell);
|
|
380
|
-
}}
|
|
381
|
-
onKeyDown={(e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
|
382
|
-
if (!inputRef.current) return;
|
|
383
|
-
const isShiftKey = e.nativeEvent.shiftKey;
|
|
384
|
-
const value = inputRef.current.value;
|
|
385
|
-
|
|
386
|
-
// Enter key
|
|
387
|
-
if (e.which === KeyCodes.Enter) {
|
|
388
|
-
onSubmit &&
|
|
389
|
-
onSubmit(
|
|
390
|
-
value,
|
|
391
|
-
cell,
|
|
392
|
-
nextFocusableCell?.(
|
|
393
|
-
cell,
|
|
394
|
-
isShiftKey ? Direction.Up : Direction.Down
|
|
395
|
-
)
|
|
396
|
-
);
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
if (e.which === KeyCodes.Escape) {
|
|
400
|
-
onCancel && onCancel(e);
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
if (e.which === KeyCodes.Tab) {
|
|
404
|
-
e.preventDefault();
|
|
405
|
-
onSubmit &&
|
|
406
|
-
onSubmit(
|
|
407
|
-
value,
|
|
408
|
-
cell,
|
|
409
|
-
nextFocusableCell?.(
|
|
410
|
-
cell,
|
|
411
|
-
isShiftKey ? Direction.Left : Direction.Right
|
|
412
|
-
)
|
|
413
|
-
);
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
onKeyDown?.(e);
|
|
417
|
-
}}
|
|
418
|
-
{...rest}
|
|
419
|
-
/>
|
|
420
|
-
</div>
|
|
421
|
-
);
|
|
422
|
-
};
|
|
423
|
-
|
|
424
|
-
export const getDefaultEditor = (cell: CellInterface | null) => DefaultEditor;
|
|
425
|
-
const defaultCanEdit = (cell: CellInterface) => true;
|
|
426
|
-
const defaultIsHidden = (i: number) => false;
|
|
427
|
-
|
|
428
|
-
/**
|
|
429
|
-
* Hook to make grid editable
|
|
430
|
-
* @param param
|
|
431
|
-
*/
|
|
432
|
-
const useEditable = ({
|
|
433
|
-
getEditor = getDefaultEditor,
|
|
434
|
-
gridRef,
|
|
435
|
-
getValue,
|
|
436
|
-
onChange,
|
|
437
|
-
onSubmit,
|
|
438
|
-
onCancel,
|
|
439
|
-
onDelete,
|
|
440
|
-
selections = [],
|
|
441
|
-
activeCell,
|
|
442
|
-
canEdit = defaultCanEdit,
|
|
443
|
-
frozenRows = 0,
|
|
444
|
-
frozenColumns = 0,
|
|
445
|
-
hideOnBlur = true,
|
|
446
|
-
isHiddenRow = defaultIsHidden,
|
|
447
|
-
isHiddenColumn = defaultIsHidden,
|
|
448
|
-
rowCount,
|
|
449
|
-
columnCount,
|
|
450
|
-
selectionTopBound = 0,
|
|
451
|
-
selectionBottomBound = rowCount - 1,
|
|
452
|
-
selectionLeftBound = 0,
|
|
453
|
-
selectionRightBound = columnCount - 1,
|
|
454
|
-
editorProps,
|
|
455
|
-
onBeforeEdit,
|
|
456
|
-
onKeyDown,
|
|
457
|
-
sticky = false,
|
|
458
|
-
}: UseEditableOptions): EditableResults => {
|
|
459
|
-
const [isEditorShown, setShowEditor] = useState<boolean>(false);
|
|
460
|
-
const [value, setValue] = useState<string>("");
|
|
461
|
-
const [position, setPosition] = useState<CellPosition>({
|
|
462
|
-
x: 0,
|
|
463
|
-
y: 0,
|
|
464
|
-
width: 0,
|
|
465
|
-
height: 0,
|
|
466
|
-
});
|
|
467
|
-
const currentActiveCellRef = useRef<CellInterface | null>(null);
|
|
468
|
-
const initialActiveCell = useRef<CellInterface | null>();
|
|
469
|
-
const [scrollPosition, setScrollPosition] = useState<ScrollCoords>({
|
|
470
|
-
scrollLeft: 0,
|
|
471
|
-
scrollTop: 0,
|
|
472
|
-
});
|
|
473
|
-
const [autoFocus, setAutoFocus] = useState<boolean>(true);
|
|
474
|
-
const isDirtyRef = useRef<boolean>(false);
|
|
475
|
-
const currentValueRef = useRef(value);
|
|
476
|
-
const initialValueRef = useRef<string>();
|
|
477
|
-
const maxEditorDimensionsRef = useRef<{ height: number; width: number }>();
|
|
478
|
-
/* To prevent stale closures data */
|
|
479
|
-
const getValueRef = useRef(getValue);
|
|
480
|
-
const showEditor = useCallback(() => setShowEditor(true), []);
|
|
481
|
-
const hideEditor = useCallback(() => {
|
|
482
|
-
setShowEditor(false);
|
|
483
|
-
currentActiveCellRef.current = null;
|
|
484
|
-
}, []);
|
|
485
|
-
const focusGrid = useCallback(() => {
|
|
486
|
-
requestAnimationFrame(() => gridRef.current && gridRef.current.focus());
|
|
487
|
-
}, []);
|
|
488
|
-
|
|
489
|
-
/* Keep ref in sync */
|
|
490
|
-
useEffect(() => {
|
|
491
|
-
currentValueRef.current = value;
|
|
492
|
-
});
|
|
493
|
-
|
|
494
|
-
/* Keep getvalue ref in sync with upstream prop */
|
|
495
|
-
useEffect(() => {
|
|
496
|
-
getValueRef.current = getValue;
|
|
497
|
-
}, [getValue]);
|
|
498
|
-
|
|
499
|
-
/**
|
|
500
|
-
* Make a cell editable
|
|
501
|
-
* @param coords
|
|
502
|
-
* @param initialValue
|
|
503
|
-
*/
|
|
504
|
-
const makeEditable = useCallback(
|
|
505
|
-
(
|
|
506
|
-
coords: CellInterface,
|
|
507
|
-
initialValue?: string,
|
|
508
|
-
autoFocus: boolean = true
|
|
509
|
-
) => {
|
|
510
|
-
if (!gridRef.current) return;
|
|
511
|
-
/* Get actual coords for merged cells */
|
|
512
|
-
coords = gridRef.current.getActualCellCoords(coords);
|
|
513
|
-
|
|
514
|
-
/* Check if its the same cell */
|
|
515
|
-
if (isEqualCells(coords, currentActiveCellRef.current)) {
|
|
516
|
-
return;
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
/* Call on before edit */
|
|
520
|
-
if (canEdit(coords)) {
|
|
521
|
-
/* Let user modify coords before edit */
|
|
522
|
-
onBeforeEdit?.(coords);
|
|
523
|
-
|
|
524
|
-
/* Focus */
|
|
525
|
-
gridRef.current?.scrollToItem(coords);
|
|
526
|
-
|
|
527
|
-
currentActiveCellRef.current = coords;
|
|
528
|
-
|
|
529
|
-
/* Get offsets */
|
|
530
|
-
const pos = gridRef.current.getCellOffsetFromCoords(coords);
|
|
531
|
-
const scrollPosition = gridRef.current.getScrollPosition();
|
|
532
|
-
const cellValue = getValueRef.current(coords);
|
|
533
|
-
const value = initialValue || cellValue || "";
|
|
534
|
-
const cellPosition = sticky
|
|
535
|
-
? // Editor is rendered outside the <Grid /> component
|
|
536
|
-
// If the user has scrolled down, and then activate the editor, we will need to adjust the position
|
|
537
|
-
// of the sticky editor accordingly
|
|
538
|
-
// Subsequent scroll events has no effect, cos of sticky option
|
|
539
|
-
getCellPosition(pos, scrollPosition)
|
|
540
|
-
: pos;
|
|
541
|
-
/**
|
|
542
|
-
* Set max editor ref based on grid container
|
|
543
|
-
*/
|
|
544
|
-
const { containerWidth, containerHeight } =
|
|
545
|
-
gridRef.current.getDimensions();
|
|
546
|
-
maxEditorDimensionsRef.current = {
|
|
547
|
-
height: containerHeight - (cellPosition.y ?? 0),
|
|
548
|
-
width: containerWidth - (cellPosition.x ?? 0),
|
|
549
|
-
};
|
|
550
|
-
|
|
551
|
-
/**
|
|
552
|
-
* If the user has entered a value in the cell, mark it as dirty
|
|
553
|
-
* So that during mousedown, onSubmit gets called
|
|
554
|
-
*/
|
|
555
|
-
isDirtyRef.current = !!initialValue;
|
|
556
|
-
initialValueRef.current = initialValue;
|
|
557
|
-
|
|
558
|
-
/* Trigger onChange handlers */
|
|
559
|
-
setValue(value);
|
|
560
|
-
onChange?.(value, coords);
|
|
561
|
-
setAutoFocus(autoFocus);
|
|
562
|
-
setScrollPosition(scrollPosition);
|
|
563
|
-
setPosition(cellPosition);
|
|
564
|
-
showEditor();
|
|
565
|
-
}
|
|
566
|
-
},
|
|
567
|
-
[frozenRows, frozenColumns, onBeforeEdit, canEdit, sticky]
|
|
568
|
-
);
|
|
569
|
-
|
|
570
|
-
/* Frozen flags */
|
|
571
|
-
const isFrozenRow =
|
|
572
|
-
currentActiveCellRef.current &&
|
|
573
|
-
currentActiveCellRef.current?.rowIndex < frozenRows;
|
|
574
|
-
const isFrozenColumn =
|
|
575
|
-
currentActiveCellRef.current &&
|
|
576
|
-
currentActiveCellRef.current?.columnIndex < frozenColumns;
|
|
577
|
-
|
|
578
|
-
/**
|
|
579
|
-
* Get current cell position based on scroll position
|
|
580
|
-
* @param position
|
|
581
|
-
* @param scrollPosition
|
|
582
|
-
*/
|
|
583
|
-
const getCellPosition = (
|
|
584
|
-
position: CellPosition,
|
|
585
|
-
scrollPosition: ScrollCoords
|
|
586
|
-
) => {
|
|
587
|
-
if (!currentActiveCellRef.current) return { x: 0, y: 0 };
|
|
588
|
-
return {
|
|
589
|
-
...position,
|
|
590
|
-
x:
|
|
591
|
-
(position.x as number) -
|
|
592
|
-
(isFrozenColumn ? 0 : scrollPosition.scrollLeft),
|
|
593
|
-
y: (position.y as number) - (isFrozenRow ? 0 : scrollPosition.scrollTop),
|
|
594
|
-
};
|
|
595
|
-
};
|
|
596
|
-
|
|
597
|
-
/* Activate edit mode */
|
|
598
|
-
const handleDoubleClick = useCallback(
|
|
599
|
-
(e: React.MouseEvent<HTMLElement>) => {
|
|
600
|
-
if (!gridRef.current) return;
|
|
601
|
-
const coords = gridRef.current.getCellCoordsFromOffset(
|
|
602
|
-
e.nativeEvent.clientX,
|
|
603
|
-
e.nativeEvent.clientY
|
|
604
|
-
);
|
|
605
|
-
if (!coords) return;
|
|
606
|
-
const { rowIndex, columnIndex } = coords;
|
|
607
|
-
makeEditable({ rowIndex, columnIndex });
|
|
608
|
-
},
|
|
609
|
-
[getValue, frozenRows, frozenColumns]
|
|
610
|
-
);
|
|
611
|
-
|
|
612
|
-
const isSelectionKey = useCallback((keyCode: number) => {
|
|
613
|
-
return (
|
|
614
|
-
[
|
|
615
|
-
KeyCodes.Right,
|
|
616
|
-
KeyCodes.Left,
|
|
617
|
-
KeyCodes.Up,
|
|
618
|
-
KeyCodes.Down,
|
|
619
|
-
KeyCodes.Meta,
|
|
620
|
-
KeyCodes.Escape,
|
|
621
|
-
KeyCodes.Tab,
|
|
622
|
-
KeyCodes.Home,
|
|
623
|
-
KeyCodes.End,
|
|
624
|
-
KeyCodes.CapsLock,
|
|
625
|
-
KeyCodes.PageDown,
|
|
626
|
-
KeyCodes.PageUp,
|
|
627
|
-
KeyCodes.ScrollLock,
|
|
628
|
-
KeyCodes.NumLock,
|
|
629
|
-
KeyCodes.Insert,
|
|
630
|
-
KeyCodes.Pause,
|
|
631
|
-
].includes(keyCode) ||
|
|
632
|
-
// Exclude Function keys
|
|
633
|
-
(keyCode >= KeyCodes.F1 && keyCode <= KeyCodes.F12)
|
|
634
|
-
);
|
|
635
|
-
}, []);
|
|
636
|
-
|
|
637
|
-
const handleKeyDown = useCallback(
|
|
638
|
-
(e: React.KeyboardEvent<HTMLElement>) => {
|
|
639
|
-
const keyCode = e.nativeEvent.keyCode;
|
|
640
|
-
if (keyCode === KeyCodes.Tab && !initialActiveCell.current) {
|
|
641
|
-
initialActiveCell.current = activeCell;
|
|
642
|
-
}
|
|
643
|
-
if (isArrowKey(keyCode)) {
|
|
644
|
-
initialActiveCell.current = undefined;
|
|
645
|
-
}
|
|
646
|
-
if (
|
|
647
|
-
isSelectionKey(keyCode) ||
|
|
648
|
-
e.nativeEvent.ctrlKey ||
|
|
649
|
-
(e.nativeEvent.shiftKey &&
|
|
650
|
-
(e.nativeEvent.key === "Shift" ||
|
|
651
|
-
e.nativeEvent.which === KeyCodes.SPACE)) ||
|
|
652
|
-
e.nativeEvent.metaKey ||
|
|
653
|
-
e.nativeEvent.which === KeyCodes.ALT
|
|
654
|
-
)
|
|
655
|
-
return;
|
|
656
|
-
|
|
657
|
-
/* If user has not made any selection yet */
|
|
658
|
-
if (!activeCell) return;
|
|
659
|
-
|
|
660
|
-
const { rowIndex, columnIndex } = activeCell;
|
|
661
|
-
|
|
662
|
-
if (keyCode === KeyCodes.Delete || keyCode === KeyCodes.BackSpace) {
|
|
663
|
-
// TODO: onbefore delete
|
|
664
|
-
onDelete && onDelete(activeCell, selections);
|
|
665
|
-
e.preventDefault();
|
|
666
|
-
return;
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
const initialValue =
|
|
670
|
-
keyCode === KeyCodes.Enter // Enter key
|
|
671
|
-
? undefined
|
|
672
|
-
: e.nativeEvent.key;
|
|
673
|
-
|
|
674
|
-
makeEditable({ rowIndex, columnIndex }, initialValue);
|
|
675
|
-
|
|
676
|
-
/* Prevent the first keystroke */
|
|
677
|
-
e.preventDefault();
|
|
678
|
-
},
|
|
679
|
-
[getValue, selections, activeCell, onDelete]
|
|
680
|
-
);
|
|
681
|
-
|
|
682
|
-
/**
|
|
683
|
-
* Get next focusable cell
|
|
684
|
-
* Respects selection bounds
|
|
685
|
-
*/
|
|
686
|
-
|
|
687
|
-
const nextFocusableCell = useCallback(
|
|
688
|
-
(
|
|
689
|
-
currentCell: CellInterface,
|
|
690
|
-
direction: Direction = Direction.Right
|
|
691
|
-
): CellInterface | null => {
|
|
692
|
-
/* Next immediate cell */
|
|
693
|
-
const bounds = gridRef.current?.getCellBounds(currentCell);
|
|
694
|
-
if (!bounds) return null;
|
|
695
|
-
let nextActiveCell;
|
|
696
|
-
switch (direction) {
|
|
697
|
-
case Direction.Right: {
|
|
698
|
-
let columnIndex = clampIndex(
|
|
699
|
-
Math.min(bounds.right + 1, selectionRightBound),
|
|
700
|
-
isHiddenColumn,
|
|
701
|
-
direction
|
|
702
|
-
);
|
|
703
|
-
nextActiveCell = {
|
|
704
|
-
rowIndex: bounds.top,
|
|
705
|
-
columnIndex,
|
|
706
|
-
};
|
|
707
|
-
break;
|
|
708
|
-
}
|
|
709
|
-
case Direction.Up:
|
|
710
|
-
let rowIndex = clampIndex(
|
|
711
|
-
Math.max(bounds.top - 1, selectionTopBound),
|
|
712
|
-
isHiddenRow,
|
|
713
|
-
direction
|
|
714
|
-
);
|
|
715
|
-
nextActiveCell = {
|
|
716
|
-
rowIndex,
|
|
717
|
-
columnIndex: bounds.left,
|
|
718
|
-
};
|
|
719
|
-
break;
|
|
720
|
-
|
|
721
|
-
case Direction.Left: {
|
|
722
|
-
let columnIndex = clampIndex(
|
|
723
|
-
Math.max(bounds.left - 1, selectionLeftBound),
|
|
724
|
-
isHiddenColumn,
|
|
725
|
-
direction
|
|
726
|
-
);
|
|
727
|
-
nextActiveCell = {
|
|
728
|
-
rowIndex: bounds.top,
|
|
729
|
-
columnIndex,
|
|
730
|
-
};
|
|
731
|
-
break;
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
default: {
|
|
735
|
-
// Down
|
|
736
|
-
let rowIndex = clampIndex(
|
|
737
|
-
Math.min(
|
|
738
|
-
(initialActiveCell.current?.rowIndex ?? bounds.bottom) + 1,
|
|
739
|
-
selectionBottomBound
|
|
740
|
-
),
|
|
741
|
-
isHiddenRow,
|
|
742
|
-
direction
|
|
743
|
-
);
|
|
744
|
-
nextActiveCell = {
|
|
745
|
-
rowIndex,
|
|
746
|
-
columnIndex: initialActiveCell.current?.columnIndex ?? bounds.left,
|
|
747
|
-
};
|
|
748
|
-
break;
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
if (direction === Direction.Right && !initialActiveCell.current) {
|
|
752
|
-
initialActiveCell.current = currentCell;
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
if (direction === Direction.Down) {
|
|
756
|
-
/* Move to the next row + cell */
|
|
757
|
-
initialActiveCell.current = undefined;
|
|
758
|
-
}
|
|
759
|
-
|
|
760
|
-
/* If user has selected some cells and active cell is within this selection */
|
|
761
|
-
if (selections.length && currentCell && gridRef.current) {
|
|
762
|
-
const { bounds } = selections[selections.length - 1];
|
|
763
|
-
const activeCellBounds = gridRef.current.getCellBounds(currentCell);
|
|
764
|
-
const nextCell = findNextCellWithinBounds(
|
|
765
|
-
activeCellBounds,
|
|
766
|
-
bounds,
|
|
767
|
-
direction
|
|
768
|
-
);
|
|
769
|
-
if (nextCell) nextActiveCell = nextCell;
|
|
770
|
-
}
|
|
771
|
-
return nextActiveCell;
|
|
772
|
-
},
|
|
773
|
-
[
|
|
774
|
-
selections,
|
|
775
|
-
isHiddenRow,
|
|
776
|
-
isHiddenColumn,
|
|
777
|
-
selectionBottomBound,
|
|
778
|
-
selectionTopBound,
|
|
779
|
-
]
|
|
780
|
-
);
|
|
781
|
-
|
|
782
|
-
/* Save the value */
|
|
783
|
-
const handleSubmit = useCallback(
|
|
784
|
-
(
|
|
785
|
-
value: string,
|
|
786
|
-
activeCell: CellInterface,
|
|
787
|
-
nextActiveCell?: CellInterface | null
|
|
788
|
-
) => {
|
|
789
|
-
/**
|
|
790
|
-
* Hide the editor first, so that we can handle onBlur events
|
|
791
|
-
* 1. Editor hides -> Submit
|
|
792
|
-
* 2. If user clicks outside the grid, onBlur is called, if there is a activeCell, we do another submit
|
|
793
|
-
*/
|
|
794
|
-
hideEditor();
|
|
795
|
-
|
|
796
|
-
/* Save the new value */
|
|
797
|
-
onSubmit && onSubmit(value, activeCell, nextActiveCell);
|
|
798
|
-
|
|
799
|
-
/* Keep the focus */
|
|
800
|
-
focusGrid();
|
|
801
|
-
},
|
|
802
|
-
[onSubmit]
|
|
803
|
-
);
|
|
804
|
-
|
|
805
|
-
/* When the input is blurred out */
|
|
806
|
-
const handleCancel = useCallback(
|
|
807
|
-
(e?: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
|
808
|
-
hideEditor();
|
|
809
|
-
onCancel && onCancel(e);
|
|
810
|
-
/* Keep the focus back in the grid */
|
|
811
|
-
focusGrid();
|
|
812
|
-
},
|
|
813
|
-
[onCancel]
|
|
814
|
-
);
|
|
815
|
-
|
|
816
|
-
const handleMouseDown = useCallback(
|
|
817
|
-
(e: React.MouseEvent<HTMLDivElement>) => {
|
|
818
|
-
/* Persistent input, hides only during Enter key or during submit or cancel calls */
|
|
819
|
-
if (!hideOnBlur) {
|
|
820
|
-
return;
|
|
821
|
-
}
|
|
822
|
-
if (currentActiveCellRef.current) {
|
|
823
|
-
if (isDirtyRef.current) {
|
|
824
|
-
handleSubmit(currentValueRef.current, currentActiveCellRef.current);
|
|
825
|
-
} else {
|
|
826
|
-
handleCancel();
|
|
827
|
-
}
|
|
828
|
-
}
|
|
829
|
-
initialActiveCell.current = undefined;
|
|
830
|
-
},
|
|
831
|
-
[hideOnBlur, handleSubmit, handleCancel]
|
|
832
|
-
);
|
|
833
|
-
|
|
834
|
-
const handleChange = useCallback(
|
|
835
|
-
(newValue: string, activeCell: CellInterface) => {
|
|
836
|
-
/**
|
|
837
|
-
* Make sure we dont call onChange if initialValue is set
|
|
838
|
-
* This is to accomodate for editor that fire onChange during initialvalue
|
|
839
|
-
* Eg: Slate <Editor value='' onChange />
|
|
840
|
-
*/
|
|
841
|
-
if (
|
|
842
|
-
initialValueRef.current !== void 0 &&
|
|
843
|
-
initialValueRef.current === newValue
|
|
844
|
-
) {
|
|
845
|
-
initialValueRef.current = void 0;
|
|
846
|
-
return;
|
|
847
|
-
}
|
|
848
|
-
if (!currentActiveCellRef.current) return;
|
|
849
|
-
/* Check if the value has changed. Used to conditionally submit if editor is not in focus */
|
|
850
|
-
isDirtyRef.current = newValue !== value;
|
|
851
|
-
setValue(newValue);
|
|
852
|
-
onChange?.(newValue, activeCell);
|
|
853
|
-
},
|
|
854
|
-
[value]
|
|
855
|
-
);
|
|
856
|
-
|
|
857
|
-
const handleScroll = useCallback((scrollPos: ScrollCoords) => {
|
|
858
|
-
if (!currentActiveCellRef.current) return;
|
|
859
|
-
setScrollPosition(scrollPos);
|
|
860
|
-
}, []);
|
|
861
|
-
|
|
862
|
-
/* Editor */
|
|
863
|
-
const editingCell = currentActiveCellRef.current;
|
|
864
|
-
const Editor = useMemo(() => {
|
|
865
|
-
return editingCell
|
|
866
|
-
? getEditor(editingCell) || getDefaultEditor(editingCell)
|
|
867
|
-
: null;
|
|
868
|
-
}, [editingCell]);
|
|
869
|
-
|
|
870
|
-
const handleBlur = useCallback((e: React.FocusEvent) => {
|
|
871
|
-
if (currentActiveCellRef.current) {
|
|
872
|
-
/* Keep the focus */
|
|
873
|
-
focusGrid();
|
|
874
|
-
}
|
|
875
|
-
}, []);
|
|
876
|
-
|
|
877
|
-
const finalCellPosition = useMemo(() => {
|
|
878
|
-
/**
|
|
879
|
-
* Since the editor is sticky,
|
|
880
|
-
* we dont need to adjust the position,
|
|
881
|
-
* as scrollposition wont move the editor
|
|
882
|
-
*
|
|
883
|
-
* When the editor is first active, in makeEditable,
|
|
884
|
-
* we accomodate for the initial scrollPosition
|
|
885
|
-
*/
|
|
886
|
-
if (sticky) {
|
|
887
|
-
return position;
|
|
888
|
-
}
|
|
889
|
-
/**
|
|
890
|
-
* If editor is not sticky, keep adjusting
|
|
891
|
-
* its position to accomodate for scroll
|
|
892
|
-
*/
|
|
893
|
-
return getCellPosition(position, scrollPosition);
|
|
894
|
-
}, [sticky, position, scrollPosition, frozenColumns, frozenRows]);
|
|
895
|
-
|
|
896
|
-
/* Get offset of frozen rows and columns */
|
|
897
|
-
const frozenRowOffset = gridRef.current?.getRowOffset(frozenRows);
|
|
898
|
-
const frozenColumnOffset = gridRef.current?.getColumnOffset(frozenColumns);
|
|
899
|
-
|
|
900
|
-
const editorComponent =
|
|
901
|
-
isEditorShown && Editor ? (
|
|
902
|
-
<Editor
|
|
903
|
-
{...editorProps?.()}
|
|
904
|
-
/* This is the cell that is currently being edited */
|
|
905
|
-
cell={editingCell}
|
|
906
|
-
activeCell={activeCell}
|
|
907
|
-
autoFocus={autoFocus}
|
|
908
|
-
value={value}
|
|
909
|
-
selections={selections}
|
|
910
|
-
onChange={handleChange}
|
|
911
|
-
onSubmit={handleSubmit}
|
|
912
|
-
onCancel={handleCancel}
|
|
913
|
-
position={finalCellPosition}
|
|
914
|
-
scrollPosition={scrollPosition}
|
|
915
|
-
nextFocusableCell={nextFocusableCell}
|
|
916
|
-
onBlur={handleBlur}
|
|
917
|
-
onKeyDown={onKeyDown}
|
|
918
|
-
maxWidth={maxEditorDimensionsRef.current?.width}
|
|
919
|
-
maxHeight={maxEditorDimensionsRef.current?.height}
|
|
920
|
-
isFrozenRow={isFrozenRow}
|
|
921
|
-
isFrozenColumn={isFrozenColumn}
|
|
922
|
-
frozenRowOffset={frozenRowOffset}
|
|
923
|
-
frozenColumnOffset={frozenColumnOffset}
|
|
924
|
-
/>
|
|
925
|
-
) : null;
|
|
926
|
-
|
|
927
|
-
return {
|
|
928
|
-
editorComponent,
|
|
929
|
-
onDoubleClick: handleDoubleClick,
|
|
930
|
-
onKeyDown: handleKeyDown,
|
|
931
|
-
nextFocusableCell,
|
|
932
|
-
isEditInProgress: !!editingCell,
|
|
933
|
-
editingCell,
|
|
934
|
-
makeEditable,
|
|
935
|
-
setValue: handleChange,
|
|
936
|
-
hideEditor,
|
|
937
|
-
showEditor,
|
|
938
|
-
submitEditor: handleSubmit,
|
|
939
|
-
cancelEditor: handleCancel,
|
|
940
|
-
onMouseDown: handleMouseDown,
|
|
941
|
-
onScroll: handleScroll,
|
|
942
|
-
};
|
|
943
|
-
};
|
|
944
|
-
|
|
945
|
-
export default useEditable;
|