@deephaven/grid 0.43.0 → 0.44.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/dist/CellInputField.css +23 -0
- package/dist/CellInputField.css.map +1 -0
- package/dist/CellInputField.js +174 -0
- package/dist/CellInputField.js.map +1 -0
- package/dist/CellRenderer.js +64 -0
- package/dist/CellRenderer.js.map +1 -0
- package/dist/ColumnHeaderGroup.js +2 -0
- package/dist/ColumnHeaderGroup.js.map +1 -0
- package/dist/DataBarCellRenderer.js +404 -0
- package/dist/DataBarCellRenderer.js.map +1 -0
- package/dist/DataBarGridModel.js +27 -0
- package/dist/DataBarGridModel.js.map +1 -0
- package/dist/EditableGridModel.js +14 -0
- package/dist/EditableGridModel.js.map +1 -0
- package/dist/EventHandlerResult.js +2 -0
- package/dist/EventHandlerResult.js.map +1 -0
- package/dist/ExpandableGridModel.js +8 -0
- package/dist/ExpandableGridModel.js.map +1 -0
- package/dist/Grid.css +45 -0
- package/dist/Grid.css.map +1 -0
- package/dist/Grid.js +1947 -0
- package/dist/Grid.js.map +1 -0
- package/dist/GridAxisRange.js +17 -0
- package/dist/GridAxisRange.js.map +1 -0
- package/dist/GridColorUtils.js +146 -0
- package/dist/GridColorUtils.js.map +1 -0
- package/dist/GridMetricCalculator.js +1500 -0
- package/dist/GridMetricCalculator.js.map +1 -0
- package/dist/GridMetrics.js +2 -0
- package/dist/GridMetrics.js.map +1 -0
- package/dist/GridModel.js +193 -0
- package/dist/GridModel.js.map +1 -0
- package/dist/GridMouseHandler.js +57 -0
- package/dist/GridMouseHandler.js.map +1 -0
- package/dist/GridRange.js +684 -0
- package/dist/GridRange.js.map +1 -0
- package/dist/GridRenderer.js +2038 -0
- package/dist/GridRenderer.js.map +1 -0
- package/dist/GridRendererTypes.js +3 -0
- package/dist/GridRendererTypes.js.map +1 -0
- package/dist/GridTestUtils.js +16 -0
- package/dist/GridTestUtils.js.map +1 -0
- package/dist/GridTheme.js +100 -0
- package/dist/GridTheme.js.map +1 -0
- package/dist/GridUtils.js +1198 -0
- package/dist/GridUtils.js.map +1 -0
- package/dist/KeyHandler.js +36 -0
- package/dist/KeyHandler.js.map +1 -0
- package/dist/MockDataBarGridModel.js +119 -0
- package/dist/MockDataBarGridModel.js.map +1 -0
- package/dist/MockGridData.js +5 -0
- package/dist/MockGridData.js.map +1 -0
- package/dist/MockGridModel.js +122 -0
- package/dist/MockGridModel.js.map +1 -0
- package/dist/MockTreeGridModel.js +193 -0
- package/dist/MockTreeGridModel.js.map +1 -0
- package/dist/StaticDataGridModel.js +40 -0
- package/dist/StaticDataGridModel.js.map +1 -0
- package/dist/TextCellRenderer.js +210 -0
- package/dist/TextCellRenderer.js.map +1 -0
- package/dist/ThemeContext.js +4 -0
- package/dist/ThemeContext.js.map +1 -0
- package/dist/TokenBoxCellRenderer.js +4 -0
- package/dist/TokenBoxCellRenderer.js.map +1 -0
- package/dist/ViewportDataGridModel.js +43 -0
- package/dist/ViewportDataGridModel.js.map +1 -0
- package/dist/errors/AssertionError.js +11 -0
- package/dist/errors/AssertionError.js.map +1 -0
- package/dist/errors/PasteError.js +11 -0
- package/dist/errors/PasteError.js.map +1 -0
- package/dist/errors/assertIsDefined.js +8 -0
- package/dist/errors/assertIsDefined.js.map +1 -0
- package/dist/errors/index.js +4 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -0
- package/dist/key-handlers/EditKeyHandler.js +46 -0
- package/dist/key-handlers/EditKeyHandler.js.map +1 -0
- package/dist/key-handlers/PasteKeyHandler.js +124 -0
- package/dist/key-handlers/PasteKeyHandler.js.map +1 -0
- package/dist/key-handlers/SelectionKeyHandler.js +272 -0
- package/dist/key-handlers/SelectionKeyHandler.js.map +1 -0
- package/dist/key-handlers/TreeKeyHandler.js +45 -0
- package/dist/key-handlers/TreeKeyHandler.js.map +1 -0
- package/dist/key-handlers/index.js +5 -0
- package/dist/key-handlers/index.js.map +1 -0
- package/dist/memoizeClear.js +33 -0
- package/dist/memoizeClear.js.map +1 -0
- package/dist/mouse-handlers/EditMouseHandler.js +25 -0
- package/dist/mouse-handlers/EditMouseHandler.js.map +1 -0
- package/dist/mouse-handlers/GridColumnMoveMouseHandler.js +504 -0
- package/dist/mouse-handlers/GridColumnMoveMouseHandler.js.map +1 -0
- package/dist/mouse-handlers/GridColumnSeparatorMouseHandler.js +67 -0
- package/dist/mouse-handlers/GridColumnSeparatorMouseHandler.js.map +1 -0
- package/dist/mouse-handlers/GridHorizontalScrollBarMouseHandler.js +164 -0
- package/dist/mouse-handlers/GridHorizontalScrollBarMouseHandler.js.map +1 -0
- package/dist/mouse-handlers/GridRowMoveMouseHandler.js +139 -0
- package/dist/mouse-handlers/GridRowMoveMouseHandler.js.map +1 -0
- package/dist/mouse-handlers/GridRowSeparatorMouseHandler.js +54 -0
- package/dist/mouse-handlers/GridRowSeparatorMouseHandler.js.map +1 -0
- package/dist/mouse-handlers/GridRowTreeMouseHandler.js +58 -0
- package/dist/mouse-handlers/GridRowTreeMouseHandler.js.map +1 -0
- package/dist/mouse-handlers/GridScrollBarCornerMouseHandler.js +39 -0
- package/dist/mouse-handlers/GridScrollBarCornerMouseHandler.js.map +1 -0
- package/dist/mouse-handlers/GridSelectionMouseHandler.js +223 -0
- package/dist/mouse-handlers/GridSelectionMouseHandler.js.map +1 -0
- package/dist/mouse-handlers/GridSeparatorMouseHandler.js +213 -0
- package/dist/mouse-handlers/GridSeparatorMouseHandler.js.map +1 -0
- package/dist/mouse-handlers/GridTokenMouseHandler.js +161 -0
- package/dist/mouse-handlers/GridTokenMouseHandler.js.map +1 -0
- package/dist/mouse-handlers/GridVerticalScrollBarMouseHandler.js +165 -0
- package/dist/mouse-handlers/GridVerticalScrollBarMouseHandler.js.map +1 -0
- package/dist/mouse-handlers/index.js +13 -0
- package/dist/mouse-handlers/index.js.map +1 -0
- package/package.json +3 -3
package/dist/Grid.js
ADDED
|
@@ -0,0 +1,1947 @@
|
|
|
1
|
+
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
|
|
2
|
+
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
|
|
3
|
+
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
4
|
+
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
5
|
+
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
6
|
+
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
|
|
7
|
+
function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
|
|
8
|
+
/* eslint react/no-did-update-set-state: "off" */
|
|
9
|
+
import React, { PureComponent } from 'react';
|
|
10
|
+
import classNames from 'classnames';
|
|
11
|
+
import memoize from 'memoize-one';
|
|
12
|
+
import clamp from 'lodash.clamp';
|
|
13
|
+
import { assertNotNull, EMPTY_ARRAY } from '@deephaven/utils';
|
|
14
|
+
import GridMetricCalculator from "./GridMetricCalculator.js";
|
|
15
|
+
import GridTheme from "./GridTheme.js";
|
|
16
|
+
import GridRange, { SELECTION_DIRECTION } from "./GridRange.js";
|
|
17
|
+
import GridRenderer from "./GridRenderer.js";
|
|
18
|
+
import GridUtils, { isLinkToken } from "./GridUtils.js";
|
|
19
|
+
import { GridSelectionMouseHandler, GridColumnMoveMouseHandler, GridColumnSeparatorMouseHandler, GridHorizontalScrollBarMouseHandler, GridRowMoveMouseHandler, GridRowSeparatorMouseHandler, GridRowTreeMouseHandler, GridScrollBarCornerMouseHandler, GridVerticalScrollBarMouseHandler, EditMouseHandler, GridTokenMouseHandler } from "./mouse-handlers/index.js";
|
|
20
|
+
import "./Grid.css";
|
|
21
|
+
import { EditKeyHandler, PasteKeyHandler, SelectionKeyHandler, TreeKeyHandler } from "./key-handlers/index.js";
|
|
22
|
+
import CellInputField from "./CellInputField.js";
|
|
23
|
+
import PasteError from "./errors/PasteError.js";
|
|
24
|
+
import { isExpandableGridModel } from "./ExpandableGridModel.js";
|
|
25
|
+
import { assertIsEditableGridModel, isEditableGridModel } from "./EditableGridModel.js";
|
|
26
|
+
import { assertIsDefined } from "./errors/index.js";
|
|
27
|
+
import ThemeContext from "./ThemeContext.js";
|
|
28
|
+
/**
|
|
29
|
+
* High performance, extendible, themeable grid component.
|
|
30
|
+
* Architectured to be fast and handle billions of rows/columns by default.
|
|
31
|
+
* The base model does not provide support for sorting, filtering, etc.
|
|
32
|
+
* To get that functionality, extend GridModel/GridRenderer, and add onClick/onContextMenu handlers to implement your own sort.
|
|
33
|
+
*
|
|
34
|
+
* Extend GridModel with your own data model to provide the data for the grid.
|
|
35
|
+
* Extend GridTheme to change the appearance if the grid. See GridTheme for all the settable values.
|
|
36
|
+
* Extend GridMetricCalculator to provide different metrics for the grid, such as column sizing, header sizing, etc.
|
|
37
|
+
* Extend GridRenderer to have complete control over the rendering process. Can override just one method such as drawColumnHeader, or override the whole drawCanvas process.
|
|
38
|
+
*
|
|
39
|
+
* Add an onViewChanged callback to page in/out data as user moves around the grid
|
|
40
|
+
* Can also add onClick and onContextMenu handlers to add custom functionality and menus.
|
|
41
|
+
*/
|
|
42
|
+
class Grid extends PureComponent {
|
|
43
|
+
// use same constant as chrome source for windows
|
|
44
|
+
// https://github.com/chromium/chromium/blob/973af9d461b6b5dc60208c8d3d66adc27e53da78/ui/events/blink/web_input_event_builders_win.cc#L285
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* On some devices there may be different scaling required for high DPI. Get the scale required for the canvas.
|
|
48
|
+
* @param context The canvas context
|
|
49
|
+
* @returns The scale to use
|
|
50
|
+
*/
|
|
51
|
+
static getScale(context) {
|
|
52
|
+
var _ref, _ref2, _ref3, _ref4, _legacyContext$webkit;
|
|
53
|
+
var devicePixelRatio = window.devicePixelRatio || 1;
|
|
54
|
+
|
|
55
|
+
// backingStorePixelRatio is deprecated, but check it just in case
|
|
56
|
+
var legacyContext = context;
|
|
57
|
+
var backingStorePixelRatio = // Not worth converting to a massive if structure
|
|
58
|
+
// Converting would reduce readability and maintainability
|
|
59
|
+
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
|
60
|
+
(_ref = (_ref2 = (_ref3 = (_ref4 = (_legacyContext$webkit = legacyContext.webkitBackingStorePixelRatio) !== null && _legacyContext$webkit !== void 0 ? _legacyContext$webkit : legacyContext.mozBackingStorePixelRatio) !== null && _ref4 !== void 0 ? _ref4 : legacyContext.msBackingStorePixelRatio) !== null && _ref3 !== void 0 ? _ref3 : legacyContext.oBackingStorePixelRatio) !== null && _ref2 !== void 0 ? _ref2 : legacyContext.backingStorePixelRatio) !== null && _ref !== void 0 ? _ref : 1;
|
|
61
|
+
return devicePixelRatio / backingStorePixelRatio;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Get the class name from the cursor provided
|
|
66
|
+
* @param cursor The grid cursor to use
|
|
67
|
+
* @returns Class name with the grid-cursor prefix or null if no cursor provided
|
|
68
|
+
*/
|
|
69
|
+
static getCursorClassName(cursor) {
|
|
70
|
+
return cursor != null && cursor !== '' ? "grid-cursor-".concat(cursor) : null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Need to disable react/sort-comp so I can put the fields here
|
|
74
|
+
/* eslint-disable react/sort-comp */
|
|
75
|
+
|
|
76
|
+
/* eslint-enable react/sort-comp */
|
|
77
|
+
|
|
78
|
+
constructor(props) {
|
|
79
|
+
super(props);
|
|
80
|
+
_defineProperty(this, "renderer", void 0);
|
|
81
|
+
_defineProperty(this, "metricCalculator", void 0);
|
|
82
|
+
_defineProperty(this, "canvas", void 0);
|
|
83
|
+
_defineProperty(this, "canvasContext", void 0);
|
|
84
|
+
_defineProperty(this, "animationFrame", void 0);
|
|
85
|
+
_defineProperty(this, "prevMetrics", void 0);
|
|
86
|
+
_defineProperty(this, "metrics", void 0);
|
|
87
|
+
_defineProperty(this, "renderState", void 0);
|
|
88
|
+
_defineProperty(this, "documentCursor", void 0);
|
|
89
|
+
_defineProperty(this, "dragTimer", void 0);
|
|
90
|
+
_defineProperty(this, "keyHandlers", void 0);
|
|
91
|
+
_defineProperty(this, "mouseHandlers", void 0);
|
|
92
|
+
_defineProperty(this, "getCachedKeyHandlers", memoize(keyHandlers => [...keyHandlers, ...this.keyHandlers].sort((a, b) => a.order - b.order)));
|
|
93
|
+
_defineProperty(this, "getCachedMouseHandlers", memoize(mouseHandlers => [...mouseHandlers, ...this.mouseHandlers].sort((a, b) => a.order - b.order)));
|
|
94
|
+
this.handleClick = this.handleClick.bind(this);
|
|
95
|
+
this.handleContextMenu = this.handleContextMenu.bind(this);
|
|
96
|
+
this.handleEditCellCancel = this.handleEditCellCancel.bind(this);
|
|
97
|
+
this.handleEditCellChange = this.handleEditCellChange.bind(this);
|
|
98
|
+
this.handleEditCellCommit = this.handleEditCellCommit.bind(this);
|
|
99
|
+
this.handleDoubleClick = this.handleDoubleClick.bind(this);
|
|
100
|
+
this.handleKeyDown = this.handleKeyDown.bind(this);
|
|
101
|
+
this.handleMouseDown = this.handleMouseDown.bind(this);
|
|
102
|
+
this.handleMouseDrag = this.handleMouseDrag.bind(this);
|
|
103
|
+
this.handleMouseMove = this.handleMouseMove.bind(this);
|
|
104
|
+
this.handleMouseLeave = this.handleMouseLeave.bind(this);
|
|
105
|
+
this.handleMouseUp = this.handleMouseUp.bind(this);
|
|
106
|
+
this.handleResize = this.handleResize.bind(this);
|
|
107
|
+
this.handleWheel = this.handleWheel.bind(this);
|
|
108
|
+
var {
|
|
109
|
+
isStuckToBottom,
|
|
110
|
+
isStuckToRight,
|
|
111
|
+
metricCalculator,
|
|
112
|
+
movedColumns,
|
|
113
|
+
movedRows,
|
|
114
|
+
renderer
|
|
115
|
+
} = props;
|
|
116
|
+
this.renderer = renderer || new GridRenderer();
|
|
117
|
+
this.metricCalculator = metricCalculator || new GridMetricCalculator();
|
|
118
|
+
this.canvas = null;
|
|
119
|
+
this.canvasContext = null;
|
|
120
|
+
this.animationFrame = null;
|
|
121
|
+
this.prevMetrics = null;
|
|
122
|
+
this.metrics = null;
|
|
123
|
+
this.renderState = {};
|
|
124
|
+
|
|
125
|
+
// Track the cursor that is currently added to the document
|
|
126
|
+
// Add to document so that when dragging the cursor stays, even if mouse leaves the canvas
|
|
127
|
+
// Note: on document, not body so that cursor styling can be combined with
|
|
128
|
+
// blocked pointer events that would otherwise prevent cursor styling from showing
|
|
129
|
+
this.documentCursor = null;
|
|
130
|
+
this.dragTimer = null;
|
|
131
|
+
|
|
132
|
+
// specify handler ordering, such that any extensions can insert handlers in between
|
|
133
|
+
this.keyHandlers = [new EditKeyHandler(400), new PasteKeyHandler(450), new SelectionKeyHandler(500), new TreeKeyHandler(900)];
|
|
134
|
+
this.mouseHandlers = [new GridRowSeparatorMouseHandler(100), new GridColumnSeparatorMouseHandler(200), new GridRowMoveMouseHandler(300), new GridColumnMoveMouseHandler(400), new EditMouseHandler(450), new GridVerticalScrollBarMouseHandler(500), new GridHorizontalScrollBarMouseHandler(600), new GridScrollBarCornerMouseHandler(700), new GridRowTreeMouseHandler(800), new GridTokenMouseHandler(825), new GridSelectionMouseHandler(900)];
|
|
135
|
+
this.state = {
|
|
136
|
+
// Top/left visible cell in the grid. Note that it's visible row/column index, not the model index (ie. if columns are re-ordered)
|
|
137
|
+
top: 0,
|
|
138
|
+
left: 0,
|
|
139
|
+
// The scroll offset of the top/left cell. 0,0 means the cell is at the top left
|
|
140
|
+
// Should be less than the width of the column
|
|
141
|
+
topOffset: 0,
|
|
142
|
+
leftOffset: 0,
|
|
143
|
+
// current column/row that user is dragging
|
|
144
|
+
draggingColumn: null,
|
|
145
|
+
draggingRow: null,
|
|
146
|
+
// Offset when dragging a row
|
|
147
|
+
draggingRowOffset: null,
|
|
148
|
+
// When drawing header separators for resizing
|
|
149
|
+
draggingColumnSeparator: null,
|
|
150
|
+
draggingRowSeparator: null,
|
|
151
|
+
isDraggingHorizontalScrollBar: false,
|
|
152
|
+
isDraggingVerticalScrollBar: false,
|
|
153
|
+
// Anything is performing a drag, for blocking hover rendering
|
|
154
|
+
isDragging: false,
|
|
155
|
+
// The coordinates of the mouse. May be outside of the canvas
|
|
156
|
+
mouseX: null,
|
|
157
|
+
mouseY: null,
|
|
158
|
+
// Move operations the user has performed on this grids columns/rows
|
|
159
|
+
movedColumns,
|
|
160
|
+
movedRows,
|
|
161
|
+
// Cursor (highlighted cell) location and active selected range
|
|
162
|
+
cursorRow: null,
|
|
163
|
+
cursorColumn: null,
|
|
164
|
+
selectionStartRow: null,
|
|
165
|
+
selectionStartColumn: null,
|
|
166
|
+
selectionEndRow: null,
|
|
167
|
+
selectionEndColumn: null,
|
|
168
|
+
// Currently selected ranges and previously selected ranges
|
|
169
|
+
// Store the previously selected ranges to determine if the new selection should
|
|
170
|
+
// deselect again (if it's the same range)
|
|
171
|
+
selectedRanges: EMPTY_ARRAY,
|
|
172
|
+
lastSelectedRanges: EMPTY_ARRAY,
|
|
173
|
+
// The mouse cursor style to use when hovering over the grid element
|
|
174
|
+
cursor: null,
|
|
175
|
+
// { column: number, row: number, selectionRange: [number, number], value: string | null, isQuickEdit?: boolean }
|
|
176
|
+
// The cell that is currently being edited
|
|
177
|
+
editingCell: null,
|
|
178
|
+
isStuckToBottom,
|
|
179
|
+
isStuckToRight
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
componentDidMount() {
|
|
183
|
+
var _this$canvas;
|
|
184
|
+
this.initContext();
|
|
185
|
+
|
|
186
|
+
// Need to explicitly add wheel event to canvas so we can preventDefault/avoid passive listener issue
|
|
187
|
+
// Otherwise React attaches listener at doc level and you can't prevent default
|
|
188
|
+
// https://github.com/facebook/react/issues/14856
|
|
189
|
+
(_this$canvas = this.canvas) === null || _this$canvas === void 0 ? void 0 : _this$canvas.addEventListener('wheel', this.handleWheel, {
|
|
190
|
+
passive: false
|
|
191
|
+
});
|
|
192
|
+
window.addEventListener('resize', this.handleResize);
|
|
193
|
+
this.updateCanvas();
|
|
194
|
+
|
|
195
|
+
// apply on mount, so that it works with a static model
|
|
196
|
+
// https://github.com/deephaven/web-client-ui/issues/581
|
|
197
|
+
var {
|
|
198
|
+
isStuckToBottom,
|
|
199
|
+
isStuckToRight
|
|
200
|
+
} = this.props;
|
|
201
|
+
if (isStuckToBottom) {
|
|
202
|
+
this.scrollToBottom();
|
|
203
|
+
}
|
|
204
|
+
if (isStuckToRight) {
|
|
205
|
+
this.scrollToRight();
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
componentDidUpdate(prevProps, prevState) {
|
|
209
|
+
var {
|
|
210
|
+
isStickyBottom,
|
|
211
|
+
isStickyRight,
|
|
212
|
+
movedColumns,
|
|
213
|
+
movedRows,
|
|
214
|
+
onMovedColumnsChanged,
|
|
215
|
+
onMoveColumnComplete,
|
|
216
|
+
onMovedRowsChanged,
|
|
217
|
+
onMoveRowComplete
|
|
218
|
+
} = this.props;
|
|
219
|
+
var {
|
|
220
|
+
isStickyBottom: prevIsStickyBottom,
|
|
221
|
+
isStickyRight: prevIsStickyRight,
|
|
222
|
+
movedColumns: prevPropMovedColumns,
|
|
223
|
+
movedRows: prevPropMovedRows
|
|
224
|
+
} = prevProps;
|
|
225
|
+
var {
|
|
226
|
+
movedColumns: prevStateMovedColumns,
|
|
227
|
+
movedRows: prevStateMovedRows
|
|
228
|
+
} = prevState;
|
|
229
|
+
var {
|
|
230
|
+
draggingColumn,
|
|
231
|
+
draggingRow,
|
|
232
|
+
movedColumns: currentStateMovedColumns,
|
|
233
|
+
movedRows: currentStateMovedRows
|
|
234
|
+
} = this.state;
|
|
235
|
+
if (prevPropMovedColumns !== movedColumns) {
|
|
236
|
+
this.setState({
|
|
237
|
+
movedColumns
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
if (prevPropMovedRows !== movedRows) {
|
|
241
|
+
this.setState({
|
|
242
|
+
movedRows
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
if (prevStateMovedColumns !== currentStateMovedColumns) {
|
|
246
|
+
onMovedColumnsChanged(currentStateMovedColumns);
|
|
247
|
+
}
|
|
248
|
+
if (prevState.draggingColumn != null && draggingColumn == null) {
|
|
249
|
+
onMoveColumnComplete(currentStateMovedColumns);
|
|
250
|
+
}
|
|
251
|
+
if (prevStateMovedRows !== currentStateMovedRows) {
|
|
252
|
+
onMovedRowsChanged(currentStateMovedRows);
|
|
253
|
+
}
|
|
254
|
+
if (prevState.draggingRow != null && draggingRow == null) {
|
|
255
|
+
onMoveRowComplete(currentStateMovedRows);
|
|
256
|
+
}
|
|
257
|
+
if (isStickyBottom !== prevIsStickyBottom) {
|
|
258
|
+
this.setState({
|
|
259
|
+
isStuckToBottom: false
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
if (isStickyRight !== prevIsStickyRight) {
|
|
263
|
+
this.setState({
|
|
264
|
+
isStuckToRight: false
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
this.updateMetrics();
|
|
268
|
+
this.requestUpdateCanvas();
|
|
269
|
+
this.checkStickyScroll();
|
|
270
|
+
if (this.validateSelection()) {
|
|
271
|
+
this.checkSelectionChange(prevState);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
componentWillUnmount() {
|
|
275
|
+
var _this$canvas2;
|
|
276
|
+
if (this.animationFrame != null) {
|
|
277
|
+
cancelAnimationFrame(this.animationFrame);
|
|
278
|
+
}
|
|
279
|
+
(_this$canvas2 = this.canvas) === null || _this$canvas2 === void 0 ? void 0 : _this$canvas2.removeEventListener('wheel', this.handleWheel);
|
|
280
|
+
window.removeEventListener('mousemove', this.handleMouseDrag, true);
|
|
281
|
+
window.removeEventListener('mouseup', this.handleMouseUp, true);
|
|
282
|
+
window.removeEventListener('resize', this.handleResize);
|
|
283
|
+
this.stopDragTimer();
|
|
284
|
+
}
|
|
285
|
+
getTheme() {
|
|
286
|
+
var {
|
|
287
|
+
theme
|
|
288
|
+
} = this.props;
|
|
289
|
+
return Grid.getTheme(this.context, theme);
|
|
290
|
+
}
|
|
291
|
+
getGridPointFromEvent(event) {
|
|
292
|
+
assertIsDefined(this.canvas);
|
|
293
|
+
var rect = this.canvas.getBoundingClientRect();
|
|
294
|
+
var x = event.clientX - rect.left;
|
|
295
|
+
var y = event.clientY - rect.top;
|
|
296
|
+
return this.getGridPointFromXY(x, y);
|
|
297
|
+
}
|
|
298
|
+
getGridPointFromXY(x, y) {
|
|
299
|
+
if (!this.metrics) throw new Error('metrics must be set');
|
|
300
|
+
return GridUtils.getGridPointFromXY(x, y, this.metrics);
|
|
301
|
+
}
|
|
302
|
+
getMetricState() {
|
|
303
|
+
var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.state;
|
|
304
|
+
var theme = this.getTheme();
|
|
305
|
+
var {
|
|
306
|
+
model,
|
|
307
|
+
stateOverride
|
|
308
|
+
} = this.props;
|
|
309
|
+
if (!this.canvasContext || !this.canvas) {
|
|
310
|
+
throw new Error('Canvas and context must be defined to get metrics');
|
|
311
|
+
}
|
|
312
|
+
var context = this.canvasContext;
|
|
313
|
+
var width = this.canvas.clientWidth;
|
|
314
|
+
var height = this.canvas.clientHeight;
|
|
315
|
+
var {
|
|
316
|
+
left,
|
|
317
|
+
top,
|
|
318
|
+
leftOffset,
|
|
319
|
+
topOffset,
|
|
320
|
+
movedColumns,
|
|
321
|
+
movedRows,
|
|
322
|
+
isDraggingHorizontalScrollBar,
|
|
323
|
+
isDraggingVerticalScrollBar,
|
|
324
|
+
draggingColumn
|
|
325
|
+
} = state;
|
|
326
|
+
return _objectSpread({
|
|
327
|
+
left,
|
|
328
|
+
top,
|
|
329
|
+
leftOffset,
|
|
330
|
+
topOffset,
|
|
331
|
+
width,
|
|
332
|
+
height,
|
|
333
|
+
context,
|
|
334
|
+
theme,
|
|
335
|
+
model,
|
|
336
|
+
movedColumns,
|
|
337
|
+
movedRows,
|
|
338
|
+
isDraggingHorizontalScrollBar,
|
|
339
|
+
isDraggingVerticalScrollBar,
|
|
340
|
+
draggingColumn
|
|
341
|
+
}, stateOverride);
|
|
342
|
+
}
|
|
343
|
+
getKeyHandlers() {
|
|
344
|
+
var {
|
|
345
|
+
keyHandlers
|
|
346
|
+
} = this.props;
|
|
347
|
+
return this.getCachedKeyHandlers(keyHandlers);
|
|
348
|
+
}
|
|
349
|
+
getMouseHandlers() {
|
|
350
|
+
var {
|
|
351
|
+
mouseHandlers
|
|
352
|
+
} = this.props;
|
|
353
|
+
return this.getCachedMouseHandlers(mouseHandlers);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Translate from the provided visible index to the model index
|
|
358
|
+
* @param columnIndex The column index to get the model for
|
|
359
|
+
* @returns The model index
|
|
360
|
+
*/
|
|
361
|
+
getModelColumn(columnIndex) {
|
|
362
|
+
var _this$metrics, _this$metrics$modelCo;
|
|
363
|
+
var modelIndex = (_this$metrics = this.metrics) === null || _this$metrics === void 0 ? void 0 : (_this$metrics$modelCo = _this$metrics.modelColumns) === null || _this$metrics$modelCo === void 0 ? void 0 : _this$metrics$modelCo.get(columnIndex);
|
|
364
|
+
if (modelIndex === undefined) {
|
|
365
|
+
throw new Error("Unable to get ModelIndex for column ".concat(columnIndex));
|
|
366
|
+
}
|
|
367
|
+
return modelIndex;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Translate from the provided visible index to the model index
|
|
372
|
+
* @param rowIndex The row index to get the model for
|
|
373
|
+
* @returns The model index
|
|
374
|
+
*/
|
|
375
|
+
getModelRow(rowIndex) {
|
|
376
|
+
var _this$metrics2, _this$metrics2$modelR;
|
|
377
|
+
var modelIndex = (_this$metrics2 = this.metrics) === null || _this$metrics2 === void 0 ? void 0 : (_this$metrics2$modelR = _this$metrics2.modelRows) === null || _this$metrics2$modelR === void 0 ? void 0 : _this$metrics2$modelR.get(rowIndex);
|
|
378
|
+
if (modelIndex === undefined) {
|
|
379
|
+
throw new Error("Unable to get ModelIndex for row ".concat(rowIndex));
|
|
380
|
+
}
|
|
381
|
+
return modelIndex;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Toggle a row between expanded and collapsed states
|
|
386
|
+
* @param row The row to toggle expansion for
|
|
387
|
+
* @param expandDescendants True if nested rows should be expanded, false otherwise
|
|
388
|
+
*/
|
|
389
|
+
toggleRowExpanded(row) {
|
|
390
|
+
var expandDescendants = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
|
391
|
+
var modelRow = this.getModelRow(row);
|
|
392
|
+
var {
|
|
393
|
+
model
|
|
394
|
+
} = this.props;
|
|
395
|
+
// We only want to set expansion if the row is expandable
|
|
396
|
+
// If it's not, still move the cursor to that position, as it may be outside of the current viewport and we don't know if it's expandable yet
|
|
397
|
+
if (isExpandableGridModel(model) && model.isRowExpandable(modelRow)) {
|
|
398
|
+
model.setRowExpanded(modelRow, !model.isRowExpanded(modelRow), expandDescendants);
|
|
399
|
+
}
|
|
400
|
+
this.clearSelectedRanges();
|
|
401
|
+
this.commitSelection(); // Need to commit before moving in case we're selecting same row again
|
|
402
|
+
this.moveCursorToPosition(0, row);
|
|
403
|
+
this.commitSelection();
|
|
404
|
+
this.setState({
|
|
405
|
+
isStuckToBottom: false
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Scrolls to bottom, if not already at bottom
|
|
411
|
+
*/
|
|
412
|
+
scrollToBottom() {
|
|
413
|
+
if (!this.metrics) return;
|
|
414
|
+
var {
|
|
415
|
+
bottomVisible,
|
|
416
|
+
rowCount,
|
|
417
|
+
top,
|
|
418
|
+
lastTop
|
|
419
|
+
} = this.metrics;
|
|
420
|
+
if (bottomVisible < rowCount - 1 && bottomVisible > 0 || top > lastTop) {
|
|
421
|
+
this.setState({
|
|
422
|
+
top: lastTop
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Scrolls to right, if not already at right
|
|
429
|
+
*/
|
|
430
|
+
scrollToRight() {
|
|
431
|
+
if (!this.metrics) return;
|
|
432
|
+
var {
|
|
433
|
+
rightVisible,
|
|
434
|
+
columnCount,
|
|
435
|
+
left,
|
|
436
|
+
lastLeft
|
|
437
|
+
} = this.metrics;
|
|
438
|
+
if (rightVisible < columnCount - 1 && rightVisible > 0 || left > lastLeft) {
|
|
439
|
+
this.setState({
|
|
440
|
+
left: lastLeft
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Allows the selected ranges to be set programatically
|
|
447
|
+
* Will update the cursor and selection start/end based on the new ranges
|
|
448
|
+
* @param gridRanges The new selected ranges to set
|
|
449
|
+
*/
|
|
450
|
+
setSelectedRanges(gridRanges) {
|
|
451
|
+
var {
|
|
452
|
+
model
|
|
453
|
+
} = this.props;
|
|
454
|
+
var {
|
|
455
|
+
columnCount,
|
|
456
|
+
rowCount
|
|
457
|
+
} = model;
|
|
458
|
+
var {
|
|
459
|
+
cursorRow,
|
|
460
|
+
cursorColumn,
|
|
461
|
+
selectedRanges
|
|
462
|
+
} = this.state;
|
|
463
|
+
this.setState({
|
|
464
|
+
selectedRanges: gridRanges,
|
|
465
|
+
lastSelectedRanges: selectedRanges
|
|
466
|
+
});
|
|
467
|
+
if (gridRanges.length > 0) {
|
|
468
|
+
var range = GridRange.boundedRange(gridRanges[0], columnCount, rowCount);
|
|
469
|
+
var newCursorRow = cursorRow;
|
|
470
|
+
var newCursorColumn = cursorColumn;
|
|
471
|
+
if (!range.containsCell(cursorColumn, cursorRow)) {
|
|
472
|
+
({
|
|
473
|
+
row: newCursorRow,
|
|
474
|
+
column: newCursorColumn
|
|
475
|
+
} = range.startCell());
|
|
476
|
+
}
|
|
477
|
+
this.setState({
|
|
478
|
+
selectionStartColumn: range.startColumn,
|
|
479
|
+
selectionStartRow: range.startRow,
|
|
480
|
+
selectionEndColumn: range.endColumn,
|
|
481
|
+
selectionEndRow: range.endRow,
|
|
482
|
+
cursorColumn: newCursorColumn,
|
|
483
|
+
cursorRow: newCursorRow
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
initContext() {
|
|
488
|
+
var {
|
|
489
|
+
canvas
|
|
490
|
+
} = this;
|
|
491
|
+
var {
|
|
492
|
+
canvasOptions
|
|
493
|
+
} = this.props;
|
|
494
|
+
if (!canvas) throw new Error('Canvas not set');
|
|
495
|
+
this.canvasContext = canvas.getContext('2d', canvasOptions);
|
|
496
|
+
}
|
|
497
|
+
requestUpdateCanvas() {
|
|
498
|
+
if (this.animationFrame != null) {
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
this.animationFrame = requestAnimationFrame(() => {
|
|
502
|
+
this.animationFrame = null;
|
|
503
|
+
this.updateCanvas();
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
updateCanvas() {
|
|
507
|
+
this.updateCanvasScale();
|
|
508
|
+
this.updateMetrics();
|
|
509
|
+
this.updateRenderState();
|
|
510
|
+
var {
|
|
511
|
+
canvasContext,
|
|
512
|
+
metrics,
|
|
513
|
+
renderState
|
|
514
|
+
} = this;
|
|
515
|
+
assertNotNull(canvasContext);
|
|
516
|
+
assertNotNull(metrics);
|
|
517
|
+
assertNotNull(renderState);
|
|
518
|
+
this.renderer.configureContext(canvasContext, renderState);
|
|
519
|
+
var {
|
|
520
|
+
onViewChanged
|
|
521
|
+
} = this.props;
|
|
522
|
+
onViewChanged(metrics);
|
|
523
|
+
this.drawCanvas(metrics);
|
|
524
|
+
}
|
|
525
|
+
updateCanvasScale() {
|
|
526
|
+
var {
|
|
527
|
+
canvas,
|
|
528
|
+
canvasContext
|
|
529
|
+
} = this;
|
|
530
|
+
if (!canvas) throw new Error('canvas not set');
|
|
531
|
+
if (!canvasContext) throw new Error('canvasContext not set');
|
|
532
|
+
if (!canvas.parentElement) throw new Error('Canvas has no parent element');
|
|
533
|
+
var scale = Grid.getScale(canvasContext);
|
|
534
|
+
// the parent wrapper has 100% width/height, and is used for determining size
|
|
535
|
+
// we don't want to stretch the canvas to 100%, to avoid fractional pixels.
|
|
536
|
+
// A wrapper element must be used for sizing, and canvas size must be
|
|
537
|
+
// set manually to a floored value in css and a scaled value in width/height
|
|
538
|
+
var rect = canvas.parentElement.getBoundingClientRect();
|
|
539
|
+
var width = Math.floor(rect.width);
|
|
540
|
+
var height = Math.floor(rect.height);
|
|
541
|
+
canvas.style.width = "".concat(width, "px");
|
|
542
|
+
canvas.style.height = "".concat(height, "px");
|
|
543
|
+
canvas.width = width * scale;
|
|
544
|
+
canvas.height = height * scale;
|
|
545
|
+
canvasContext.scale(scale, scale);
|
|
546
|
+
}
|
|
547
|
+
updateScrollBounds() {
|
|
548
|
+
if (!this.metrics) throw new Error('metrics not set');
|
|
549
|
+
var {
|
|
550
|
+
left,
|
|
551
|
+
top
|
|
552
|
+
} = this.state;
|
|
553
|
+
var {
|
|
554
|
+
lastLeft,
|
|
555
|
+
lastTop
|
|
556
|
+
} = this.metrics;
|
|
557
|
+
if (left > lastLeft) {
|
|
558
|
+
this.setState({
|
|
559
|
+
left: lastLeft,
|
|
560
|
+
leftOffset: 0
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
if (top > lastTop) {
|
|
564
|
+
this.setState({
|
|
565
|
+
top: lastTop,
|
|
566
|
+
topOffset: 0
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* Compares the current metrics with the previous metrics to see if we need to scroll when it is stuck to the bottom or the right
|
|
573
|
+
*/
|
|
574
|
+
checkStickyScroll() {
|
|
575
|
+
if (!this.metrics) {
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
if (this.prevMetrics) {
|
|
579
|
+
var {
|
|
580
|
+
rowCount,
|
|
581
|
+
columnCount,
|
|
582
|
+
height,
|
|
583
|
+
width
|
|
584
|
+
} = this.metrics;
|
|
585
|
+
var {
|
|
586
|
+
rowCount: prevRowCount,
|
|
587
|
+
columnCount: prevColumnCount,
|
|
588
|
+
height: prevHeight,
|
|
589
|
+
width: prevWidth
|
|
590
|
+
} = this.prevMetrics;
|
|
591
|
+
if (prevRowCount !== rowCount || height !== prevHeight) {
|
|
592
|
+
var {
|
|
593
|
+
isStuckToBottom
|
|
594
|
+
} = this.state;
|
|
595
|
+
if (isStuckToBottom) {
|
|
596
|
+
this.scrollToBottom();
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
if (prevColumnCount !== columnCount || width !== prevWidth) {
|
|
600
|
+
var {
|
|
601
|
+
isStuckToRight
|
|
602
|
+
} = this.state;
|
|
603
|
+
if (isStuckToRight) {
|
|
604
|
+
this.scrollToRight();
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
this.prevMetrics = this.metrics;
|
|
609
|
+
}
|
|
610
|
+
updateMetrics() {
|
|
611
|
+
var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.state;
|
|
612
|
+
this.prevMetrics = this.metrics;
|
|
613
|
+
var {
|
|
614
|
+
metricCalculator
|
|
615
|
+
} = this;
|
|
616
|
+
var metricState = this.getMetricState(state);
|
|
617
|
+
this.metrics = metricCalculator.getMetrics(metricState);
|
|
618
|
+
this.updateScrollBounds();
|
|
619
|
+
return this.metrics;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/**
|
|
623
|
+
* Check if the selection state has changed, and call the onSelectionChanged callback if they have
|
|
624
|
+
* @param prevState The previous grid state
|
|
625
|
+
*/
|
|
626
|
+
checkSelectionChange(prevState) {
|
|
627
|
+
var {
|
|
628
|
+
selectedRanges: oldSelectedRanges
|
|
629
|
+
} = prevState;
|
|
630
|
+
var {
|
|
631
|
+
selectedRanges
|
|
632
|
+
} = this.state;
|
|
633
|
+
if (selectedRanges !== oldSelectedRanges) {
|
|
634
|
+
var {
|
|
635
|
+
onSelectionChanged
|
|
636
|
+
} = this.props;
|
|
637
|
+
onSelectionChanged(selectedRanges);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/**
|
|
642
|
+
* Validate the current selection, and reset if it is invalid
|
|
643
|
+
* @returns True if the selection is valid, false if the selection was invalid and has been reset
|
|
644
|
+
*/
|
|
645
|
+
validateSelection() {
|
|
646
|
+
var {
|
|
647
|
+
model
|
|
648
|
+
} = this.props;
|
|
649
|
+
var {
|
|
650
|
+
selectedRanges
|
|
651
|
+
} = this.state;
|
|
652
|
+
var {
|
|
653
|
+
columnCount,
|
|
654
|
+
rowCount
|
|
655
|
+
} = model;
|
|
656
|
+
for (var i = 0; i < selectedRanges.length; i += 1) {
|
|
657
|
+
var range = selectedRanges[i];
|
|
658
|
+
if (range.endColumn != null && range.endColumn >= columnCount || range.endRow != null && range.endRow >= rowCount) {
|
|
659
|
+
// Just clear the selection rather than trying to trim it.
|
|
660
|
+
this.setState({
|
|
661
|
+
selectedRanges: [],
|
|
662
|
+
lastSelectedRanges: []
|
|
663
|
+
});
|
|
664
|
+
return false;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
return true;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* Clears all selected ranges
|
|
672
|
+
*/
|
|
673
|
+
clearSelectedRanges() {
|
|
674
|
+
var {
|
|
675
|
+
selectedRanges
|
|
676
|
+
} = this.state;
|
|
677
|
+
this.setState({
|
|
678
|
+
selectedRanges: [],
|
|
679
|
+
lastSelectedRanges: selectedRanges
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/** Clears all but the last selected range */
|
|
684
|
+
trimSelectedRanges() {
|
|
685
|
+
var {
|
|
686
|
+
selectedRanges
|
|
687
|
+
} = this.state;
|
|
688
|
+
if (selectedRanges.length > 0) {
|
|
689
|
+
this.setState({
|
|
690
|
+
selectedRanges: selectedRanges.slice(selectedRanges.length - 1)
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
/**
|
|
696
|
+
* Begin a selection operation at the provided location
|
|
697
|
+
* @param column Column where the selection is beginning
|
|
698
|
+
* @param row Row where the selection is beginning
|
|
699
|
+
*/
|
|
700
|
+
beginSelection(column, row) {
|
|
701
|
+
this.setState({
|
|
702
|
+
selectionStartColumn: column,
|
|
703
|
+
selectionStartRow: row,
|
|
704
|
+
selectionEndColumn: column,
|
|
705
|
+
selectionEndRow: row,
|
|
706
|
+
cursorColumn: column,
|
|
707
|
+
cursorRow: row
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
/**
|
|
712
|
+
* Moves the selection to the cell specified
|
|
713
|
+
* @param column The column index to move the cursor to
|
|
714
|
+
* @param row The row index to move the cursor to
|
|
715
|
+
* @param extendSelection Whether to extend the current selection (eg. holding Shift)
|
|
716
|
+
* @param maximizePreviousRange When true, maximize/add to the previous range only, ignoring where the selection was started.
|
|
717
|
+
*/
|
|
718
|
+
moveSelection(column, row) {
|
|
719
|
+
var extendSelection = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
|
720
|
+
var maximizePreviousRange = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
|
|
721
|
+
this.setState(state => {
|
|
722
|
+
var {
|
|
723
|
+
selectedRanges,
|
|
724
|
+
selectionStartRow,
|
|
725
|
+
selectionStartColumn
|
|
726
|
+
} = state;
|
|
727
|
+
var {
|
|
728
|
+
theme
|
|
729
|
+
} = this.props;
|
|
730
|
+
var {
|
|
731
|
+
autoSelectRow,
|
|
732
|
+
autoSelectColumn
|
|
733
|
+
} = theme;
|
|
734
|
+
if (extendSelection && selectedRanges.length > 0) {
|
|
735
|
+
var lastSelectedRange = selectedRanges[selectedRanges.length - 1];
|
|
736
|
+
var left = null;
|
|
737
|
+
var top = null;
|
|
738
|
+
var right = null;
|
|
739
|
+
var bottom = null;
|
|
740
|
+
if (maximizePreviousRange) {
|
|
741
|
+
var _lastSelectedRange$st, _lastSelectedRange$st2, _lastSelectedRange$en, _lastSelectedRange$en2;
|
|
742
|
+
left = autoSelectRow !== undefined && autoSelectRow ? null : Math.min(column !== null && column !== void 0 ? column : 0, (_lastSelectedRange$st = lastSelectedRange.startColumn) !== null && _lastSelectedRange$st !== void 0 ? _lastSelectedRange$st : 0);
|
|
743
|
+
top = autoSelectColumn !== undefined && autoSelectColumn ? null : Math.min(row !== null && row !== void 0 ? row : 0, (_lastSelectedRange$st2 = lastSelectedRange.startRow) !== null && _lastSelectedRange$st2 !== void 0 ? _lastSelectedRange$st2 : 0);
|
|
744
|
+
right = autoSelectRow !== undefined && autoSelectRow ? null : Math.max(column !== null && column !== void 0 ? column : 0, (_lastSelectedRange$en = lastSelectedRange.endColumn) !== null && _lastSelectedRange$en !== void 0 ? _lastSelectedRange$en : 0);
|
|
745
|
+
bottom = autoSelectColumn !== undefined && autoSelectColumn ? null : Math.max(row !== null && row !== void 0 ? row : 0, (_lastSelectedRange$en2 = lastSelectedRange.endRow) !== null && _lastSelectedRange$en2 !== void 0 ? _lastSelectedRange$en2 : 0);
|
|
746
|
+
} else {
|
|
747
|
+
left = lastSelectedRange.startColumn;
|
|
748
|
+
top = lastSelectedRange.startRow;
|
|
749
|
+
if (selectionStartColumn != null || selectionStartRow != null) {
|
|
750
|
+
if (autoSelectRow === undefined || !autoSelectRow) {
|
|
751
|
+
left = selectionStartColumn;
|
|
752
|
+
}
|
|
753
|
+
if (autoSelectColumn === undefined || !autoSelectColumn) {
|
|
754
|
+
top = selectionStartRow;
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
right = autoSelectRow !== undefined && autoSelectRow ? null : column;
|
|
758
|
+
bottom = autoSelectColumn !== undefined && autoSelectColumn ? null : row;
|
|
759
|
+
}
|
|
760
|
+
var selectedRange = GridRange.makeNormalized(left, top, right, bottom);
|
|
761
|
+
if (lastSelectedRange.equals(selectedRange)) {
|
|
762
|
+
return null;
|
|
763
|
+
}
|
|
764
|
+
var _newRanges = [...selectedRanges];
|
|
765
|
+
_newRanges[_newRanges.length - 1] = selectedRange;
|
|
766
|
+
return {
|
|
767
|
+
selectedRanges: _newRanges,
|
|
768
|
+
selectionEndColumn: column,
|
|
769
|
+
selectionEndRow: row
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
var newRanges = [...selectedRanges];
|
|
773
|
+
var selectedColumn = autoSelectRow !== undefined && autoSelectRow ? null : column;
|
|
774
|
+
var selectedRow = autoSelectColumn !== undefined && autoSelectColumn ? null : row;
|
|
775
|
+
newRanges.push(GridRange.makeNormalized(selectedColumn, selectedRow, selectedColumn, selectedRow));
|
|
776
|
+
return {
|
|
777
|
+
selectedRanges: newRanges,
|
|
778
|
+
selectionEndColumn: column,
|
|
779
|
+
selectionEndRow: row
|
|
780
|
+
};
|
|
781
|
+
});
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
/**
|
|
785
|
+
* Commits the last selected range to the selected ranges.
|
|
786
|
+
* First checks if the last range is completely contained within another range, and if it
|
|
787
|
+
* is then it blows those ranges apart.
|
|
788
|
+
* Then it consolidates all the selected ranges, reducing them.
|
|
789
|
+
*/
|
|
790
|
+
commitSelection() {
|
|
791
|
+
this.setState(state => {
|
|
792
|
+
var {
|
|
793
|
+
theme
|
|
794
|
+
} = this.props;
|
|
795
|
+
var {
|
|
796
|
+
autoSelectRow
|
|
797
|
+
} = theme;
|
|
798
|
+
var {
|
|
799
|
+
selectedRanges,
|
|
800
|
+
lastSelectedRanges,
|
|
801
|
+
cursorRow,
|
|
802
|
+
cursorColumn
|
|
803
|
+
} = state;
|
|
804
|
+
if (selectedRanges.length === 1 && (autoSelectRow !== undefined && autoSelectRow ? GridRange.rowCount(selectedRanges) === 1 : GridRange.cellCount(selectedRanges) === 1) && GridRange.rangeArraysEqual(selectedRanges, lastSelectedRanges)) {
|
|
805
|
+
// If it's the exact same single selection, then deselect.
|
|
806
|
+
// For if we click on one cell multiple times.
|
|
807
|
+
return {
|
|
808
|
+
selectedRanges: [],
|
|
809
|
+
lastSelectedRanges: [],
|
|
810
|
+
cursorColumn: null,
|
|
811
|
+
cursorRow: null
|
|
812
|
+
};
|
|
813
|
+
}
|
|
814
|
+
var newSelectedRanges = selectedRanges.slice();
|
|
815
|
+
if (newSelectedRanges.length > 1) {
|
|
816
|
+
// Check if the latest selection is entirely within a previously selected range
|
|
817
|
+
// If that's the case, then deselect that section instead
|
|
818
|
+
var lastRange = newSelectedRanges[newSelectedRanges.length - 1];
|
|
819
|
+
for (var i = 0; i < newSelectedRanges.length - 1; i += 1) {
|
|
820
|
+
var selectedRange = newSelectedRanges[i];
|
|
821
|
+
if (selectedRange.contains(lastRange)) {
|
|
822
|
+
// We found a match, now remove the two matching ranges, and add back
|
|
823
|
+
// the remainder of the two
|
|
824
|
+
var remainder = selectedRange.subtract(lastRange);
|
|
825
|
+
newSelectedRanges.pop();
|
|
826
|
+
newSelectedRanges.splice(i, 1);
|
|
827
|
+
newSelectedRanges = newSelectedRanges.concat(remainder);
|
|
828
|
+
break;
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
newSelectedRanges = GridRange.consolidate(newSelectedRanges);
|
|
832
|
+
}
|
|
833
|
+
var newCursorColumn = cursorColumn;
|
|
834
|
+
var newCursorRow = cursorRow;
|
|
835
|
+
if (!GridRange.containsCell(newSelectedRanges, cursorColumn, cursorRow)) {
|
|
836
|
+
var {
|
|
837
|
+
model
|
|
838
|
+
} = this.props;
|
|
839
|
+
var {
|
|
840
|
+
columnCount,
|
|
841
|
+
rowCount
|
|
842
|
+
} = model;
|
|
843
|
+
var nextCursor = GridRange.nextCell(GridRange.boundedRanges(selectedRanges, columnCount, rowCount));
|
|
844
|
+
if (nextCursor != null) {
|
|
845
|
+
({
|
|
846
|
+
column: newCursorColumn,
|
|
847
|
+
row: newCursorRow
|
|
848
|
+
} = nextCursor);
|
|
849
|
+
} else {
|
|
850
|
+
newCursorColumn = null;
|
|
851
|
+
newCursorRow = null;
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
if (newSelectedRanges.length === 0) {
|
|
855
|
+
newCursorColumn = null;
|
|
856
|
+
newCursorRow = null;
|
|
857
|
+
}
|
|
858
|
+
return {
|
|
859
|
+
cursorRow: newCursorRow,
|
|
860
|
+
cursorColumn: newCursorColumn,
|
|
861
|
+
selectedRanges: newSelectedRanges,
|
|
862
|
+
lastSelectedRanges: selectedRanges
|
|
863
|
+
};
|
|
864
|
+
});
|
|
865
|
+
}
|
|
866
|
+
setFocusRow(focusedRow) {
|
|
867
|
+
var _userRowHeights$get;
|
|
868
|
+
if (!this.metrics || !this.prevMetrics) {
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
var {
|
|
872
|
+
gridY,
|
|
873
|
+
height,
|
|
874
|
+
lastTop,
|
|
875
|
+
userRowHeights,
|
|
876
|
+
rowHeight
|
|
877
|
+
} = this.metrics;
|
|
878
|
+
var tableHeight = height - gridY;
|
|
879
|
+
var halfViewportHeight = Math.round(tableHeight / 2) + ((_userRowHeights$get = userRowHeights.get(focusedRow)) !== null && _userRowHeights$get !== void 0 ? _userRowHeights$get : rowHeight);
|
|
880
|
+
var metricState = this.getMetricState();
|
|
881
|
+
var newTop = this.metricCalculator.getLastTop(metricState, focusedRow + 1, halfViewportHeight);
|
|
882
|
+
this.setState({
|
|
883
|
+
top: Math.min(lastTop, newTop),
|
|
884
|
+
selectedRanges: [new GridRange(null, focusedRow, null, focusedRow)],
|
|
885
|
+
isStuckToBottom: false
|
|
886
|
+
});
|
|
887
|
+
var {
|
|
888
|
+
cursorColumn
|
|
889
|
+
} = this.state;
|
|
890
|
+
this.moveCursorToPosition(cursorColumn, focusedRow, false, false);
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
/**
|
|
894
|
+
* Set the selection to the entire grid
|
|
895
|
+
*/
|
|
896
|
+
selectAll() {
|
|
897
|
+
var {
|
|
898
|
+
model,
|
|
899
|
+
theme
|
|
900
|
+
} = this.props;
|
|
901
|
+
var {
|
|
902
|
+
autoSelectRow,
|
|
903
|
+
autoSelectColumn
|
|
904
|
+
} = theme;
|
|
905
|
+
var top = autoSelectColumn !== undefined && autoSelectColumn ? null : 0;
|
|
906
|
+
var bottom = autoSelectColumn !== undefined && autoSelectColumn ? null : model.rowCount - 1;
|
|
907
|
+
var left = autoSelectRow !== undefined && autoSelectRow ? null : 0;
|
|
908
|
+
var right = autoSelectRow !== undefined && autoSelectRow ? null : model.columnCount - 1;
|
|
909
|
+
this.setSelectedRanges([new GridRange(left, top, right, bottom)]);
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
/**
|
|
913
|
+
* Move the cursor in relation to the current cursor position
|
|
914
|
+
* @param deltaColumn Number of columns to move the cursor
|
|
915
|
+
* @param deltaRow Number of rows to move the cursor
|
|
916
|
+
* @param extendSelection True if the current selection should be extended, false to start a new selection
|
|
917
|
+
*/
|
|
918
|
+
moveCursor(deltaColumn, deltaRow, extendSelection) {
|
|
919
|
+
var {
|
|
920
|
+
cursorRow,
|
|
921
|
+
cursorColumn,
|
|
922
|
+
selectionEndColumn,
|
|
923
|
+
selectionEndRow
|
|
924
|
+
} = this.state;
|
|
925
|
+
var column = extendSelection ? selectionEndColumn : cursorColumn;
|
|
926
|
+
var row = extendSelection ? selectionEndRow : cursorRow;
|
|
927
|
+
if (row === null || column === null) {
|
|
928
|
+
var {
|
|
929
|
+
left,
|
|
930
|
+
top
|
|
931
|
+
} = this.state;
|
|
932
|
+
this.moveCursorToPosition(left, top, extendSelection);
|
|
933
|
+
} else {
|
|
934
|
+
var {
|
|
935
|
+
model
|
|
936
|
+
} = this.props;
|
|
937
|
+
var {
|
|
938
|
+
columnCount,
|
|
939
|
+
rowCount
|
|
940
|
+
} = model;
|
|
941
|
+
var _left = clamp(column + deltaColumn, 0, columnCount - 1);
|
|
942
|
+
var _top = clamp(row + deltaRow, 0, rowCount - 1);
|
|
943
|
+
this.moveCursorToPosition(_left, _top, extendSelection);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
/**
|
|
948
|
+
* Move the cursor in the provided selection direction
|
|
949
|
+
* @param direction The direction to move the cursor in
|
|
950
|
+
*/
|
|
951
|
+
moveCursorInDirection() {
|
|
952
|
+
var direction = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : GridRange.SELECTION_DIRECTION.DOWN;
|
|
953
|
+
var {
|
|
954
|
+
model
|
|
955
|
+
} = this.props;
|
|
956
|
+
var {
|
|
957
|
+
columnCount,
|
|
958
|
+
rowCount
|
|
959
|
+
} = model;
|
|
960
|
+
var {
|
|
961
|
+
cursorRow,
|
|
962
|
+
cursorColumn,
|
|
963
|
+
selectedRanges
|
|
964
|
+
} = this.state;
|
|
965
|
+
var ranges = selectedRanges.length > 0 ? selectedRanges : [GridRange.makeCell(cursorColumn, cursorRow)];
|
|
966
|
+
var nextCursor = null;
|
|
967
|
+
if (ranges.length === 1 && GridRange.cellCount(ranges) === 1) {
|
|
968
|
+
var _gridRange$nextCell;
|
|
969
|
+
// If we only have one cell selected, we want to update the cursor and we want to update the selected cells
|
|
970
|
+
var gridRange = new GridRange(0, 0, columnCount - 1, rowCount - 1);
|
|
971
|
+
nextCursor = (_gridRange$nextCell = gridRange.nextCell(cursorColumn, cursorRow, direction)) !== null && _gridRange$nextCell !== void 0 ? _gridRange$nextCell : gridRange.startCell(direction);
|
|
972
|
+
} else {
|
|
973
|
+
nextCursor = GridRange.nextCell(GridRange.boundedRanges(ranges, columnCount, rowCount), cursorColumn, cursorRow, direction);
|
|
974
|
+
}
|
|
975
|
+
if (nextCursor != null) {
|
|
976
|
+
var {
|
|
977
|
+
column,
|
|
978
|
+
row
|
|
979
|
+
} = nextCursor;
|
|
980
|
+
this.setState({
|
|
981
|
+
cursorColumn: column,
|
|
982
|
+
cursorRow: row
|
|
983
|
+
});
|
|
984
|
+
if (!GridRange.containsCell(selectedRanges, column, row)) {
|
|
985
|
+
this.setState({
|
|
986
|
+
selectedRanges: [GridRange.makeCell(column, row)],
|
|
987
|
+
selectionStartColumn: column,
|
|
988
|
+
selectionStartRow: row,
|
|
989
|
+
selectionEndColumn: column,
|
|
990
|
+
selectionEndRow: row
|
|
991
|
+
});
|
|
992
|
+
}
|
|
993
|
+
this.moveViewToCell(nextCursor.column, nextCursor.row);
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
/**
|
|
998
|
+
* Move a cursor to the specified position in the grid.
|
|
999
|
+
* @param column The column index to move the cursor to
|
|
1000
|
+
* @param row The row index to move the cursor to
|
|
1001
|
+
* @param extendSelection Whether to extend the current selection (eg. holding Shift)
|
|
1002
|
+
* @param keepCursorInView Whether to move the viewport so that the cursor is in view
|
|
1003
|
+
* @param maximizePreviousRange With this and `extendSelection` true, it will maximize/add to the previous range only, ignoring where the selection was started
|
|
1004
|
+
*/
|
|
1005
|
+
moveCursorToPosition(column, row) {
|
|
1006
|
+
var extendSelection = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
|
1007
|
+
var keepCursorInView = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
|
|
1008
|
+
var maximizePreviousRange = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
|
|
1009
|
+
if (!extendSelection) {
|
|
1010
|
+
this.beginSelection(column, row);
|
|
1011
|
+
}
|
|
1012
|
+
this.moveSelection(column, row, extendSelection, maximizePreviousRange);
|
|
1013
|
+
if (keepCursorInView) {
|
|
1014
|
+
this.moveViewToCell(column, row);
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
/**
|
|
1019
|
+
* Moves the view to make the specified cell visible
|
|
1020
|
+
*
|
|
1021
|
+
* @param column The column index to bring into view
|
|
1022
|
+
* @param row The row index to bring into view
|
|
1023
|
+
*/
|
|
1024
|
+
moveViewToCell(column, row) {
|
|
1025
|
+
if (!this.metrics) throw new Error('metrics not set');
|
|
1026
|
+
var {
|
|
1027
|
+
metricCalculator
|
|
1028
|
+
} = this;
|
|
1029
|
+
var {
|
|
1030
|
+
bottomVisible,
|
|
1031
|
+
rightVisible,
|
|
1032
|
+
topVisible,
|
|
1033
|
+
leftVisible
|
|
1034
|
+
} = this.metrics;
|
|
1035
|
+
var metricState = this.getMetricState(this.state);
|
|
1036
|
+
var {
|
|
1037
|
+
top,
|
|
1038
|
+
left,
|
|
1039
|
+
topOffset,
|
|
1040
|
+
leftOffset
|
|
1041
|
+
} = this.state;
|
|
1042
|
+
if (row != null && !GridUtils.isFloatingRow(row, this.metrics)) {
|
|
1043
|
+
if (row < topVisible) {
|
|
1044
|
+
top = metricCalculator.getTopForTopVisible(metricState, row);
|
|
1045
|
+
topOffset = 0;
|
|
1046
|
+
} else if (row > bottomVisible) {
|
|
1047
|
+
top = metricCalculator.getTopForBottomVisible(metricState, row);
|
|
1048
|
+
topOffset = 0;
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
if (column != null && !GridUtils.isFloatingColumn(column, this.metrics)) {
|
|
1052
|
+
if (column < leftVisible) {
|
|
1053
|
+
left = metricCalculator.getLeftForLeftVisible(metricState, column);
|
|
1054
|
+
leftOffset = 0;
|
|
1055
|
+
} else if (column > rightVisible) {
|
|
1056
|
+
left = metricCalculator.getLeftForRightVisible(metricState, column);
|
|
1057
|
+
leftOffset = 0;
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
this.setViewState({
|
|
1061
|
+
top,
|
|
1062
|
+
left,
|
|
1063
|
+
topOffset,
|
|
1064
|
+
leftOffset
|
|
1065
|
+
});
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
/**
|
|
1069
|
+
* Checks the `top` and `left` properties that are set and updates the isStuckToBottom/Right properties
|
|
1070
|
+
* Should be called when user interaction occurs
|
|
1071
|
+
* @param viewState New state properties to set.
|
|
1072
|
+
* @param forceUpdate Whether to force an update.
|
|
1073
|
+
*/
|
|
1074
|
+
setViewState(viewState) {
|
|
1075
|
+
var forceUpdate = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
|
1076
|
+
if (!this.metrics) throw new Error('metrics not set');
|
|
1077
|
+
var {
|
|
1078
|
+
isStickyBottom,
|
|
1079
|
+
isStickyRight
|
|
1080
|
+
} = this.props;
|
|
1081
|
+
var {
|
|
1082
|
+
top,
|
|
1083
|
+
left
|
|
1084
|
+
} = viewState;
|
|
1085
|
+
var {
|
|
1086
|
+
lastTop,
|
|
1087
|
+
lastLeft
|
|
1088
|
+
} = this.metrics;
|
|
1089
|
+
if (top != null) {
|
|
1090
|
+
this.setState({
|
|
1091
|
+
isStuckToBottom: isStickyBottom && top >= lastTop
|
|
1092
|
+
});
|
|
1093
|
+
}
|
|
1094
|
+
if (left != null) {
|
|
1095
|
+
this.setState({
|
|
1096
|
+
isStuckToRight: isStickyRight && left >= lastLeft
|
|
1097
|
+
});
|
|
1098
|
+
}
|
|
1099
|
+
this.setState(viewState);
|
|
1100
|
+
if (forceUpdate) {
|
|
1101
|
+
this.forceUpdate();
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
/**
|
|
1106
|
+
* Start editing the data at the given index
|
|
1107
|
+
*
|
|
1108
|
+
* @param column The visible column index to start editing
|
|
1109
|
+
* @param row The visible row index to start editing
|
|
1110
|
+
* @param isQuickEdit If this is a quick edit (the arrow keys can commit)
|
|
1111
|
+
* @param selectionRange The tuple [start,end] text selection range of the value to select when editing
|
|
1112
|
+
* @param value The value to start with in the edit field. Leave undefined to use the current value.
|
|
1113
|
+
*/
|
|
1114
|
+
startEditing(column, row) {
|
|
1115
|
+
var isQuickEdit = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
|
1116
|
+
var selectionRange = arguments.length > 3 ? arguments[3] : undefined;
|
|
1117
|
+
var value = arguments.length > 4 ? arguments[4] : undefined;
|
|
1118
|
+
var {
|
|
1119
|
+
model
|
|
1120
|
+
} = this.props;
|
|
1121
|
+
if (!isEditableGridModel(model)) throw new Error('model is not editable');
|
|
1122
|
+
var modelColumn = this.getModelColumn(column);
|
|
1123
|
+
var modelRow = this.getModelRow(row);
|
|
1124
|
+
var cell = {
|
|
1125
|
+
column,
|
|
1126
|
+
row,
|
|
1127
|
+
selectionRange,
|
|
1128
|
+
value: value !== undefined ? value : model.editValueForCell(modelColumn, modelRow),
|
|
1129
|
+
isQuickEdit
|
|
1130
|
+
};
|
|
1131
|
+
this.setState({
|
|
1132
|
+
editingCell: cell,
|
|
1133
|
+
cursorColumn: column,
|
|
1134
|
+
cursorRow: row
|
|
1135
|
+
});
|
|
1136
|
+
this.moveViewToCell(column, row);
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
/**
|
|
1140
|
+
* Check if a value is valid for a specific cell
|
|
1141
|
+
* @param column Column index of the cell to check
|
|
1142
|
+
* @param row Row index of the cell to check
|
|
1143
|
+
* @param value Value to check
|
|
1144
|
+
* @returns True if the value is valid for the provided cell, false otherwise
|
|
1145
|
+
*/
|
|
1146
|
+
isValidForCell(column, row, value) {
|
|
1147
|
+
var {
|
|
1148
|
+
model
|
|
1149
|
+
} = this.props;
|
|
1150
|
+
var modelColumn = this.getModelColumn(column);
|
|
1151
|
+
var modelRow = this.getModelRow(row);
|
|
1152
|
+
return isEditableGridModel(model) && model.isValidForCell(modelColumn, modelRow, value);
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
/**
|
|
1156
|
+
* Paste a value with the current selection
|
|
1157
|
+
* It first needs to validate that the pasted table is valid for the given selection.
|
|
1158
|
+
* Also may update selection if single cells are selected and a table is pasted.
|
|
1159
|
+
* @param value Table or a string that is being pasted
|
|
1160
|
+
*/
|
|
1161
|
+
pasteValue(value) {
|
|
1162
|
+
var _this = this;
|
|
1163
|
+
return _asyncToGenerator(function* () {
|
|
1164
|
+
var {
|
|
1165
|
+
model
|
|
1166
|
+
} = _this.props;
|
|
1167
|
+
var {
|
|
1168
|
+
movedColumns,
|
|
1169
|
+
movedRows,
|
|
1170
|
+
selectedRanges
|
|
1171
|
+
} = _this.state;
|
|
1172
|
+
try {
|
|
1173
|
+
assertIsEditableGridModel(model);
|
|
1174
|
+
if (!model.isEditable || !selectedRanges.every(range => model.isEditableRange(range))) {
|
|
1175
|
+
throw new PasteError("Can't paste in to read-only area.");
|
|
1176
|
+
}
|
|
1177
|
+
if (selectedRanges.length <= 0) {
|
|
1178
|
+
throw new PasteError('Select an area to paste to.');
|
|
1179
|
+
}
|
|
1180
|
+
if (typeof value === 'string') {
|
|
1181
|
+
// Just paste the value into all the selected cells
|
|
1182
|
+
var _edits = [];
|
|
1183
|
+
var modelRanges = GridUtils.getModelRanges(selectedRanges, movedColumns, movedRows);
|
|
1184
|
+
GridRange.forEachCell(modelRanges, (column, row) => {
|
|
1185
|
+
_edits.push({
|
|
1186
|
+
column,
|
|
1187
|
+
row,
|
|
1188
|
+
text: value
|
|
1189
|
+
});
|
|
1190
|
+
});
|
|
1191
|
+
yield model.setValues(_edits);
|
|
1192
|
+
return;
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
// Otherwise it's a table of data
|
|
1196
|
+
var tableHeight = value.length;
|
|
1197
|
+
var tableWidth = value[0].length;
|
|
1198
|
+
var {
|
|
1199
|
+
columnCount,
|
|
1200
|
+
rowCount
|
|
1201
|
+
} = model;
|
|
1202
|
+
var _ranges = selectedRanges;
|
|
1203
|
+
// If each cell is a single selection, we need to update the selection to map to the newly pasted data
|
|
1204
|
+
if (_ranges.every(range => {
|
|
1205
|
+
var _range$startColumn, _range$startRow;
|
|
1206
|
+
return GridRange.cellCount([range]) === 1 && ((_range$startColumn = range.startColumn) !== null && _range$startColumn !== void 0 ? _range$startColumn : 0) + tableWidth <= columnCount && ((_range$startRow = range.startRow) !== null && _range$startRow !== void 0 ? _range$startRow : 0) + tableHeight <= rowCount;
|
|
1207
|
+
})) {
|
|
1208
|
+
// Remap the selected ranges
|
|
1209
|
+
_ranges = _ranges.map(range => {
|
|
1210
|
+
var _range$startColumn2, _range$startRow2;
|
|
1211
|
+
return new GridRange(range.startColumn, range.startRow, ((_range$startColumn2 = range.startColumn) !== null && _range$startColumn2 !== void 0 ? _range$startColumn2 : 0) + tableWidth - 1, ((_range$startRow2 = range.startRow) !== null && _range$startRow2 !== void 0 ? _range$startRow2 : 0) + tableHeight - 1);
|
|
1212
|
+
});
|
|
1213
|
+
_this.setSelectedRanges(_ranges);
|
|
1214
|
+
}
|
|
1215
|
+
if (!_ranges.every(range => GridRange.rowCount([range]) === tableHeight && GridRange.columnCount([range]) === tableWidth)) {
|
|
1216
|
+
throw new PasteError('Copy and paste area are not same size.');
|
|
1217
|
+
}
|
|
1218
|
+
var edits = [];
|
|
1219
|
+
_ranges.forEach(range => {
|
|
1220
|
+
for (var x = 0; x < tableWidth; x += 1) {
|
|
1221
|
+
for (var y = 0; y < tableHeight; y += 1) {
|
|
1222
|
+
var _range$startColumn3, _range$startRow3;
|
|
1223
|
+
edits.push({
|
|
1224
|
+
column: ((_range$startColumn3 = range.startColumn) !== null && _range$startColumn3 !== void 0 ? _range$startColumn3 : 0) + x,
|
|
1225
|
+
row: ((_range$startRow3 = range.startRow) !== null && _range$startRow3 !== void 0 ? _range$startRow3 : 0) + y,
|
|
1226
|
+
text: value[y][x]
|
|
1227
|
+
});
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
});
|
|
1231
|
+
yield model.setValues(edits);
|
|
1232
|
+
} catch (e) {
|
|
1233
|
+
var {
|
|
1234
|
+
onError
|
|
1235
|
+
} = _this.props;
|
|
1236
|
+
onError(e instanceof Error ? e : new Error("".concat(e)));
|
|
1237
|
+
}
|
|
1238
|
+
})();
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
/**
|
|
1242
|
+
* Set a value to a specific cell. If the value is not valid for that cell, do not set it
|
|
1243
|
+
* @param column Column index to set the value for
|
|
1244
|
+
* @param row Row index to set the value for
|
|
1245
|
+
* @param value Value to set at that cell
|
|
1246
|
+
* @returns true If the value was valid and attempted to be set, false is it was not valid
|
|
1247
|
+
*/
|
|
1248
|
+
setValueForCell(column, row, value) {
|
|
1249
|
+
var {
|
|
1250
|
+
model
|
|
1251
|
+
} = this.props;
|
|
1252
|
+
assertIsEditableGridModel(model);
|
|
1253
|
+
var modelColumn = this.getModelColumn(column);
|
|
1254
|
+
var modelRow = this.getModelRow(row);
|
|
1255
|
+
if (model.isValidForCell(modelColumn, modelRow, value)) {
|
|
1256
|
+
model.setValueForCell(modelColumn, modelRow, value);
|
|
1257
|
+
return true;
|
|
1258
|
+
}
|
|
1259
|
+
return false;
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
/**
|
|
1263
|
+
* Set a value on all the ranges provided
|
|
1264
|
+
* @param ranges Ranges to set
|
|
1265
|
+
* @param value The value to set on all the ranges
|
|
1266
|
+
*/
|
|
1267
|
+
setValueForRanges(ranges, value) {
|
|
1268
|
+
var {
|
|
1269
|
+
model
|
|
1270
|
+
} = this.props;
|
|
1271
|
+
var {
|
|
1272
|
+
movedColumns,
|
|
1273
|
+
movedRows
|
|
1274
|
+
} = this.state;
|
|
1275
|
+
var modelRanges = GridUtils.getModelRanges(ranges, movedColumns, movedRows);
|
|
1276
|
+
if (isEditableGridModel(model)) {
|
|
1277
|
+
model.setValueForRanges(modelRanges, value);
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
/**
|
|
1282
|
+
* Check if a given cell is within the current selection
|
|
1283
|
+
* @param row Row to check
|
|
1284
|
+
* @param column Column to check
|
|
1285
|
+
* @returns True if the cell is in the current selection, false otherwise
|
|
1286
|
+
*/
|
|
1287
|
+
isSelected(row, column) {
|
|
1288
|
+
var {
|
|
1289
|
+
selectedRanges
|
|
1290
|
+
} = this.state;
|
|
1291
|
+
for (var i = 0; i < selectedRanges.length; i += 1) {
|
|
1292
|
+
var _selectedRange$endRow, _selectedRange$endCol;
|
|
1293
|
+
var selectedRange = selectedRanges[i];
|
|
1294
|
+
var rowSelected = selectedRange.startRow === null || selectedRange.startRow <= row && row <= ((_selectedRange$endRow = selectedRange.endRow) !== null && _selectedRange$endRow !== void 0 ? _selectedRange$endRow : 0);
|
|
1295
|
+
var columnSelected = selectedRange.startColumn === null || selectedRange.startColumn <= column && column <= ((_selectedRange$endCol = selectedRange.endColumn) !== null && _selectedRange$endCol !== void 0 ? _selectedRange$endCol : 0);
|
|
1296
|
+
if (rowSelected && columnSelected) {
|
|
1297
|
+
return true;
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
return false;
|
|
1301
|
+
}
|
|
1302
|
+
addDocumentCursor() {
|
|
1303
|
+
var cursor = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
|
|
1304
|
+
if (this.documentCursor === Grid.getCursorClassName(cursor)) return;
|
|
1305
|
+
if (this.documentCursor != null) {
|
|
1306
|
+
document.documentElement.classList.remove(this.documentCursor);
|
|
1307
|
+
}
|
|
1308
|
+
this.documentCursor = Grid.getCursorClassName(cursor);
|
|
1309
|
+
if (this.documentCursor != null) {
|
|
1310
|
+
document.documentElement.classList.add(this.documentCursor);
|
|
1311
|
+
}
|
|
1312
|
+
document.documentElement.classList.add('grid-block-events');
|
|
1313
|
+
}
|
|
1314
|
+
removeDocumentCursor() {
|
|
1315
|
+
if (this.documentCursor != null) {
|
|
1316
|
+
document.documentElement.classList.remove(this.documentCursor);
|
|
1317
|
+
document.documentElement.classList.remove('grid-block-events');
|
|
1318
|
+
this.documentCursor = null;
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
startDragTimer(event) {
|
|
1322
|
+
this.stopDragTimer();
|
|
1323
|
+
var mouseEvent = new MouseEvent('custom', event.nativeEvent);
|
|
1324
|
+
this.dragTimer = setTimeout(() => {
|
|
1325
|
+
this.handleMouseDrag(mouseEvent);
|
|
1326
|
+
}, Grid.dragTimeout);
|
|
1327
|
+
}
|
|
1328
|
+
stopDragTimer() {
|
|
1329
|
+
if (this.dragTimer) {
|
|
1330
|
+
clearTimeout(this.dragTimer);
|
|
1331
|
+
this.dragTimer = null;
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
/**
|
|
1336
|
+
* Draw the grid with the metrics provided
|
|
1337
|
+
* When scrolling you've have to re-draw the whole canvas. As a consequence, all these drawing methods
|
|
1338
|
+
* must be very quick.
|
|
1339
|
+
* @param metrics Metrics to use for rendering the grid
|
|
1340
|
+
*/
|
|
1341
|
+
drawCanvas() {
|
|
1342
|
+
var metrics = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.updateMetrics();
|
|
1343
|
+
if (!this.canvas) throw new Error('canvas is not set');
|
|
1344
|
+
if (!this.canvasContext) throw new Error('context not set');
|
|
1345
|
+
var {
|
|
1346
|
+
renderer,
|
|
1347
|
+
canvasContext: context,
|
|
1348
|
+
renderState
|
|
1349
|
+
} = this;
|
|
1350
|
+
context.save();
|
|
1351
|
+
renderer.drawCanvas(renderState);
|
|
1352
|
+
context.restore();
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
/**
|
|
1356
|
+
* Set focus to this grid element
|
|
1357
|
+
*/
|
|
1358
|
+
focus() {
|
|
1359
|
+
var _this$canvas3;
|
|
1360
|
+
(_this$canvas3 = this.canvas) === null || _this$canvas3 === void 0 ? void 0 : _this$canvas3.focus();
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
/**
|
|
1364
|
+
* Check if this grid is currently focused
|
|
1365
|
+
* @returns True if the active element is this grid
|
|
1366
|
+
*/
|
|
1367
|
+
isFocused() {
|
|
1368
|
+
return document.activeElement === this.canvas;
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
/**
|
|
1372
|
+
* Handle a mouse click event. Pass the event to the registered mouse handlers until one handles it.
|
|
1373
|
+
* Focuses the grid after the click.
|
|
1374
|
+
* @param event The mouse event
|
|
1375
|
+
*/
|
|
1376
|
+
handleClick(event) {
|
|
1377
|
+
var _this$canvas4;
|
|
1378
|
+
var gridPoint = this.getGridPointFromEvent(event);
|
|
1379
|
+
var mouseHandlers = this.getMouseHandlers();
|
|
1380
|
+
for (var i = 0; i < mouseHandlers.length; i += 1) {
|
|
1381
|
+
var mouseHandler = mouseHandlers[i];
|
|
1382
|
+
if (mouseHandler.onClick(gridPoint, this, event) !== false) {
|
|
1383
|
+
event.stopPropagation();
|
|
1384
|
+
event.preventDefault();
|
|
1385
|
+
break;
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
(_this$canvas4 = this.canvas) === null || _this$canvas4 === void 0 ? void 0 : _this$canvas4.focus();
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
/**
|
|
1392
|
+
* Handle a mouse context menu event. Pass the event to the registered mouse handlers until one handles it.
|
|
1393
|
+
* @param event The mouse event triggering the context menu
|
|
1394
|
+
*/
|
|
1395
|
+
handleContextMenu(event) {
|
|
1396
|
+
var gridPoint = this.getGridPointFromEvent(event);
|
|
1397
|
+
var mouseHandlers = this.getMouseHandlers();
|
|
1398
|
+
for (var i = 0; i < mouseHandlers.length; i += 1) {
|
|
1399
|
+
var mouseHandler = mouseHandlers[i];
|
|
1400
|
+
if (mouseHandler.onContextMenu(gridPoint, this, event) !== false) {
|
|
1401
|
+
event.stopPropagation();
|
|
1402
|
+
event.preventDefault();
|
|
1403
|
+
break;
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
/**
|
|
1409
|
+
* Handle a key down event from the keyboard. Pass the event to the registered keyboard handlers until one handles it.
|
|
1410
|
+
* @param event Keyboard event
|
|
1411
|
+
*/
|
|
1412
|
+
handleKeyDown(event) {
|
|
1413
|
+
var keyHandlers = this.getKeyHandlers();
|
|
1414
|
+
for (var i = 0; i < keyHandlers.length; i += 1) {
|
|
1415
|
+
var keyHandler = keyHandlers[i];
|
|
1416
|
+
var result = keyHandler.onDown(event, this);
|
|
1417
|
+
if (result !== false) {
|
|
1418
|
+
var _options$stopPropagat, _options$preventDefau;
|
|
1419
|
+
var options = result;
|
|
1420
|
+
if ((_options$stopPropagat = options === null || options === void 0 ? void 0 : options.stopPropagation) !== null && _options$stopPropagat !== void 0 ? _options$stopPropagat : true) event.stopPropagation();
|
|
1421
|
+
if ((_options$preventDefau = options === null || options === void 0 ? void 0 : options.preventDefault) !== null && _options$preventDefau !== void 0 ? _options$preventDefau : true) event.preventDefault();
|
|
1422
|
+
break;
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
/**
|
|
1428
|
+
* Notify all of the mouse handlers for this grid of a mouse event.
|
|
1429
|
+
* @param functionName The name of the function in the mouse handler to call
|
|
1430
|
+
* @param event The mouse event to notify
|
|
1431
|
+
* @param updateCoordinates Whether to update the mouse coordinates
|
|
1432
|
+
* @param addCursorToDocument Whether to add a cursor overlay or not (for dragging)
|
|
1433
|
+
*/
|
|
1434
|
+
notifyMouseHandlers(functionName, event) {
|
|
1435
|
+
var updateCoordinates = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
|
|
1436
|
+
var addCursorToDocument = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
|
|
1437
|
+
var gridPoint = this.getGridPointFromEvent(event);
|
|
1438
|
+
var mouseHandlers = this.getMouseHandlers();
|
|
1439
|
+
var cursor = null;
|
|
1440
|
+
for (var i = 0; i < mouseHandlers.length; i += 1) {
|
|
1441
|
+
var mouseHandler = mouseHandlers[i];
|
|
1442
|
+
var result = mouseHandler[functionName] != null && mouseHandler[functionName](gridPoint, this, event);
|
|
1443
|
+
if (result !== false) {
|
|
1444
|
+
var _options$stopPropagat2, _options$preventDefau2;
|
|
1445
|
+
if (mouseHandler.cursor != null) {
|
|
1446
|
+
({
|
|
1447
|
+
cursor
|
|
1448
|
+
} = mouseHandler);
|
|
1449
|
+
if (addCursorToDocument) {
|
|
1450
|
+
this.addDocumentCursor(cursor);
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
// result is bool or object, events are stopped by default
|
|
1455
|
+
var options = result;
|
|
1456
|
+
if ((_options$stopPropagat2 = options === null || options === void 0 ? void 0 : options.stopPropagation) !== null && _options$stopPropagat2 !== void 0 ? _options$stopPropagat2 : true) event.stopPropagation();
|
|
1457
|
+
if ((_options$preventDefau2 = options === null || options === void 0 ? void 0 : options.preventDefault) !== null && _options$preventDefau2 !== void 0 ? _options$preventDefau2 : true) event.preventDefault();
|
|
1458
|
+
break;
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
this.setState({
|
|
1462
|
+
cursor
|
|
1463
|
+
});
|
|
1464
|
+
if (updateCoordinates) {
|
|
1465
|
+
var {
|
|
1466
|
+
x,
|
|
1467
|
+
y
|
|
1468
|
+
} = gridPoint;
|
|
1469
|
+
this.setState({
|
|
1470
|
+
mouseX: x,
|
|
1471
|
+
mouseY: y
|
|
1472
|
+
});
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
handleMouseDown(event) {
|
|
1476
|
+
window.addEventListener('mousemove', this.handleMouseDrag, true);
|
|
1477
|
+
window.addEventListener('mouseup', this.handleMouseUp, true);
|
|
1478
|
+
if (event.button != null && event.button !== 0) {
|
|
1479
|
+
return;
|
|
1480
|
+
}
|
|
1481
|
+
this.notifyMouseHandlers('onDown', event);
|
|
1482
|
+
this.startDragTimer(event);
|
|
1483
|
+
}
|
|
1484
|
+
handleDoubleClick(event) {
|
|
1485
|
+
this.notifyMouseHandlers('onDoubleClick', event);
|
|
1486
|
+
}
|
|
1487
|
+
handleMouseMove(event) {
|
|
1488
|
+
this.notifyMouseHandlers('onMove', event);
|
|
1489
|
+
}
|
|
1490
|
+
handleMouseLeave(event) {
|
|
1491
|
+
this.notifyMouseHandlers('onLeave', event, false);
|
|
1492
|
+
this.setState({
|
|
1493
|
+
mouseX: null,
|
|
1494
|
+
mouseY: null
|
|
1495
|
+
});
|
|
1496
|
+
}
|
|
1497
|
+
handleMouseDrag(event) {
|
|
1498
|
+
this.setState({
|
|
1499
|
+
isDragging: true
|
|
1500
|
+
});
|
|
1501
|
+
this.notifyMouseHandlers('onDrag', event, true, true);
|
|
1502
|
+
this.stopDragTimer();
|
|
1503
|
+
}
|
|
1504
|
+
handleMouseUp(event) {
|
|
1505
|
+
window.removeEventListener('mousemove', this.handleMouseDrag, true);
|
|
1506
|
+
window.removeEventListener('mouseup', this.handleMouseUp, true);
|
|
1507
|
+
if (event.button != null && event.button !== 0) {
|
|
1508
|
+
return;
|
|
1509
|
+
}
|
|
1510
|
+
this.notifyMouseHandlers('onUp', event, false);
|
|
1511
|
+
this.stopDragTimer();
|
|
1512
|
+
this.removeDocumentCursor();
|
|
1513
|
+
}
|
|
1514
|
+
handleResize() {
|
|
1515
|
+
/**
|
|
1516
|
+
* We need to always redraw the canvas in the same frame as the updateCanvasScale
|
|
1517
|
+
* because it clears the canvas by nature of direct dom manipulation. However,
|
|
1518
|
+
* We also need to verify the state/metrics, which we currently have no way
|
|
1519
|
+
* of doing outside of a full componentDidUpdate() call, so we force the update.
|
|
1520
|
+
* Ideally, we could verify state/metrics without the forced update.
|
|
1521
|
+
*/
|
|
1522
|
+
this.updateCanvas();
|
|
1523
|
+
if (!this.metrics) throw new Error('metrics not set');
|
|
1524
|
+
this.forceUpdate();
|
|
1525
|
+
}
|
|
1526
|
+
handleWheel(event) {
|
|
1527
|
+
this.notifyMouseHandlers('onWheel', event);
|
|
1528
|
+
if (event.defaultPrevented) {
|
|
1529
|
+
return;
|
|
1530
|
+
}
|
|
1531
|
+
var {
|
|
1532
|
+
metricCalculator,
|
|
1533
|
+
metrics
|
|
1534
|
+
} = this;
|
|
1535
|
+
var metricState = this.getMetricState();
|
|
1536
|
+
if (!metrics) throw new Error('metrics not set');
|
|
1537
|
+
var {
|
|
1538
|
+
lastTop,
|
|
1539
|
+
lastLeft,
|
|
1540
|
+
columnCount,
|
|
1541
|
+
rowCount,
|
|
1542
|
+
scrollableContentWidth,
|
|
1543
|
+
scrollableViewportWidth,
|
|
1544
|
+
scrollableContentHeight,
|
|
1545
|
+
scrollableViewportHeight,
|
|
1546
|
+
hasHorizontalBar,
|
|
1547
|
+
hasVerticalBar
|
|
1548
|
+
} = metrics;
|
|
1549
|
+
var {
|
|
1550
|
+
top,
|
|
1551
|
+
left,
|
|
1552
|
+
topOffset,
|
|
1553
|
+
leftOffset
|
|
1554
|
+
} = metrics;
|
|
1555
|
+
var theme = this.getTheme();
|
|
1556
|
+
var {
|
|
1557
|
+
deltaX,
|
|
1558
|
+
deltaY
|
|
1559
|
+
} = GridUtils.getScrollDelta(event, metrics.barWidth, metrics.barHeight, metrics.rowHeight, metrics.rowHeight);
|
|
1560
|
+
|
|
1561
|
+
// iterate through each column to determine column width and figure out how far to scroll
|
|
1562
|
+
// get column width of next column to scroll to, and subract it from the remaining distance to travel
|
|
1563
|
+
while (hasHorizontalBar && deltaX !== 0) {
|
|
1564
|
+
leftOffset += deltaX;
|
|
1565
|
+
deltaX = 0;
|
|
1566
|
+
if (columnCount > 1) {
|
|
1567
|
+
// no scrolling needed, at directional edge
|
|
1568
|
+
if (leftOffset > 0 && left >= lastLeft || leftOffset < 0 && left <= 0) {
|
|
1569
|
+
leftOffset = 0;
|
|
1570
|
+
break;
|
|
1571
|
+
}
|
|
1572
|
+
} else {
|
|
1573
|
+
// single column at edge
|
|
1574
|
+
if (leftOffset <= 0) {
|
|
1575
|
+
leftOffset = 0;
|
|
1576
|
+
break;
|
|
1577
|
+
}
|
|
1578
|
+
var maxLeftOffset = scrollableContentWidth - scrollableViewportWidth;
|
|
1579
|
+
if (leftOffset >= maxLeftOffset) {
|
|
1580
|
+
leftOffset = maxLeftOffset;
|
|
1581
|
+
break;
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
if (leftOffset > 0) {
|
|
1585
|
+
var _metrics$allColumnWid;
|
|
1586
|
+
// scroll right
|
|
1587
|
+
|
|
1588
|
+
// get width of next column
|
|
1589
|
+
var columnWidth = (_metrics$allColumnWid = metrics.allColumnWidths.get(left)) !== null && _metrics$allColumnWid !== void 0 ? _metrics$allColumnWid : metricCalculator.getVisibleColumnWidth(left, metricState);
|
|
1590
|
+
if (leftOffset >= columnWidth) {
|
|
1591
|
+
// remove width from balance and advance by 1 column
|
|
1592
|
+
deltaX = leftOffset - columnWidth;
|
|
1593
|
+
leftOffset = 0;
|
|
1594
|
+
left += 1;
|
|
1595
|
+
} else if (theme.scrollSnapToColumn && columnCount > 1) {
|
|
1596
|
+
// if there's still a balance to travel but its less then a column and snapping is on
|
|
1597
|
+
leftOffset = 0;
|
|
1598
|
+
left += 1;
|
|
1599
|
+
}
|
|
1600
|
+
} else if (leftOffset < 0) {
|
|
1601
|
+
var _metrics$allColumnWid2;
|
|
1602
|
+
// scroll left
|
|
1603
|
+
|
|
1604
|
+
// get width of next column
|
|
1605
|
+
var _columnWidth = (_metrics$allColumnWid2 = metrics.allColumnWidths.get(left - 1)) !== null && _metrics$allColumnWid2 !== void 0 ? _metrics$allColumnWid2 : metricCalculator.getVisibleColumnWidth(left - 1, metricState);
|
|
1606
|
+
if (Math.abs(leftOffset) <= _columnWidth && theme.scrollSnapToColumn && columnCount > 1) {
|
|
1607
|
+
// if there's still a balance to travel but its less then a column and snapping is on
|
|
1608
|
+
leftOffset = 0;
|
|
1609
|
+
left -= 1;
|
|
1610
|
+
} else {
|
|
1611
|
+
// remove width from balance and advance by 1 column
|
|
1612
|
+
deltaX = leftOffset + _columnWidth;
|
|
1613
|
+
leftOffset = 0;
|
|
1614
|
+
left -= 1;
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
// iterate through each row to determine row height and figure out how far to scroll
|
|
1620
|
+
// get row height of next row to scroll to, and subract it from the remaining distance to travel
|
|
1621
|
+
while (hasVerticalBar && deltaY !== 0) {
|
|
1622
|
+
topOffset += deltaY;
|
|
1623
|
+
deltaY = 0;
|
|
1624
|
+
if (rowCount > 1) {
|
|
1625
|
+
// no scrolling needed, at directional edge
|
|
1626
|
+
if (topOffset > 0 && top >= lastTop || topOffset < 0 && top <= 0) {
|
|
1627
|
+
topOffset = 0;
|
|
1628
|
+
break;
|
|
1629
|
+
}
|
|
1630
|
+
} else {
|
|
1631
|
+
// single row at edge
|
|
1632
|
+
if (topOffset <= 0) {
|
|
1633
|
+
topOffset = 0;
|
|
1634
|
+
break;
|
|
1635
|
+
}
|
|
1636
|
+
var maxTopOffset = scrollableContentHeight - scrollableViewportHeight;
|
|
1637
|
+
if (topOffset >= maxTopOffset) {
|
|
1638
|
+
topOffset = maxTopOffset;
|
|
1639
|
+
break;
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
if (topOffset > 0) {
|
|
1643
|
+
var _metrics$allRowHeight;
|
|
1644
|
+
// scroll direction down
|
|
1645
|
+
|
|
1646
|
+
// get height of next row
|
|
1647
|
+
var rowHeight = (_metrics$allRowHeight = metrics.allRowHeights.get(top)) !== null && _metrics$allRowHeight !== void 0 ? _metrics$allRowHeight : metricCalculator.getVisibleRowHeight(top, metricState);
|
|
1648
|
+
if (topOffset >= rowHeight) {
|
|
1649
|
+
// remove height from balance and advance by 1 row
|
|
1650
|
+
deltaY = topOffset - rowHeight;
|
|
1651
|
+
topOffset = 0;
|
|
1652
|
+
top += 1;
|
|
1653
|
+
} else if (theme.scrollSnapToRow && rowCount > 1) {
|
|
1654
|
+
// if there's still a balance to travel but its less then a row and snapping is on
|
|
1655
|
+
topOffset = 0;
|
|
1656
|
+
top += 1;
|
|
1657
|
+
}
|
|
1658
|
+
} else if (topOffset < 0) {
|
|
1659
|
+
var _metrics$allRowHeight2;
|
|
1660
|
+
// scroll direction up
|
|
1661
|
+
|
|
1662
|
+
// get height of next row
|
|
1663
|
+
var _rowHeight = (_metrics$allRowHeight2 = metrics.allRowHeights.get(top - 1)) !== null && _metrics$allRowHeight2 !== void 0 ? _metrics$allRowHeight2 : metricCalculator.getVisibleRowHeight(top - 1, metricState);
|
|
1664
|
+
if (Math.abs(topOffset) <= _rowHeight && theme.scrollSnapToRow && rowCount > 1) {
|
|
1665
|
+
// if there's still a balance to travel but its less then a row and snapping is on
|
|
1666
|
+
topOffset = 0;
|
|
1667
|
+
top -= 1;
|
|
1668
|
+
} else {
|
|
1669
|
+
// remove height from balance and advance by 1 row
|
|
1670
|
+
deltaY = topOffset + _rowHeight;
|
|
1671
|
+
topOffset = 0;
|
|
1672
|
+
top -= 1;
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
this.setViewState({
|
|
1677
|
+
top,
|
|
1678
|
+
left,
|
|
1679
|
+
leftOffset,
|
|
1680
|
+
topOffset
|
|
1681
|
+
});
|
|
1682
|
+
event.stopPropagation();
|
|
1683
|
+
event.preventDefault();
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1686
|
+
/**
|
|
1687
|
+
* Handle cancelling the cell edit action
|
|
1688
|
+
*/
|
|
1689
|
+
handleEditCellCancel() {
|
|
1690
|
+
this.setState({
|
|
1691
|
+
editingCell: null
|
|
1692
|
+
});
|
|
1693
|
+
this.focus();
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
/**
|
|
1697
|
+
* Handle a change in the value in an editing cell
|
|
1698
|
+
* @param value New value set
|
|
1699
|
+
*/
|
|
1700
|
+
handleEditCellChange(value) {
|
|
1701
|
+
this.setState(_ref5 => {
|
|
1702
|
+
var {
|
|
1703
|
+
editingCell
|
|
1704
|
+
} = _ref5;
|
|
1705
|
+
try {
|
|
1706
|
+
assertIsDefined(editingCell);
|
|
1707
|
+
return {
|
|
1708
|
+
editingCell: _objectSpread(_objectSpread({}, editingCell), {}, {
|
|
1709
|
+
value
|
|
1710
|
+
})
|
|
1711
|
+
};
|
|
1712
|
+
} catch (e) {
|
|
1713
|
+
// This case should _never_ happen, since the editingCell shouldn't be null if this method is called
|
|
1714
|
+
var {
|
|
1715
|
+
onError
|
|
1716
|
+
} = this.props;
|
|
1717
|
+
onError(e instanceof Error ? e : new Error("".concat(e)));
|
|
1718
|
+
return null;
|
|
1719
|
+
}
|
|
1720
|
+
});
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1723
|
+
/**
|
|
1724
|
+
* Commit an edit for the currently editing cell
|
|
1725
|
+
* @param value Value that was committed
|
|
1726
|
+
* @param options Options for committing
|
|
1727
|
+
*/
|
|
1728
|
+
handleEditCellCommit(value) {
|
|
1729
|
+
var {
|
|
1730
|
+
direction = SELECTION_DIRECTION.DOWN,
|
|
1731
|
+
fillRange = false
|
|
1732
|
+
} = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
1733
|
+
var {
|
|
1734
|
+
editingCell,
|
|
1735
|
+
selectedRanges
|
|
1736
|
+
} = this.state;
|
|
1737
|
+
if (!editingCell) throw new Error('editingCell not set');
|
|
1738
|
+
var {
|
|
1739
|
+
column,
|
|
1740
|
+
row
|
|
1741
|
+
} = editingCell;
|
|
1742
|
+
if (!this.isValidForCell(column, row, value)) {
|
|
1743
|
+
// Don't allow an invalid value to be commited, the editing cell should show an error
|
|
1744
|
+
if (direction === null) {
|
|
1745
|
+
// If they clicked off of the editing cell, just remove focus
|
|
1746
|
+
this.setState({
|
|
1747
|
+
editingCell: null
|
|
1748
|
+
});
|
|
1749
|
+
}
|
|
1750
|
+
return;
|
|
1751
|
+
}
|
|
1752
|
+
if (fillRange) {
|
|
1753
|
+
this.setValueForRanges(selectedRanges, value);
|
|
1754
|
+
} else {
|
|
1755
|
+
this.setValueForCell(column, row, value);
|
|
1756
|
+
}
|
|
1757
|
+
if (direction !== null) {
|
|
1758
|
+
this.moveCursorInDirection(direction);
|
|
1759
|
+
}
|
|
1760
|
+
this.setState({
|
|
1761
|
+
editingCell: null
|
|
1762
|
+
});
|
|
1763
|
+
this.focus();
|
|
1764
|
+
}
|
|
1765
|
+
renderInputField() {
|
|
1766
|
+
var {
|
|
1767
|
+
model
|
|
1768
|
+
} = this.props;
|
|
1769
|
+
var {
|
|
1770
|
+
editingCell
|
|
1771
|
+
} = this.state;
|
|
1772
|
+
var {
|
|
1773
|
+
metrics
|
|
1774
|
+
} = this;
|
|
1775
|
+
if (editingCell == null || metrics == null || !isEditableGridModel(model)) {
|
|
1776
|
+
return null;
|
|
1777
|
+
}
|
|
1778
|
+
var {
|
|
1779
|
+
selectionRange,
|
|
1780
|
+
value,
|
|
1781
|
+
isQuickEdit
|
|
1782
|
+
} = editingCell;
|
|
1783
|
+
var {
|
|
1784
|
+
column,
|
|
1785
|
+
row
|
|
1786
|
+
} = editingCell;
|
|
1787
|
+
var {
|
|
1788
|
+
gridX,
|
|
1789
|
+
gridY,
|
|
1790
|
+
allColumnXs,
|
|
1791
|
+
allRowYs,
|
|
1792
|
+
allColumnWidths,
|
|
1793
|
+
allRowHeights
|
|
1794
|
+
} = metrics;
|
|
1795
|
+
var x = allColumnXs.get(column);
|
|
1796
|
+
var y = allRowYs.get(row);
|
|
1797
|
+
var w = allColumnWidths.get(column);
|
|
1798
|
+
var h = allRowHeights.get(row);
|
|
1799
|
+
|
|
1800
|
+
// If the cell isn't visible, we still need to display an invisible cell for focus purposes
|
|
1801
|
+
var wrapperStyle = x != null && y != null && w != null && h != null ? {
|
|
1802
|
+
position: 'absolute',
|
|
1803
|
+
left: gridX + x,
|
|
1804
|
+
top: gridY + y,
|
|
1805
|
+
width: w,
|
|
1806
|
+
height: h
|
|
1807
|
+
} : {
|
|
1808
|
+
opacity: 0
|
|
1809
|
+
};
|
|
1810
|
+
var modelColumn = this.getModelColumn(column);
|
|
1811
|
+
var modelRow = this.getModelRow(row);
|
|
1812
|
+
var inputStyle = modelColumn != null && modelRow != null ? {
|
|
1813
|
+
textAlign: model.textAlignForCell(modelColumn, modelRow)
|
|
1814
|
+
} : undefined;
|
|
1815
|
+
var isValid = model.isValidForCell(modelColumn, modelRow, value);
|
|
1816
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
1817
|
+
style: wrapperStyle
|
|
1818
|
+
}, /*#__PURE__*/React.createElement(CellInputField, {
|
|
1819
|
+
key: "".concat(column, ",").concat(row),
|
|
1820
|
+
selectionRange: selectionRange,
|
|
1821
|
+
className: classNames({
|
|
1822
|
+
error: !isValid
|
|
1823
|
+
}),
|
|
1824
|
+
onCancel: this.handleEditCellCancel,
|
|
1825
|
+
onChange: this.handleEditCellChange,
|
|
1826
|
+
onDone: this.handleEditCellCommit,
|
|
1827
|
+
isQuickEdit: isQuickEdit,
|
|
1828
|
+
style: inputStyle,
|
|
1829
|
+
value: value
|
|
1830
|
+
}));
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
/**
|
|
1834
|
+
* Gets the render state
|
|
1835
|
+
* @returns The render state
|
|
1836
|
+
*/
|
|
1837
|
+
updateRenderState() {
|
|
1838
|
+
if (!this.canvas) throw new Error('canvas is not set');
|
|
1839
|
+
if (!this.canvasContext) throw new Error('context not set');
|
|
1840
|
+
var {
|
|
1841
|
+
cursorColumn,
|
|
1842
|
+
cursorRow,
|
|
1843
|
+
draggingColumn,
|
|
1844
|
+
draggingColumnSeparator,
|
|
1845
|
+
draggingRow,
|
|
1846
|
+
draggingRowOffset,
|
|
1847
|
+
draggingRowSeparator,
|
|
1848
|
+
editingCell,
|
|
1849
|
+
isDraggingHorizontalScrollBar,
|
|
1850
|
+
isDraggingVerticalScrollBar,
|
|
1851
|
+
isDragging,
|
|
1852
|
+
mouseX,
|
|
1853
|
+
mouseY,
|
|
1854
|
+
selectedRanges
|
|
1855
|
+
} = this.state;
|
|
1856
|
+
var {
|
|
1857
|
+
model,
|
|
1858
|
+
stateOverride
|
|
1859
|
+
} = this.props;
|
|
1860
|
+
var {
|
|
1861
|
+
metrics
|
|
1862
|
+
} = this;
|
|
1863
|
+
var context = this.canvasContext;
|
|
1864
|
+
var theme = this.getTheme();
|
|
1865
|
+
var width = this.canvas.clientWidth;
|
|
1866
|
+
var height = this.canvas.clientHeight;
|
|
1867
|
+
assertNotNull(metrics);
|
|
1868
|
+
this.renderState = _objectSpread({
|
|
1869
|
+
width,
|
|
1870
|
+
height,
|
|
1871
|
+
context,
|
|
1872
|
+
theme,
|
|
1873
|
+
model,
|
|
1874
|
+
metrics,
|
|
1875
|
+
mouseX,
|
|
1876
|
+
mouseY,
|
|
1877
|
+
selectedRanges,
|
|
1878
|
+
draggingColumn,
|
|
1879
|
+
draggingColumnSeparator,
|
|
1880
|
+
draggingRow,
|
|
1881
|
+
draggingRowOffset,
|
|
1882
|
+
draggingRowSeparator,
|
|
1883
|
+
editingCell,
|
|
1884
|
+
isDraggingHorizontalScrollBar,
|
|
1885
|
+
isDraggingVerticalScrollBar,
|
|
1886
|
+
isDragging,
|
|
1887
|
+
cursorColumn,
|
|
1888
|
+
cursorRow
|
|
1889
|
+
}, stateOverride);
|
|
1890
|
+
return this.renderState;
|
|
1891
|
+
}
|
|
1892
|
+
render() {
|
|
1893
|
+
var {
|
|
1894
|
+
cursor
|
|
1895
|
+
} = this.state;
|
|
1896
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("canvas", {
|
|
1897
|
+
className: classNames('grid-canvas', Grid.getCursorClassName(cursor)),
|
|
1898
|
+
ref: canvas => {
|
|
1899
|
+
this.canvas = canvas;
|
|
1900
|
+
},
|
|
1901
|
+
onClick: this.handleClick,
|
|
1902
|
+
onContextMenu: this.handleContextMenu,
|
|
1903
|
+
onDoubleClick: this.handleDoubleClick,
|
|
1904
|
+
onKeyDown: this.handleKeyDown,
|
|
1905
|
+
onMouseDown: this.handleMouseDown,
|
|
1906
|
+
onMouseMove: this.handleMouseMove,
|
|
1907
|
+
onMouseLeave: this.handleMouseLeave,
|
|
1908
|
+
tabIndex: 0
|
|
1909
|
+
}, "Your browser does not support HTML canvas. Update your browser?"), this.renderInputField());
|
|
1910
|
+
}
|
|
1911
|
+
}
|
|
1912
|
+
_defineProperty(Grid, "contextType", ThemeContext);
|
|
1913
|
+
_defineProperty(Grid, "defaultProps", {
|
|
1914
|
+
canvasOptions: {
|
|
1915
|
+
alpha: false
|
|
1916
|
+
},
|
|
1917
|
+
isStickyBottom: false,
|
|
1918
|
+
isStickyRight: false,
|
|
1919
|
+
isStuckToBottom: false,
|
|
1920
|
+
isStuckToRight: false,
|
|
1921
|
+
keyHandlers: EMPTY_ARRAY,
|
|
1922
|
+
mouseHandlers: EMPTY_ARRAY,
|
|
1923
|
+
movedColumns: EMPTY_ARRAY,
|
|
1924
|
+
movedRows: EMPTY_ARRAY,
|
|
1925
|
+
onError: () => undefined,
|
|
1926
|
+
onSelectionChanged: () => undefined,
|
|
1927
|
+
onMovedColumnsChanged: moveOperations => undefined,
|
|
1928
|
+
onMoveColumnComplete: () => undefined,
|
|
1929
|
+
onMovedRowsChanged: () => undefined,
|
|
1930
|
+
onMoveRowComplete: () => undefined,
|
|
1931
|
+
onViewChanged: metrics => undefined,
|
|
1932
|
+
onTokenClicked: token => {
|
|
1933
|
+
if (isLinkToken(token)) {
|
|
1934
|
+
window.open(token.href, '_blank', 'noopener,noreferrer');
|
|
1935
|
+
}
|
|
1936
|
+
},
|
|
1937
|
+
stateOverride: {},
|
|
1938
|
+
theme: {
|
|
1939
|
+
autoSelectColumn: false,
|
|
1940
|
+
autoSelectRow: false
|
|
1941
|
+
}
|
|
1942
|
+
});
|
|
1943
|
+
_defineProperty(Grid, "pixelsPerLine", 100 / 3);
|
|
1944
|
+
_defineProperty(Grid, "dragTimeout", 1000);
|
|
1945
|
+
_defineProperty(Grid, "getTheme", memoize((contextTheme, userTheme) => _objectSpread(_objectSpread(_objectSpread({}, GridTheme), contextTheme), userTheme)));
|
|
1946
|
+
export default Grid;
|
|
1947
|
+
//# sourceMappingURL=Grid.js.map
|