UNPKG

react-spreadsheet

Version:

Simple, customizable yet performant spreadsheet for React

366 lines (335 loc) 10.6 kB
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 }; }