pixel-react 1.1.8 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/AddButton/AddButton.d.ts +5 -0
- package/lib/components/AddButton/AddButton.stories.d.ts +6 -0
- package/lib/components/AddButton/index.d.ts +1 -0
- package/lib/components/AddButton/types.d.ts +4 -0
- package/lib/components/AppHeader/types.d.ts +7 -7
- package/lib/components/Drawer/Drawer.stories.d.ts +2 -0
- package/lib/components/Drawer/Types.d.ts +11 -0
- package/lib/components/ExcelFile/ChangeExcelStyles.d.ts +14 -0
- package/lib/components/ExcelFile/ColorBarSelector/ColorBarSelector.d.ts +8 -0
- package/lib/components/ExcelFile/ContextMenu/ContextMenu.d.ts +4 -0
- package/lib/components/ExcelFile/ExcelFile/Excel/ActiveCell.d.ts +7 -0
- package/lib/components/ExcelFile/ExcelFile/Excel/Cell.d.ts +4 -0
- package/lib/components/ExcelFile/ExcelFile/Excel/ColumnIndicator.d.ts +5 -0
- package/lib/components/ExcelFile/ExcelFile/Excel/Copied.d.ts +3 -0
- package/lib/components/ExcelFile/ExcelFile/Excel/CornerIndicator.d.ts +5 -0
- package/lib/components/ExcelFile/ExcelFile/Excel/DataEditor.d.ts +5 -0
- package/lib/components/ExcelFile/ExcelFile/Excel/DataViewer.d.ts +8 -0
- package/lib/components/ExcelFile/ExcelFile/Excel/FloatingRect.d.ts +10 -0
- package/lib/components/ExcelFile/ExcelFile/Excel/HeaderRow.d.ts +3 -0
- package/lib/components/ExcelFile/ExcelFile/Excel/Row.d.ts +3 -0
- package/lib/components/ExcelFile/ExcelFile/Excel/RowIndicator.d.ts +5 -0
- package/lib/components/ExcelFile/ExcelFile/Excel/Selected.d.ts +3 -0
- package/lib/components/ExcelFile/ExcelFile/Excel/Spreadsheet.d.ts +81 -0
- package/lib/components/ExcelFile/ExcelFile/Excel/Table.d.ts +3 -0
- package/lib/components/ExcelFile/ExcelFile/Excel/actions.d.ts +130 -0
- package/lib/components/ExcelFile/ExcelFile/Excel/areModelsEqual.d.ts +1 -0
- package/lib/components/ExcelFile/ExcelFile/Excel/context.d.ts +8 -0
- package/lib/components/ExcelFile/ExcelFile/Excel/engine/engine.d.ts +22 -0
- package/lib/components/ExcelFile/ExcelFile/Excel/engine/formula.d.ts +17 -0
- package/lib/components/ExcelFile/ExcelFile/Excel/engine/index.d.ts +2 -0
- package/lib/components/ExcelFile/ExcelFile/Excel/engine/point-graph.d.ts +21 -0
- package/lib/components/ExcelFile/ExcelFile/Excel/engine/point-hash.d.ts +3 -0
- package/lib/components/ExcelFile/ExcelFile/Excel/engine/point-set.d.ts +24 -0
- package/lib/components/ExcelFile/ExcelFile/Excel/index.d.ts +13 -0
- package/lib/components/ExcelFile/ExcelFile/Excel/matrix.d.ts +67 -0
- package/lib/components/ExcelFile/ExcelFile/Excel/point-range.d.ts +22 -0
- package/lib/components/ExcelFile/ExcelFile/Excel/point.d.ts +11 -0
- package/lib/components/ExcelFile/ExcelFile/Excel/reducer.d.ts +27 -0
- package/lib/components/ExcelFile/ExcelFile/Excel/selection.d.ts +95 -0
- package/lib/components/ExcelFile/ExcelFile/Excel/types.d.ts +215 -0
- package/lib/components/ExcelFile/ExcelFile/Excel/use-dispatch.d.ts +3 -0
- package/lib/components/ExcelFile/ExcelFile/Excel/use-selector.d.ts +3 -0
- package/lib/components/ExcelFile/ExcelFile/Excel/util.d.ts +45 -0
- package/lib/components/ExcelFile/ExcelFile/ExcelFile.d.ts +3 -0
- package/lib/components/ExcelFile/ExcelFile.stories.d.ts +6 -0
- package/lib/components/ExcelFile/ExcelSheetBar/ExcelSheetBar.d.ts +15 -0
- package/lib/components/ExcelFile/ExcelToolBar/ExcelToolBar.d.ts +3 -0
- package/lib/components/ExcelFile/ImportExcelStyles.d.ts +24 -0
- package/lib/components/ExcelFile/Types.d.ts +176 -0
- package/lib/components/ExcelFile/index.d.ts +1 -0
- package/lib/components/Icon/Icon.stories.d.ts +1 -0
- package/lib/components/Icon/types.d.ts +1 -0
- package/lib/components/IconRadioGroup/IconRadioGroup.d.ts +5 -0
- package/lib/components/IconRadioGroup/IconRadioGroup.stories.d.ts +7 -0
- package/lib/components/IconRadioGroup/index.d.ts +1 -0
- package/lib/components/IconRadioGroup/type.d.ts +41 -0
- package/lib/components/Table/Table.d.ts +1 -1
- package/lib/components/Table/Table.stories.d.ts +2 -0
- package/lib/components/Table/Types.d.ts +16 -0
- package/lib/index.d.ts +81 -9
- package/lib/index.esm.js +739 -248
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +739 -247
- package/lib/index.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/utils/find/findAndInsert.d.ts +7 -0
- package/lib/utils/find/findAndInsert.stories.d.ts +7 -0
- package/package.json +4 -2
- package/src/assets/Themes/BaseTheme.scss +4 -0
- package/src/assets/Themes/DarkTheme.scss +4 -0
- package/src/assets/icons/all_borders.svg +3 -0
- package/src/assets/icons/bold.svg +3 -0
- package/src/assets/icons/border_bottom.svg +3 -0
- package/src/assets/icons/border_left.svg +3 -0
- package/src/assets/icons/border_right.svg +3 -0
- package/src/assets/icons/border_top.svg +3 -0
- package/src/assets/icons/clone_icon.svg +3 -0
- package/src/assets/icons/double_underline.svg +5 -0
- package/src/assets/icons/eye_closed.svg +3 -0
- package/src/assets/icons/fill_color.svg +7 -0
- package/src/assets/icons/formate_painter.svg +5 -0
- package/src/assets/icons/full_access_icon.svg +4 -0
- package/src/assets/icons/history_icon.svg +19 -0
- package/src/assets/icons/italic.svg +3 -0
- package/src/assets/icons/jira.svg +3 -0
- package/src/assets/icons/linked_defects.svg +11 -0
- package/src/assets/icons/move_icon.svg +5 -0
- package/src/assets/icons/no_access_icon.svg +4 -0
- package/src/assets/icons/no_border.svg +3 -0
- package/src/assets/icons/strike_through.svg +3 -0
- package/src/assets/icons/text_align_center.svg +3 -0
- package/src/assets/icons/text_align_left.svg +3 -0
- package/src/assets/icons/text_align_right.svg +3 -0
- package/src/assets/icons/text_color.svg +3 -0
- package/src/assets/icons/underline.svg +4 -0
- package/src/assets/icons/view_access_icon.svg +4 -0
- package/src/components/AppHeader/AppHeader.scss +23 -10
- package/src/components/AppHeader/AppHeader.stories.tsx +29 -28
- package/src/components/AppHeader/AppHeader.tsx +18 -16
- package/src/components/AppHeader/types.ts +7 -7
- package/src/components/Button/Button.scss +1 -0
- package/src/components/Checkbox/Checkbox.tsx +1 -1
- package/src/components/Drawer/Drawer.scss +13 -9
- package/src/components/Drawer/Drawer.stories.tsx +28 -0
- package/src/components/Drawer/Drawer.tsx +29 -6
- package/src/components/Drawer/Types.ts +11 -0
- package/src/components/ExcelFile/ChangeExcelStyles.tsx +78 -0
- package/src/components/ExcelFile/ColorBarselector/ColorBarSelector.scss +13 -0
- package/src/components/ExcelFile/ColorBarselector/ColorBarSelector.tsx +43 -0
- package/src/components/ExcelFile/ContextMenu/ContextMenu.scss +102 -0
- package/src/components/ExcelFile/ContextMenu/ContextMenu.tsx +104 -0
- package/src/components/ExcelFile/ExcelFile/Excel/ActiveCell.tsx +131 -0
- package/src/components/ExcelFile/ExcelFile/Excel/Cell.tsx +201 -0
- package/src/components/ExcelFile/ExcelFile/Excel/ColumnIndicator.tsx +123 -0
- package/src/components/ExcelFile/ExcelFile/Excel/Copied.tsx +25 -0
- package/src/components/ExcelFile/ExcelFile/Excel/CornerIndicator.tsx +49 -0
- package/src/components/ExcelFile/ExcelFile/Excel/DataEditor.tsx +37 -0
- package/src/components/ExcelFile/ExcelFile/Excel/DataViewer.tsx +46 -0
- package/src/components/ExcelFile/ExcelFile/Excel/FloatingRect.tsx +31 -0
- package/src/components/ExcelFile/ExcelFile/Excel/HeaderRow.tsx +5 -0
- package/src/components/ExcelFile/ExcelFile/Excel/Row.tsx +5 -0
- package/src/components/ExcelFile/ExcelFile/Excel/RowIndicator.tsx +102 -0
- package/src/components/ExcelFile/ExcelFile/Excel/Selected.tsx +32 -0
- package/src/components/ExcelFile/ExcelFile/Excel/Spreadsheet.css +144 -0
- package/src/components/ExcelFile/ExcelFile/Excel/Spreadsheet.tsx +494 -0
- package/src/components/ExcelFile/ExcelFile/Excel/Table.tsx +19 -0
- package/src/components/ExcelFile/ExcelFile/Excel/actions.ts +302 -0
- package/src/components/ExcelFile/ExcelFile/Excel/areModelsEqual.ts +18 -0
- package/src/components/ExcelFile/ExcelFile/Excel/context.ts +12 -0
- package/src/components/ExcelFile/ExcelFile/Excel/engine/engine.ts +200 -0
- package/src/components/ExcelFile/ExcelFile/Excel/engine/formula.ts +137 -0
- package/src/components/ExcelFile/ExcelFile/Excel/engine/index.ts +2 -0
- package/src/components/ExcelFile/ExcelFile/Excel/engine/point-graph.ts +154 -0
- package/src/components/ExcelFile/ExcelFile/Excel/engine/point-hash.ts +10 -0
- package/src/components/ExcelFile/ExcelFile/Excel/engine/point-set.ts +69 -0
- package/src/components/ExcelFile/ExcelFile/Excel/index.ts +48 -0
- package/src/components/ExcelFile/ExcelFile/Excel/matrix.ts +388 -0
- package/src/components/ExcelFile/ExcelFile/Excel/point-range.ts +82 -0
- package/src/components/ExcelFile/ExcelFile/Excel/point.ts +15 -0
- package/src/components/ExcelFile/ExcelFile/Excel/reducer.ts +682 -0
- package/src/components/ExcelFile/ExcelFile/Excel/selection.ts +257 -0
- package/src/components/ExcelFile/ExcelFile/Excel/types.ts +269 -0
- package/src/components/ExcelFile/ExcelFile/Excel/typings/fast-formula-parser.d.ts +58 -0
- package/src/components/ExcelFile/ExcelFile/Excel/use-dispatch.ts +8 -0
- package/src/components/ExcelFile/ExcelFile/Excel/use-selector.ts +9 -0
- package/src/components/ExcelFile/ExcelFile/Excel/util.ts +173 -0
- package/src/components/ExcelFile/ExcelFile/ExcelFile.scss +27 -0
- package/src/components/ExcelFile/ExcelFile/ExcelFile.tsx +520 -0
- package/src/components/ExcelFile/ExcelFile.stories.tsx +132 -0
- package/src/components/ExcelFile/ExcelSheetBar/ExcelSheetBar.scss +16 -0
- package/src/components/ExcelFile/ExcelSheetBar/ExcelSheetBar.tsx +79 -0
- package/src/components/ExcelFile/ExcelToolBar/ExcelToolBar.scss +22 -0
- package/src/components/ExcelFile/ExcelToolBar/ExcelToolBar.tsx +271 -0
- package/src/components/ExcelFile/ImportExcelStyles.tsx +86 -0
- package/src/components/ExcelFile/Types.ts +241 -0
- package/src/components/ExcelFile/index.ts +1 -0
- package/src/components/Icon/Icon.stories.tsx +27 -0
- package/src/components/Icon/Icon.tsx +5 -1
- package/src/components/Icon/Icons.scss +15 -5
- package/src/components/Icon/iconList.ts +52 -1
- package/src/components/Icon/types.ts +1 -0
- package/src/components/IconRadioGroup/IconRadioGroup.scss +60 -0
- package/src/components/IconRadioGroup/IconRadioGroup.stories.tsx +108 -0
- package/src/components/IconRadioGroup/IconRadioGroup.tsx +72 -0
- package/src/components/IconRadioGroup/index.ts +1 -0
- package/src/components/IconRadioGroup/type.ts +50 -0
- package/src/components/Modal/Modal.tsx +8 -1
- package/src/components/Modal/modal.scss +10 -2
- package/src/components/Table/Table.scss +16 -4
- package/src/components/Table/Table.stories.tsx +36 -12
- package/src/components/Table/Table.tsx +33 -16
- package/src/components/Table/Types.ts +121 -105
- package/src/index.ts +2 -0
@@ -0,0 +1,494 @@
|
|
1
|
+
import * as React from "react";
|
2
|
+
import classNames from "classnames";
|
3
|
+
import * as Types from "./types";
|
4
|
+
import * as Actions from "./actions";
|
5
|
+
import * as Matrix from "./matrix";
|
6
|
+
import * as Point from "./point";
|
7
|
+
import { Selection } from "./selection";
|
8
|
+
import reducer, { INITIAL_STATE, hasKeyDownHandler } from "./reducer";
|
9
|
+
import context from "./context";
|
10
|
+
import { Model, createFormulaParser } from "./engine";
|
11
|
+
import {
|
12
|
+
range,
|
13
|
+
readTextFromClipboard,
|
14
|
+
writeTextToClipboard,
|
15
|
+
calculateSpreadsheetSize,
|
16
|
+
getCSV,
|
17
|
+
shouldHandleClipboardEvent,
|
18
|
+
isFocusedWithin,
|
19
|
+
} from "./util";
|
20
|
+
|
21
|
+
import DefaultTable from "./Table";
|
22
|
+
import DefaultRow from "./Row";
|
23
|
+
import DefaultHeaderRow from "./HeaderRow";
|
24
|
+
import DefaultCornerIndicator, {
|
25
|
+
enhance as enhanceCornerIndicator,
|
26
|
+
} from "./CornerIndicator";
|
27
|
+
import DefaultColumnIndicator, {
|
28
|
+
enhance as enhanceColumnIndicator,
|
29
|
+
} from "./ColumnIndicator";
|
30
|
+
import DefaultRowIndicator, {
|
31
|
+
enhance as enhanceRowIndicator,
|
32
|
+
} from "./RowIndicator";
|
33
|
+
import { Cell as DefaultCell, enhance as enhanceCell } from "./Cell";
|
34
|
+
import DefaultDataViewer from "./DataViewer";
|
35
|
+
import DefaultDataEditor from "./DataEditor";
|
36
|
+
import ActiveCell from "./ActiveCell";
|
37
|
+
import Selected from "./Selected";
|
38
|
+
import Copied from "./Copied";
|
39
|
+
import "./Spreadsheet.css";
|
40
|
+
|
41
|
+
/** The Spreadsheet component props */
|
42
|
+
export type Props<CellType extends Types.CellBase> = {
|
43
|
+
/** The spreadsheet's data */
|
44
|
+
data: Matrix.Matrix<CellType>;
|
45
|
+
/** Class name to be added to the spreadsheet's root element */
|
46
|
+
className?: string;
|
47
|
+
/**
|
48
|
+
* Use dark colors that complement dark mode
|
49
|
+
* @defaultValue `false`
|
50
|
+
*/
|
51
|
+
darkMode?: boolean;
|
52
|
+
/**
|
53
|
+
* Function used to create the formula parser (instance of
|
54
|
+
* "fast-formula-parser") used by the Spreadsheet by getting the spreadsheet's
|
55
|
+
* data.
|
56
|
+
* @defaultValue function which creates a formula parser bound to the
|
57
|
+
* Spreadsheet's data.
|
58
|
+
* @see `createFormulaParser`
|
59
|
+
* @see https://www.npmjs.com/package/fast-formula-parser
|
60
|
+
*/
|
61
|
+
createFormulaParser?: Types.CreateFormulaParser;
|
62
|
+
/**
|
63
|
+
* Labels to use in column indicators.
|
64
|
+
* @defaultValue alphabetical labels.
|
65
|
+
*/
|
66
|
+
columnLabels?: string[];
|
67
|
+
/**
|
68
|
+
* Labels to use in row indicators.
|
69
|
+
* @defaultValue row index labels.
|
70
|
+
*/
|
71
|
+
rowLabels?: string[];
|
72
|
+
/**
|
73
|
+
* If set to true, hides the row indicators of the spreadsheet.
|
74
|
+
* @defaultValue `false`.
|
75
|
+
*/
|
76
|
+
hideRowIndicators?: boolean;
|
77
|
+
/**
|
78
|
+
* If set to true, hides the column indicators of the spreadsheet.
|
79
|
+
* @defaultValue `false`.
|
80
|
+
*/
|
81
|
+
hideColumnIndicators?: boolean;
|
82
|
+
/** The selected cells in the worksheet. */
|
83
|
+
selected?: Selection;
|
84
|
+
// Custom Components
|
85
|
+
/** Component rendered above each column. */
|
86
|
+
ColumnIndicator?: Types.ColumnIndicatorComponent;
|
87
|
+
/** Component rendered in the corner of row and column indicators. */
|
88
|
+
CornerIndicator?: Types.CornerIndicatorComponent;
|
89
|
+
/** Component rendered next to each row. */
|
90
|
+
RowIndicator?: Types.RowIndicatorComponent;
|
91
|
+
/** The Spreadsheet's table component. */
|
92
|
+
Table?: Types.TableComponent;
|
93
|
+
/** The Spreadsheet's row component. */
|
94
|
+
Row?: Types.RowComponent;
|
95
|
+
/** The spreadsheet's header row component */
|
96
|
+
HeaderRow?: Types.HeaderRowComponent;
|
97
|
+
/** The Spreadsheet's cell component. */
|
98
|
+
Cell?: Types.CellComponent<CellType>;
|
99
|
+
/** Component rendered for cells in view mode. */
|
100
|
+
DataViewer?: Types.DataViewerComponent<CellType>;
|
101
|
+
/** Component rendered for cells in edit mode. */
|
102
|
+
DataEditor?: Types.DataEditorComponent<CellType>;
|
103
|
+
// Handlers
|
104
|
+
/** Callback called on key down inside the spreadsheet. */
|
105
|
+
onKeyDown?: (event: React.KeyboardEvent) => void;
|
106
|
+
/** Callback called when the Spreadsheet's selection changes. */
|
107
|
+
onSelect?: (selected: Selection) => void;
|
108
|
+
/** Callback called when Spreadsheet's active cell changes. */
|
109
|
+
onActivate?: (active: Point.Point) => void;
|
110
|
+
/** Callback called when the Spreadsheet's evaluated data changes. */
|
111
|
+
onEvaluatedDataChange?: (data: Matrix.Matrix<CellType>) => void;
|
112
|
+
};
|
113
|
+
|
114
|
+
/**
|
115
|
+
* The Spreadsheet component
|
116
|
+
*/
|
117
|
+
const Spreadsheet = <CellType extends Types.CellBase>(
|
118
|
+
props: Props<CellType>
|
119
|
+
): React.ReactElement => {
|
120
|
+
const {
|
121
|
+
className,
|
122
|
+
darkMode,
|
123
|
+
columnLabels,
|
124
|
+
rowLabels,
|
125
|
+
// hideColumnIndicators,
|
126
|
+
// hideRowIndicators,
|
127
|
+
onKeyDown,
|
128
|
+
Table = DefaultTable,
|
129
|
+
Row = DefaultRow,
|
130
|
+
HeaderRow = DefaultHeaderRow,
|
131
|
+
DataEditor = DefaultDataEditor,
|
132
|
+
DataViewer = DefaultDataViewer,
|
133
|
+
// onChange = () => {},
|
134
|
+
// onModeChange = () => {},
|
135
|
+
onSelect = () => {},
|
136
|
+
onActivate = () => {},
|
137
|
+
// onBlur = () => {},
|
138
|
+
// onCellCommit = () => {},
|
139
|
+
onEvaluatedDataChange = () => {},
|
140
|
+
} = props;
|
141
|
+
type State = Types.StoreState<CellType>;
|
142
|
+
|
143
|
+
const initialState = React.useMemo(() => {
|
144
|
+
const createParser = (props.createFormulaParser ||
|
145
|
+
createFormulaParser) as Types.CreateFormulaParser;
|
146
|
+
const model = new Model(createParser, props.data);
|
147
|
+
return {
|
148
|
+
...INITIAL_STATE,
|
149
|
+
model,
|
150
|
+
selected: props.selected || INITIAL_STATE.selected,
|
151
|
+
} as State;
|
152
|
+
}, [props.createFormulaParser, props.data, props.selected]);
|
153
|
+
|
154
|
+
const reducerElements = React.useReducer(
|
155
|
+
reducer as unknown as React.Reducer<State, Actions.Action>,
|
156
|
+
initialState
|
157
|
+
);
|
158
|
+
const [state, dispatch] = reducerElements;
|
159
|
+
|
160
|
+
const size = React.useMemo(() => {
|
161
|
+
return calculateSpreadsheetSize(state.model.data, rowLabels, columnLabels);
|
162
|
+
}, [state.model.data, rowLabels, columnLabels]);
|
163
|
+
|
164
|
+
const mode = state.mode;
|
165
|
+
|
166
|
+
const rootRef = React.useRef<HTMLDivElement>(null);
|
167
|
+
|
168
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
169
|
+
const useAction = <T extends (...args: any[]) => Actions.Action>(
|
170
|
+
action: T
|
171
|
+
) => {
|
172
|
+
return React.useCallback(
|
173
|
+
(...args: Parameters<T>) => dispatch(action(...args)),
|
174
|
+
[action]
|
175
|
+
);
|
176
|
+
};
|
177
|
+
|
178
|
+
const cut = useAction(Actions.cut);
|
179
|
+
const copy = useAction(Actions.copy);
|
180
|
+
const paste = useAction(Actions.paste);
|
181
|
+
const onKeyDownAction = useAction(Actions.keyDown);
|
182
|
+
const onKeyPress = useAction(Actions.keyPress);
|
183
|
+
const onDragStart = useAction(Actions.dragStart);
|
184
|
+
const onDragEnd = useAction(Actions.dragEnd);
|
185
|
+
const setData = useAction(Actions.setData);
|
186
|
+
const setCreateFormulaParser = useAction(Actions.setCreateFormulaParser);
|
187
|
+
const setSelection = useAction(Actions.setSelection);
|
188
|
+
|
189
|
+
// Track active
|
190
|
+
const prevActiveRef = React.useRef<Point.Point | null>(state.active);
|
191
|
+
React.useEffect(() => {
|
192
|
+
if (state.active !== prevActiveRef.current) {
|
193
|
+
if (state.active) {
|
194
|
+
onActivate(state.active);
|
195
|
+
} else {
|
196
|
+
const root = rootRef.current;
|
197
|
+
if (root && isFocusedWithin(root) && document.activeElement) {
|
198
|
+
(document.activeElement as HTMLElement).blur();
|
199
|
+
}
|
200
|
+
}
|
201
|
+
}
|
202
|
+
|
203
|
+
prevActiveRef.current = state.active;
|
204
|
+
}, [onActivate, state.active]);
|
205
|
+
|
206
|
+
const prevEvaluatedDataRef = React.useRef<Matrix.Matrix<CellType>>(
|
207
|
+
state.model.evaluatedData
|
208
|
+
);
|
209
|
+
React.useEffect(() => {
|
210
|
+
if (state?.model?.evaluatedData !== prevEvaluatedDataRef?.current) {
|
211
|
+
onEvaluatedDataChange(state?.model?.evaluatedData);
|
212
|
+
}
|
213
|
+
|
214
|
+
prevEvaluatedDataRef.current = state.model.evaluatedData;
|
215
|
+
}, [state?.model?.evaluatedData, onEvaluatedDataChange]);
|
216
|
+
|
217
|
+
// Listen to selection changes
|
218
|
+
const prevSelectedRef = React.useRef<Selection>(state.selected);
|
219
|
+
React.useEffect(() => {
|
220
|
+
if (!state.selected.equals(prevSelectedRef.current)) {
|
221
|
+
// Call on select only if the selection change internal
|
222
|
+
if (!props.selected || !state.selected.equals(props.selected)) {
|
223
|
+
onSelect(state.selected);
|
224
|
+
}
|
225
|
+
}
|
226
|
+
|
227
|
+
prevSelectedRef.current = state.selected;
|
228
|
+
}, [state.selected, onSelect, props.selected]);
|
229
|
+
|
230
|
+
// Update selection when props.selected changes
|
231
|
+
const prevSelectedPropRef = React.useRef<Selection | undefined>(
|
232
|
+
props.selected
|
233
|
+
);
|
234
|
+
React.useEffect(() => {
|
235
|
+
if (
|
236
|
+
props.selected &&
|
237
|
+
prevSelectedPropRef.current &&
|
238
|
+
!props.selected.equals(prevSelectedPropRef.current)
|
239
|
+
) {
|
240
|
+
setSelection(props.selected);
|
241
|
+
}
|
242
|
+
prevSelectedPropRef.current = props.selected;
|
243
|
+
}, [props.selected, setSelection]);
|
244
|
+
|
245
|
+
// Update data when props.data changes
|
246
|
+
const prevDataPropRef = React.useRef<Matrix.Matrix<CellType> | undefined>(
|
247
|
+
props.data
|
248
|
+
);
|
249
|
+
React.useEffect(() => {
|
250
|
+
if (props.data !== prevDataPropRef.current) {
|
251
|
+
setData(props.data);
|
252
|
+
}
|
253
|
+
prevDataPropRef.current = props.data;
|
254
|
+
}, [props.data, setData]);
|
255
|
+
|
256
|
+
// Update createFormulaParser when props.createFormulaParser changes
|
257
|
+
const prevCreateFormulaParserPropRef = React.useRef<
|
258
|
+
Types.CreateFormulaParser | undefined
|
259
|
+
>(props.createFormulaParser);
|
260
|
+
React.useEffect(() => {
|
261
|
+
if (
|
262
|
+
props.createFormulaParser !== prevCreateFormulaParserPropRef.current &&
|
263
|
+
props.createFormulaParser
|
264
|
+
)
|
265
|
+
setCreateFormulaParser(props.createFormulaParser);
|
266
|
+
prevCreateFormulaParserPropRef.current = props.createFormulaParser;
|
267
|
+
}, [props.createFormulaParser, setCreateFormulaParser]);
|
268
|
+
|
269
|
+
const writeDataToClipboard = React.useCallback(
|
270
|
+
(event: ClipboardEvent): void => {
|
271
|
+
const { model, selected } = state;
|
272
|
+
const { data } = model;
|
273
|
+
const range = selected.toRange(data);
|
274
|
+
if (range) {
|
275
|
+
const selectedData = Matrix.slice(range.start, range.end, data);
|
276
|
+
const csv = getCSV(selectedData);
|
277
|
+
writeTextToClipboard(event, csv);
|
278
|
+
}
|
279
|
+
},
|
280
|
+
[state]
|
281
|
+
);
|
282
|
+
|
283
|
+
const handleCut = React.useCallback(
|
284
|
+
(event: ClipboardEvent) => {
|
285
|
+
if (shouldHandleClipboardEvent(rootRef.current, mode)) {
|
286
|
+
event.preventDefault();
|
287
|
+
event.stopPropagation();
|
288
|
+
writeDataToClipboard(event);
|
289
|
+
cut();
|
290
|
+
}
|
291
|
+
},
|
292
|
+
[mode, writeDataToClipboard, cut]
|
293
|
+
);
|
294
|
+
|
295
|
+
const handleCopy = React.useCallback(
|
296
|
+
(event: ClipboardEvent) => {
|
297
|
+
if (shouldHandleClipboardEvent(rootRef.current, mode)) {
|
298
|
+
event.preventDefault();
|
299
|
+
event.stopPropagation();
|
300
|
+
writeDataToClipboard(event);
|
301
|
+
copy();
|
302
|
+
}
|
303
|
+
},
|
304
|
+
[mode, writeDataToClipboard, copy]
|
305
|
+
);
|
306
|
+
|
307
|
+
const handlePaste = React.useCallback(
|
308
|
+
(event: ClipboardEvent) => {
|
309
|
+
if (shouldHandleClipboardEvent(rootRef.current, mode)) {
|
310
|
+
event.preventDefault();
|
311
|
+
event.stopPropagation();
|
312
|
+
if (event.clipboardData) {
|
313
|
+
const text = readTextFromClipboard(event);
|
314
|
+
paste(text);
|
315
|
+
}
|
316
|
+
}
|
317
|
+
},
|
318
|
+
[mode, paste]
|
319
|
+
);
|
320
|
+
|
321
|
+
const handleKeyDown = React.useCallback(
|
322
|
+
(event: React.KeyboardEvent) => {
|
323
|
+
event.persist();
|
324
|
+
if (onKeyDown) {
|
325
|
+
onKeyDown(event);
|
326
|
+
}
|
327
|
+
// Do not use event in case preventDefault() was called inside onKeyDown
|
328
|
+
if (!event.defaultPrevented) {
|
329
|
+
// Only disable default behavior if an handler exist
|
330
|
+
if (hasKeyDownHandler(state, event)) {
|
331
|
+
event.nativeEvent.preventDefault();
|
332
|
+
}
|
333
|
+
onKeyDownAction(event);
|
334
|
+
}
|
335
|
+
},
|
336
|
+
[state, onKeyDown, onKeyDownAction]
|
337
|
+
);
|
338
|
+
|
339
|
+
const handleMouseUp = React.useCallback(() => {
|
340
|
+
onDragEnd();
|
341
|
+
document.removeEventListener("mouseup", handleMouseUp);
|
342
|
+
}, [onDragEnd]);
|
343
|
+
|
344
|
+
const handleMouseMove = React.useCallback(
|
345
|
+
(event: React.MouseEvent) => {
|
346
|
+
if (!state.dragging && event.buttons === 1) {
|
347
|
+
onDragStart();
|
348
|
+
document.addEventListener("mouseup", handleMouseUp);
|
349
|
+
}
|
350
|
+
},
|
351
|
+
[state, onDragStart, handleMouseUp]
|
352
|
+
);
|
353
|
+
|
354
|
+
const Cell = React.useMemo(() => {
|
355
|
+
// @ts-ignore
|
356
|
+
return enhanceCell(props.Cell || DefaultCell);
|
357
|
+
}, [props.Cell]);
|
358
|
+
|
359
|
+
const CornerIndicator = React.useMemo(
|
360
|
+
() =>
|
361
|
+
enhanceCornerIndicator(props.CornerIndicator || DefaultCornerIndicator),
|
362
|
+
[props.CornerIndicator]
|
363
|
+
);
|
364
|
+
|
365
|
+
const RowIndicator = React.useMemo(
|
366
|
+
() => enhanceRowIndicator(props.RowIndicator || DefaultRowIndicator),
|
367
|
+
[props.RowIndicator]
|
368
|
+
);
|
369
|
+
|
370
|
+
const ColumnIndicator = React.useMemo(
|
371
|
+
() =>
|
372
|
+
enhanceColumnIndicator(props.ColumnIndicator || DefaultColumnIndicator),
|
373
|
+
[props.ColumnIndicator]
|
374
|
+
);
|
375
|
+
|
376
|
+
React.useEffect(() => {
|
377
|
+
document.addEventListener("cut", handleCut);
|
378
|
+
document.addEventListener("copy", handleCopy);
|
379
|
+
document.addEventListener("paste", handlePaste);
|
380
|
+
|
381
|
+
return () => {
|
382
|
+
document.removeEventListener("cut", handleCut);
|
383
|
+
document.removeEventListener("copy", handleCopy);
|
384
|
+
document.removeEventListener("paste", handlePaste);
|
385
|
+
};
|
386
|
+
}, [handleCut, handleCopy, handlePaste]);
|
387
|
+
|
388
|
+
const tableNode = React.useMemo(
|
389
|
+
() => (
|
390
|
+
<Table columns={size.columns}>
|
391
|
+
<HeaderRow>
|
392
|
+
{<CornerIndicator />}
|
393
|
+
{
|
394
|
+
range(size.columns).map((columnNumber) =>
|
395
|
+
columnLabels ? (
|
396
|
+
<ColumnIndicator
|
397
|
+
key={columnNumber}
|
398
|
+
column={columnNumber}
|
399
|
+
label={
|
400
|
+
columnNumber in columnLabels
|
401
|
+
? columnLabels[columnNumber]
|
402
|
+
: null
|
403
|
+
}
|
404
|
+
/>
|
405
|
+
) : (
|
406
|
+
<ColumnIndicator key={columnNumber} column={columnNumber} />
|
407
|
+
)
|
408
|
+
)}
|
409
|
+
</HeaderRow>
|
410
|
+
{range(size.rows).map((rowNumber) => (
|
411
|
+
<Row key={rowNumber} row={rowNumber}>
|
412
|
+
{
|
413
|
+
(rowLabels ? (
|
414
|
+
<RowIndicator
|
415
|
+
key={rowNumber}
|
416
|
+
row={rowNumber}
|
417
|
+
label={rowNumber in rowLabels ? rowLabels[rowNumber] : null}
|
418
|
+
/>
|
419
|
+
) : (
|
420
|
+
<RowIndicator key={rowNumber} row={rowNumber} />
|
421
|
+
))}
|
422
|
+
{range(size.columns).map((columnNumber) => (
|
423
|
+
<Cell
|
424
|
+
key={columnNumber}
|
425
|
+
row={rowNumber}
|
426
|
+
column={columnNumber}
|
427
|
+
// @ts-ignore
|
428
|
+
DataViewer={DataViewer}
|
429
|
+
/>
|
430
|
+
))}
|
431
|
+
</Row>
|
432
|
+
))}
|
433
|
+
</Table>
|
434
|
+
),
|
435
|
+
[
|
436
|
+
Table,
|
437
|
+
size.rows,
|
438
|
+
size.columns,
|
439
|
+
Row,
|
440
|
+
HeaderRow,
|
441
|
+
CornerIndicator,
|
442
|
+
columnLabels,
|
443
|
+
ColumnIndicator,
|
444
|
+
rowLabels,
|
445
|
+
RowIndicator,
|
446
|
+
Cell,
|
447
|
+
DataViewer,
|
448
|
+
]
|
449
|
+
);
|
450
|
+
|
451
|
+
const activeCellNode = React.useMemo(
|
452
|
+
() => (
|
453
|
+
<ActiveCell
|
454
|
+
// @ts-ignore
|
455
|
+
DataEditor={DataEditor}
|
456
|
+
/>
|
457
|
+
),
|
458
|
+
[DataEditor]
|
459
|
+
);
|
460
|
+
|
461
|
+
const rootNode = React.useMemo(
|
462
|
+
() => (
|
463
|
+
<div
|
464
|
+
ref={rootRef}
|
465
|
+
className={classNames("Spreadsheet", className, {
|
466
|
+
"Spreadsheet--dark-mode": darkMode,
|
467
|
+
})}
|
468
|
+
onKeyPress={onKeyPress}
|
469
|
+
onKeyDown={handleKeyDown}
|
470
|
+
onMouseMove={handleMouseMove}
|
471
|
+
>
|
472
|
+
{tableNode}
|
473
|
+
{activeCellNode}
|
474
|
+
<Selected />
|
475
|
+
<Copied />
|
476
|
+
</div>
|
477
|
+
),
|
478
|
+
[
|
479
|
+
className,
|
480
|
+
darkMode,
|
481
|
+
onKeyPress,
|
482
|
+
handleKeyDown,
|
483
|
+
handleMouseMove,
|
484
|
+
tableNode,
|
485
|
+
activeCellNode,
|
486
|
+
]
|
487
|
+
);
|
488
|
+
|
489
|
+
return (
|
490
|
+
<context.Provider value={reducerElements}>{rootNode}</context.Provider>
|
491
|
+
);
|
492
|
+
};
|
493
|
+
|
494
|
+
export default Spreadsheet;
|
@@ -0,0 +1,19 @@
|
|
1
|
+
import * as Types from "./types";
|
2
|
+
import { range } from "./util";
|
3
|
+
|
4
|
+
const Table: Types.TableComponent = ({
|
5
|
+
children,
|
6
|
+
columns,
|
7
|
+
hideColumnIndicators,
|
8
|
+
}) => {
|
9
|
+
const columnCount = columns + (hideColumnIndicators ? 0 : 1);
|
10
|
+
const columnNodes = range(columnCount).map((i) => <col key={i} />);
|
11
|
+
return (
|
12
|
+
<table className="Spreadsheet__table">
|
13
|
+
<colgroup>{columnNodes}</colgroup>
|
14
|
+
<tbody>{children}</tbody>
|
15
|
+
</table>
|
16
|
+
);
|
17
|
+
};
|
18
|
+
|
19
|
+
export default Table;
|