react-konva-grid
Version:
Declarative React Canvas Grid primitive for Data table, Pivot table, Excel Worksheets
236 lines • 9.63 kB
JavaScript
;
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