UNPKG

react-konva-grid

Version:

Declarative React Canvas Grid primitive for Data table, Pivot table, Excel Worksheets

236 lines 9.63 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; Object.defineProperty(exports, "__esModule", { value: true }); const react_1 = __importStar(require("react")); const types_1 = require("./../types"); const helpers_1 = require("../helpers"); /** * Default cell editor * @param props */ const DefaultEditor = (props) => { const { rowIndex, columnIndex, onChange, onSubmit, onBlur, onCancel, scrollPosition, position } = props, rest = __rest(props, ["rowIndex", "columnIndex", "onChange", "onSubmit", "onBlur", "onCancel", "scrollPosition", "position"]); const inputRef = react_1.useRef(null); react_1.useEffect(() => { if (!inputRef.current) return; inputRef.current.focus(); }, []); return (react_1.default.createElement("input", Object.assign({ type: "text", ref: inputRef, style: { position: "absolute", top: position.y, left: position.x, transform: `translate3d(-${scrollPosition.scrollLeft}px, -${scrollPosition.scrollTop}px, 0)`, width: position.width, height: position.height, padding: "0 3px", margin: 0, boxSizing: "border-box", border: "1px #1a73e8 solid", boxShadow: "0 2px 6px 2px rgba(60,64,67,.15)", outline: "none", }, onChange: (e) => onChange(e.target.value), onKeyDown: (e) => { // Enter key if (e.which === types_1.KeyCodes.Enter) { onSubmit && onSubmit(e); } if (e.which === types_1.KeyCodes.Escape) { onCancel && onCancel(e); } if (e.which === types_1.KeyCodes.Tab) { e.preventDefault(); onSubmit && onSubmit(e); } }, onBlur: onBlur }, rest))); }; const getDefaultEditor = (cell) => DefaultEditor; /** * Hook to make grid editable * @param param */ const useEditable = ({ getEditor = getDefaultEditor, gridRef, getValue, onChange, onSubmit, onCancel, onDelete, selections = [], activeCell, onBeforeEdit, }) => { const [isEditorShown, setShowEditor] = react_1.useState(false); const [value, setValue] = react_1.useState(""); const [position, setPosition] = react_1.useState({ x: 0, y: 0, width: 0, height: 0, }); const currentActiveCellRef = react_1.useRef(null); const initialActiveCell = react_1.useRef(); const [scrollPosition, setScrollPosition] = react_1.useState({ scrollLeft: 0, scrollTop: 0, }); const showEditor = () => setShowEditor(true); const hideEditor = () => { setShowEditor(false); currentActiveCellRef.current = null; }; react_1.useEffect(() => { if (!currentActiveCellRef.current) return; /** * Active cell has changed, but submit has not been clicked - currentActiveCellRef */ onSubmit && onSubmit(value, currentActiveCellRef.current); }, [activeCell]); /** * Make a cell editable * @param coords * @param initialValue */ const makeEditable = (coords, initialValue) => { if (!gridRef.current) return; /* Call on before edit */ if (onBeforeEdit && !onBeforeEdit(coords)) return; currentActiveCellRef.current = coords; const pos = gridRef.current.getCellOffsetFromCoords(coords); showEditor(); setValue(initialValue || getValue(coords) || ""); setPosition(pos); }; /* Activate edit mode */ const handleDoubleClick = react_1.useCallback((e) => { const { rowIndex, columnIndex } = gridRef.current.getCellCoordsFromOffset(e.clientX, e.clientY); makeEditable({ rowIndex, columnIndex }); }, [getValue]); const isSelectionKey = (keyCode) => { return [ types_1.KeyCodes.Right, types_1.KeyCodes.Left, types_1.KeyCodes.Up, types_1.KeyCodes.Down, types_1.KeyCodes.Meta, types_1.KeyCodes.Escape, types_1.KeyCodes.Tab, ].includes(keyCode); }; const handleKeyDown = react_1.useCallback((e) => { const keyCode = e.nativeEvent.keyCode; if (isSelectionKey(keyCode) || e.nativeEvent.ctrlKey || (e.nativeEvent.shiftKey && (e.nativeEvent.key === "Shift" || e.nativeEvent.which === types_1.KeyCodes.SPACE)) || e.nativeEvent.metaKey || e.nativeEvent.which === types_1.KeyCodes.ALT) return; /* If user has not made any selection yet */ if (!activeCell) return; const { rowIndex, columnIndex } = activeCell; if (keyCode === types_1.KeyCodes.Delete || keyCode === types_1.KeyCodes.BackSpace) { // TODO: onbefore delete onDelete && onDelete(activeCell, selections); return; } const initialValue = keyCode === types_1.KeyCodes.Enter // Enter key ? undefined : e.nativeEvent.key; makeEditable({ rowIndex, columnIndex }, initialValue); }, [selections, activeCell]); /* Save the value */ const handleSubmit = react_1.useCallback((e) => { var _a, _b; if (!activeCell) return; const isTabKeyPressed = e.which === types_1.KeyCodes.Tab; const shiftKey = e.shiftKey; const nextIndex = shiftKey ? -1 : 1; let nextActiveCell = isTabKeyPressed ? { rowIndex: activeCell.rowIndex, columnIndex: activeCell.columnIndex + nextIndex, } : { rowIndex: (((_a = initialActiveCell.current) === null || _a === void 0 ? void 0 : _a.rowIndex) || activeCell.rowIndex) + 1, columnIndex: ((_b = initialActiveCell.current) === null || _b === void 0 ? void 0 : _b.columnIndex) || activeCell.columnIndex, }; /* Set previous key */ if (isTabKeyPressed && !initialActiveCell.current) { initialActiveCell.current = activeCell; } if (e.which === types_1.KeyCodes.Enter) { /* Move to the next row + cell */ initialActiveCell.current = undefined; /* If user has selected some cells and active cell is within this selection */ if (selections.length && activeCell && gridRef) { const { bounds } = selections[0]; const activeCellBounds = gridRef.current.getCellBounds(activeCell); const nextCell = helpers_1.findNextCellWithinBounds(activeCellBounds, bounds, types_1.Movement.downwards); if (nextCell) nextActiveCell = nextCell; } } /* Save the new value */ onSubmit && onSubmit(value, activeCell, nextActiveCell); /* Show editor */ hideEditor(); /* Keep the focus */ gridRef.current.focus(); }, [value, selections, activeCell]); const handleMouseDown = react_1.useCallback(() => { initialActiveCell.current = undefined; }, []); const handleChange = react_1.useCallback((value) => { if (!activeCell) return; setValue(value); onChange && onChange(value, activeCell); }, [activeCell]); /* When the input is blurred out */ const handleHide = react_1.useCallback((e) => { hideEditor(); onCancel && onCancel(); /* Keep the focus back in the grid */ gridRef.current.focus(); }, []); const handleScroll = react_1.useCallback((scrollPos) => { setScrollPosition(scrollPos); }, []); /* Editor */ const Editor = react_1.useMemo(() => getEditor(activeCell), [activeCell]); const editorComponent = isEditorShown ? (react_1.default.createElement(Editor, { value: value, onChange: handleChange, onSubmit: handleSubmit, onBlur: hideEditor, onCancel: handleHide, position: position, scrollPosition: scrollPosition })) : null; return { editorComponent, onDoubleClick: handleDoubleClick, onScroll: handleScroll, onKeyDown: handleKeyDown, onMouseDown: handleMouseDown, }; }; exports.default = useEditable; //# sourceMappingURL=useEditable.js.map