react-spreadsheet
Version:
Simple, customizable yet performant spreadsheet for React
366 lines (335 loc) • 10.6 kB
JavaScript
import _regeneratorRuntime from "@babel/runtime/regenerator";
import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
import _asyncToGenerator from "@babel/runtime/helpers/esm/asyncToGenerator";
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
import * as PointSet from "./point-set";
import * as PointMap from "./point-map";
import * as Matrix from "./matrix";
import * as Types from "./types";
import { isActive, setCell, updateData } from "./util";
export var setData = function setData(state, data) {
var nextActive = state.active && Matrix.has(state.active.row, state.active.column, data) ? state.active : null;
var nextSelected = PointSet.filter(function (point) {
return Matrix.has(point.row, point.column, data);
}, state.selected);
var nextBindings = PointMap.map(function (bindings) {
return PointSet.filter(function (point) {
return Matrix.has(point.row, point.column, data);
}, bindings);
}, PointMap.filter(function (_, point) {
return Matrix.has(point.row, point.column, data);
}, state.bindings));
return {
data: data,
active: nextActive,
selected: nextSelected,
bindings: nextBindings
};
};
export var select = function select(state, cellPointer) {
if (state.active && !isActive(state.active, cellPointer)) {
return {
selected: PointSet.from(Matrix.inclusiveRange({
row: cellPointer.row,
column: cellPointer.column
}, {
row: state.active.row,
column: state.active.column
})),
mode: "view"
};
}
return null;
};
export var activate = function activate(state, cellPointer) {
return {
selected: PointSet.from([cellPointer]),
active: cellPointer,
mode: isActive(state.active, cellPointer) ? "edit" : "view"
};
};
export function setCellData(state, active, data, bindings) {
if (isActiveReadOnly(state)) {
return null;
}
return {
mode: "edit",
data: setCell(state, active, data),
lastChanged: active,
bindings: PointMap.set(active, PointSet.from(bindings), state.bindings)
};
}
export function setCellDimensions(state, point, dimensions) {
var prevRowDimensions = state.rowDimensions[point.row];
var prevColumnDimensions = state.columnDimensions[point.column];
if (prevRowDimensions && prevColumnDimensions && prevRowDimensions.top === dimensions.top && prevRowDimensions.height === dimensions.height && prevColumnDimensions.left === dimensions.left && prevColumnDimensions.width === dimensions.width) {
return null;
}
return {
rowDimensions: _objectSpread({}, state.rowDimensions, _defineProperty({}, point.row, {
top: dimensions.top,
height: dimensions.height
})),
columnDimensions: _objectSpread({}, state.columnDimensions, _defineProperty({}, point.column, {
left: dimensions.left,
width: dimensions.width
}))
};
}
export function copy(state) {
return {
copied: PointSet.reduce(function (acc, point) {
return PointMap.set(point, Matrix.get(point.row, point.column, state.data), acc);
}, state.selected, PointMap.from([])),
cut: false,
hasPasted: false
};
}
export var cut = function cut(state) {
return _objectSpread({}, copy(state), {
cut: true
});
};
export function paste(_x, _x2) {
return _paste.apply(this, arguments);
}
function _paste() {
_paste = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(state, text) {
var copiedMatrix, copied, minPoint, requiredRows, paddedData, _PointMap$reduce, data, selected, commit;
return _regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
if (text) {
_context.next = 2;
break;
}
return _context.abrupt("return", null);
case 2:
copiedMatrix = Matrix.split(text, function (value) {
return {
value: value
};
});
copied = PointMap.fromMatrix(copiedMatrix);
minPoint = PointSet.min(copied);
requiredRows = state.active.row + Matrix.getSize(copiedMatrix).rows;
paddedData = Matrix.padMatrix(state.data, requiredRows);
_PointMap$reduce = PointMap.reduce(function (acc, value, _ref) {
var row = _ref.row,
column = _ref.column;
if (!state.active) {
return acc;
}
var commit = acc.commit || [];
var nextRow = row - minPoint.row + state.active.row;
var nextColumn = column - minPoint.column + state.active.column;
var nextData = state.cut ? Matrix.unset(row, column, acc.data) : acc.data;
if (state.cut) {
commit = [].concat(_toConsumableArray(commit), [{
prevCell: value,
nextCell: undefined
}]);
}
if (!Matrix.has(nextRow, nextColumn, paddedData)) {
return {
data: nextData,
selected: acc.selected,
commit: commit
};
}
var currentValue = Matrix.get(nextRow, nextColumn, nextData) || {};
commit = [].concat(_toConsumableArray(commit), [{
prevCell: currentValue,
nextCell: value
}]);
return {
data: Matrix.set(nextRow, nextColumn, _objectSpread({}, currentValue, {}, value), nextData),
selected: PointSet.add(acc.selected, {
row: nextRow,
column: nextColumn
}),
commit: commit
};
}, copied, {
data: paddedData,
selected: PointSet.from([]),
commit: []
}), data = _PointMap$reduce.data, selected = _PointMap$reduce.selected, commit = _PointMap$reduce.commit;
return _context.abrupt("return", {
data: data,
selected: selected,
cut: false,
hasPasted: true,
mode: "view",
lastCommit: commit
});
case 9:
case "end":
return _context.stop();
}
}
}, _callee);
}));
return _paste.apply(this, arguments);
}
export var edit = function edit(state) {
if (isActiveReadOnly(state)) {
return null;
}
return {
mode: "edit"
};
};
export var view = function view() {
return {
mode: "view"
};
};
export var clear = function clear(state) {
if (!state.active) {
return null;
}
var _state$active = state.active,
row = _state$active.row,
column = _state$active.column;
var cell = Matrix.get(row, column, state.data);
return _objectSpread({
data: PointSet.reduce(function (acc, point) {
return updateData(acc, _objectSpread({}, point, {
data: _objectSpread({}, cell, {
value: ""
})
}));
}, state.selected, state.data)
}, commit(state, PointSet.toArray(state.selected).map(function (point) {
var cell = Matrix.get(point.row, point.column, state.data);
return {
prevCell: cell,
nextCell: _objectSpread({}, cell, {
value: ""
})
};
})));
};
export var go = function go(rowDelta, columnDelta) {
return function (state, event) {
if (!state.active) {
return null;
}
var nextActive = {
row: state.active.row + rowDelta,
column: state.active.column + columnDelta
};
if (!Matrix.has(nextActive.row, nextActive.column, state.data)) {
return {
mode: "view"
};
}
return {
active: nextActive,
selected: PointSet.from([nextActive]),
mode: "view"
};
};
};
export var modifyEdge = function modifyEdge(field, delta) {
return function (state, event) {
if (!state.active) {
return null;
}
var edgeOffsets = PointSet.has(state.selected, _objectSpread({}, state.active, _defineProperty({}, field, state.active[field] + delta * -1)));
var nextSelected = edgeOffsets ? PointSet.shrinkEdge(state.selected, field, delta * -1) : PointSet.extendEdge(state.selected, field, delta);
return {
selected: PointSet.filter(function (point) {
return Matrix.has(point.row, point.column, state.data);
}, nextSelected)
};
};
};
export var blur = function blur() {
return {
active: null
};
}; // Key Bindings
/** @todo handle inactive state? */
var keyDownHandlers = {
ArrowUp: go(-1, 0),
ArrowDown: go(+1, 0),
ArrowLeft: go(0, -1),
ArrowRight: go(0, +1),
Tab: go(0, +1),
Enter: edit,
Backspace: clear,
Escape: blur
};
var editKeyDownHandlers = {
Escape: view,
Tab: keyDownHandlers.Tab,
Enter: keyDownHandlers.ArrowDown
};
var shiftKeyDownHandlers = {
ArrowUp: modifyEdge("row", -1),
ArrowDown: modifyEdge("row", 1),
ArrowLeft: modifyEdge("column", -1),
ArrowRight: modifyEdge("column", 1)
};
var shiftMetaKeyDownHandlers = {};
var metaKeyDownHandlers = {};
function getActive(state) {
return state.active && Matrix.get(state.active.row, state.active.column, state.data);
}
var isActiveReadOnly = function isActiveReadOnly(state) {
var activeCell = getActive(state);
return Boolean(activeCell && activeCell.readOnly);
};
export function keyPress(state, event) {
if (isActiveReadOnly(state) || event.metaKey) {
return null;
}
if (state.mode === "view" && state.active) {
return {
mode: "edit"
};
}
return null;
}
export function getKeyDownHandler(state, event) {
var key = event.key;
var handlers; // Order matters
if (state.mode === "edit") {
handlers = editKeyDownHandlers;
} else if (event.shiftKey && event.metaKey) {
handlers = shiftMetaKeyDownHandlers;
} else if (event.shiftKey) {
handlers = shiftKeyDownHandlers;
} else if (event.metaKey) {
handlers = metaKeyDownHandlers;
} else {
handlers = keyDownHandlers;
}
return handlers[key];
}
export function keyDown(state, event) {
var handler = getKeyDownHandler(state, event);
if (handler) {
return handler(state, event);
}
return null;
}
export function dragStart(state) {
return {
dragging: true
};
}
export function dragEnd(state) {
return {
dragging: false
};
}
export function commit(state, changes) {
return {
lastCommit: changes
};
}