react-konva-grid
Advanced tools
Comparing version 2.6.3 to 2.6.4
@@ -51,2 +51,6 @@ import React, { Key } from "react"; | ||
/** | ||
* Currently active cell | ||
*/ | ||
activeCell?: CellInterface; | ||
/** | ||
* Background of selection | ||
@@ -66,3 +70,3 @@ */ | ||
*/ | ||
selections?: AreaProps[]; | ||
selections?: SelectionArea[]; | ||
/** | ||
@@ -136,2 +140,6 @@ * Array of merged cells | ||
}; | ||
export declare type OptionalScrollCoords = { | ||
scrollTop?: number; | ||
scrollLeft?: number; | ||
}; | ||
export interface ScrollState extends ScrollCoords { | ||
@@ -147,2 +155,6 @@ isScrolling: boolean; | ||
export declare type ItemSizer = (index: number) => number; | ||
export interface SelectionArea { | ||
bounds: AreaProps; | ||
inProgress?: boolean; | ||
} | ||
export interface AreaProps { | ||
@@ -203,3 +215,3 @@ top: number; | ||
getCellOffsetFromCoords: (coords: CellInterface) => CellPosition; | ||
scrollToItem: (coords: CellInterface) => void; | ||
scrollToItem: (coords: OptionalCellInterface) => void; | ||
focus: () => void; | ||
@@ -206,0 +218,0 @@ resizeColumns: (indexes: number[]) => void; |
175
dist/Grid.js
@@ -53,3 +53,3 @@ "use strict"; | ||
const defaultSelectionRenderer = (props) => { | ||
return utils_1.createBox(Object.assign(Object.assign({}, props), { strokeWidth: 2, strokeBoxWidth: 0 })); | ||
return utils_1.createBox(Object.assign({ strokeWidth: 1, strokeBoxWidth: 0 }, props)); | ||
}; | ||
@@ -62,3 +62,3 @@ const RESET_SCROLL_EVENTS_DEBOUNCE_INTERVAL = 150; | ||
const Grid = react_1.memo(react_1.forwardRef((props, forwardedRef) => { | ||
const { width: containerWidth = 800, height: containerHeight = 600, estimatedColumnWidth, estimatedRowHeight, rowHeight = defaultRowHeight, columnWidth = defaultColumnWidth, rowCount = 0, columnCount = 0, scrollbarSize = 13, onScroll, showScrollbar = true, selectionBackgroundColor = "rgb(14, 101, 235, 0.1)", selectionBorderColor = "#1a73e8", selectionStrokeWidth = 2, selections = [], frozenRows = 0, frozenColumns = 0, itemRenderer = Cell_1.CellRenderer, mergedCells = [], snap = false, scrollThrottleTimeout = 100, onViewChange, selectionRenderer = defaultSelectionRenderer, onBeforeRenderRow, showFrozenShadow = true, shadowSettings = defaultShadowSettings, borderStyles = [], children, stageProps } = props, rest = __rest(props, ["width", "height", "estimatedColumnWidth", "estimatedRowHeight", "rowHeight", "columnWidth", "rowCount", "columnCount", "scrollbarSize", "onScroll", "showScrollbar", "selectionBackgroundColor", "selectionBorderColor", "selectionStrokeWidth", "selections", "frozenRows", "frozenColumns", "itemRenderer", "mergedCells", "snap", "scrollThrottleTimeout", "onViewChange", "selectionRenderer", "onBeforeRenderRow", "showFrozenShadow", "shadowSettings", "borderStyles", "children", "stageProps"]); | ||
const { width: containerWidth = 800, height: containerHeight = 600, estimatedColumnWidth, estimatedRowHeight, rowHeight = defaultRowHeight, columnWidth = defaultColumnWidth, rowCount = 0, columnCount = 0, scrollbarSize = 13, onScroll, showScrollbar = true, selectionBackgroundColor = "rgb(14, 101, 235, 0.1)", selectionBorderColor = "#1a73e8", selectionStrokeWidth = 2, activeCell, selections = [], frozenRows = 0, frozenColumns = 0, itemRenderer = Cell_1.CellRenderer, mergedCells = [], snap = false, scrollThrottleTimeout = 100, onViewChange, selectionRenderer = defaultSelectionRenderer, onBeforeRenderRow, showFrozenShadow = true, shadowSettings = defaultShadowSettings, borderStyles = [], children, stageProps } = props, rest = __rest(props, ["width", "height", "estimatedColumnWidth", "estimatedRowHeight", "rowHeight", "columnWidth", "rowCount", "columnCount", "scrollbarSize", "onScroll", "showScrollbar", "selectionBackgroundColor", "selectionBorderColor", "selectionStrokeWidth", "activeCell", "selections", "frozenRows", "frozenColumns", "itemRenderer", "mergedCells", "snap", "scrollThrottleTimeout", "onViewChange", "selectionRenderer", "onBeforeRenderRow", "showFrozenShadow", "shadowSettings", "borderStyles", "children", "stageProps"]); | ||
tiny_invariant_1.default(!(children && typeof children !== "function"), "Children should be a function"); | ||
@@ -303,5 +303,5 @@ /* Expose some methods in ref */ | ||
if (showScrollbar) { | ||
if (horizontalScrollRef.current) | ||
if (horizontalScrollRef.current && scrollLeft !== void 0) | ||
horizontalScrollRef.current.scrollLeft = scrollLeft; | ||
if (verticalScrollRef.current) | ||
if (verticalScrollRef.current && scrollTop !== void 0) | ||
verticalScrollRef.current.scrollTop = scrollTop; | ||
@@ -322,15 +322,17 @@ } | ||
}); | ||
const newScrollLeft = helpers_1.getOffsetForColumnAndAlignment({ | ||
index: columnIndex, | ||
containerHeight, | ||
containerWidth, | ||
columnCount, | ||
columnWidth, | ||
rowCount, | ||
rowHeight, | ||
scrollOffset: scrollLeft, | ||
instanceProps: instanceProps.current, | ||
scrollbarSize, | ||
frozenOffset: frozenColumnOffset, | ||
}); | ||
const newScrollLeft = columnIndex | ||
? helpers_1.getOffsetForColumnAndAlignment({ | ||
index: columnIndex, | ||
containerHeight, | ||
containerWidth, | ||
columnCount, | ||
columnWidth, | ||
rowCount, | ||
rowHeight, | ||
scrollOffset: scrollLeft, | ||
instanceProps: instanceProps.current, | ||
scrollbarSize, | ||
frozenOffset: frozenColumnOffset, | ||
}) | ||
: void 0; | ||
const frozenRowOffset = helpers_1.getRowOffset({ | ||
@@ -342,15 +344,17 @@ index: frozenRows, | ||
}); | ||
const newScrollTop = helpers_1.getOffsetForRowAndAlignment({ | ||
index: rowIndex, | ||
containerHeight, | ||
containerWidth, | ||
columnCount, | ||
columnWidth, | ||
rowCount, | ||
rowHeight, | ||
scrollOffset: scrollTop, | ||
instanceProps: instanceProps.current, | ||
scrollbarSize, | ||
frozenOffset: frozenRowOffset, | ||
}); | ||
const newScrollTop = rowIndex | ||
? helpers_1.getOffsetForRowAndAlignment({ | ||
index: rowIndex, | ||
containerHeight, | ||
containerWidth, | ||
columnCount, | ||
columnWidth, | ||
rowCount, | ||
rowHeight, | ||
scrollOffset: scrollTop, | ||
instanceProps: instanceProps.current, | ||
scrollbarSize, | ||
frozenOffset: frozenRowOffset, | ||
}) | ||
: void 0; | ||
scrollTo({ | ||
@@ -683,2 +687,47 @@ scrollLeft: newScrollLeft, | ||
} | ||
const activeCellSelection = []; | ||
if (activeCell) { | ||
const { rowIndex, columnIndex } = activeCell; | ||
const bounds = getCellBounds(activeCell); | ||
const { top, left, right, bottom } = bounds; | ||
const actualBottom = Math.min(rowStopIndex, bottom); | ||
const actualRight = Math.min(columnStopIndex, right); | ||
const y = helpers_1.getRowOffset({ | ||
index: top, | ||
rowHeight, | ||
columnWidth, | ||
instanceProps: instanceProps.current, | ||
}); | ||
const height = helpers_1.getRowOffset({ | ||
index: actualBottom, | ||
rowHeight, | ||
columnWidth, | ||
instanceProps: instanceProps.current, | ||
}) - | ||
y + | ||
helpers_1.getRowHeight(actualBottom, instanceProps.current); | ||
const x = helpers_1.getColumnOffset({ | ||
index: left, | ||
rowHeight, | ||
columnWidth, | ||
instanceProps: instanceProps.current, | ||
}); | ||
const width = helpers_1.getColumnOffset({ | ||
index: actualRight, | ||
rowHeight, | ||
columnWidth, | ||
instanceProps: instanceProps.current, | ||
}) - | ||
x + | ||
helpers_1.getColumnWidth(actualRight, instanceProps.current); | ||
activeCellSelection.push(selectionRenderer({ | ||
stroke: selectionBorderColor, | ||
strokeWidth: 2, | ||
fill: selectionBackgroundColor, | ||
x: x, | ||
y: y, | ||
width: width, | ||
height: height, | ||
})); | ||
} | ||
/** | ||
@@ -695,3 +744,4 @@ * Convert selections to area | ||
for (let i = 0; i < selections.length; i++) { | ||
const { top, left, right, bottom } = selections[i]; | ||
const { bounds, inProgress } = selections[i]; | ||
const { top, left, right, bottom } = bounds; | ||
const selectionBounds = { x: 0, y: 0, width: 0, height: 0 }; | ||
@@ -703,2 +753,6 @@ const actualBottom = Math.min(rowStopIndex, bottom); | ||
const isIntersectionFrozen = top < frozenRows && left < frozenColumns; | ||
const styles = { | ||
stroke: inProgress ? "transparent" : selectionBorderColor, | ||
fill: selectionBackgroundColor, | ||
}; | ||
selectionBounds.y = helpers_1.getRowOffset({ | ||
@@ -741,14 +795,5 @@ index: top, | ||
})); | ||
selectionAreasFrozenColumns.push(selectionRenderer({ | ||
key: i, | ||
stroke: selectionBorderColor, | ||
fill: selectionBackgroundColor, | ||
x: selectionBounds.x, | ||
y: selectionBounds.y, | ||
width: frozenColumnSelectionWidth, | ||
height: selectionBounds.height, | ||
strokeRightWidth: frozenColumnSelectionWidth === selectionBounds.width | ||
selectionAreasFrozenColumns.push(selectionRenderer(Object.assign(Object.assign({}, styles), { key: i, x: selectionBounds.x, y: selectionBounds.y, width: frozenColumnSelectionWidth, height: selectionBounds.height, strokeRightWidth: frozenColumnSelectionWidth === selectionBounds.width | ||
? selectionStrokeWidth | ||
: 0, | ||
})); | ||
: 0 }))); | ||
} | ||
@@ -762,14 +807,5 @@ if (isTopBoundFrozen) { | ||
})); | ||
selectionAreasFrozenRows.push(selectionRenderer({ | ||
key: i, | ||
stroke: selectionBorderColor, | ||
fill: selectionBackgroundColor, | ||
x: selectionBounds.x, | ||
y: selectionBounds.y, | ||
width: selectionBounds.width, | ||
height: frozenRowSelectionHeight, | ||
strokeBottomWidth: frozenRowSelectionHeight === selectionBounds.height | ||
selectionAreasFrozenRows.push(selectionRenderer(Object.assign(Object.assign({}, styles), { key: i, x: selectionBounds.x, y: selectionBounds.y, width: selectionBounds.width, height: frozenRowSelectionHeight, strokeBottomWidth: frozenRowSelectionHeight === selectionBounds.height | ||
? selectionStrokeWidth | ||
: 0, | ||
})); | ||
: 0 }))); | ||
} | ||
@@ -789,27 +825,9 @@ if (isIntersectionFrozen) { | ||
})); | ||
selectionAreasIntersection.push(selectionRenderer({ | ||
key: i, | ||
stroke: selectionBorderColor, | ||
fill: selectionBackgroundColor, | ||
x: selectionBounds.x, | ||
y: selectionBounds.y, | ||
width: frozenIntersectionSelectionWidth, | ||
height: frozenIntersectionSelectionHeight, | ||
strokeBottomWidth: frozenIntersectionSelectionHeight === selectionBounds.height | ||
selectionAreasIntersection.push(selectionRenderer(Object.assign(Object.assign({}, styles), { key: i, x: selectionBounds.x, y: selectionBounds.y, width: frozenIntersectionSelectionWidth, height: frozenIntersectionSelectionHeight, strokeBottomWidth: frozenIntersectionSelectionHeight === selectionBounds.height | ||
? selectionStrokeWidth | ||
: 0, | ||
strokeRightWidth: frozenIntersectionSelectionWidth === selectionBounds.width | ||
: 0, strokeRightWidth: frozenIntersectionSelectionWidth === selectionBounds.width | ||
? selectionStrokeWidth | ||
: 0, | ||
})); | ||
: 0 }))); | ||
} | ||
selectionAreas.push(selectionRenderer({ | ||
key: i, | ||
stroke: selectionBorderColor, | ||
fill: selectionBackgroundColor, | ||
x: selectionBounds.x, | ||
y: selectionBounds.y, | ||
width: selectionBounds.width, | ||
height: selectionBounds.height, | ||
})); | ||
selectionAreas.push(selectionRenderer(Object.assign(Object.assign({}, styles), { key: i, x: selectionBounds.x, y: selectionBounds.y, width: selectionBounds.width, height: selectionBounds.height }))); | ||
} | ||
@@ -883,3 +901,5 @@ /** | ||
}); | ||
return { rowIndex, columnIndex }; | ||
/* To be compatible with merged cells */ | ||
const bounds = getCellBounds({ rowIndex, columnIndex }); | ||
return { rowIndex: bounds.top, columnIndex: bounds.left }; | ||
}, [scrollLeft, scrollTop, rowCount, columnCount]); | ||
@@ -936,3 +956,4 @@ const borderStylesCells = react_1.useMemo(() => { | ||
borderStylesCells, | ||
selectionAreas), | ||
selectionAreas, | ||
activeCellSelection), | ||
frozenColumnShadow, | ||
@@ -939,0 +960,0 @@ frozenRowShadow, |
import React from "react"; | ||
import { CellInterface, ScrollCoords, CellPosition, GridRef, AreaProps } from "../Grid"; | ||
import { CellInterface, ScrollCoords, CellPosition, GridRef, SelectionArea } from "../Grid"; | ||
import { KeyCodes } from "./../types"; | ||
@@ -11,4 +11,5 @@ export interface UseEditableOptions { | ||
onSubmit?: (value: string, coords: CellInterface, nextCoords?: CellInterface) => void; | ||
onDelete: (selections: AreaProps[]) => void; | ||
selections: AreaProps[]; | ||
onDelete?: (activeCell: CellInterface, selections: SelectionArea[]) => void; | ||
selections: SelectionArea[]; | ||
activeCell: CellInterface | null; | ||
onBeforeEdit?: (coords: CellInterface) => boolean; | ||
@@ -34,3 +35,3 @@ } | ||
*/ | ||
declare const useEditable: ({ getEditor, gridRef, getValue, onChange, onSubmit, onCancel, onDelete, selections, onBeforeEdit, }: UseEditableOptions) => EditableResults; | ||
declare const useEditable: ({ getEditor, gridRef, getValue, onChange, onSubmit, onCancel, onDelete, selections, activeCell, onBeforeEdit, }: UseEditableOptions) => EditableResults; | ||
export default useEditable; |
@@ -89,4 +89,4 @@ "use strict"; | ||
*/ | ||
const useEditable = ({ getEditor = getDefaultEditor, gridRef, getValue, onChange, onSubmit, onCancel, onDelete, selections = [], onBeforeEdit, }) => { | ||
const [activeCell, setActiveCell] = react_1.useState(null); | ||
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(""); | ||
@@ -99,2 +99,3 @@ const [position, setPosition] = react_1.useState({ | ||
}); | ||
const currentActiveCellRef = react_1.useRef(null); | ||
const [scrollPosition, setScrollPosition] = react_1.useState({ | ||
@@ -104,2 +105,7 @@ scrollLeft: 0, | ||
}); | ||
const showEditor = () => setShowEditor(true); | ||
const hideEditor = () => { | ||
setShowEditor(false); | ||
currentActiveCellRef.current = null; | ||
}; | ||
const makeEditable = (coords, initialValue) => { | ||
@@ -111,4 +117,5 @@ if (!gridRef.current) | ||
return; | ||
currentActiveCellRef.current = coords; | ||
const pos = gridRef.current.getCellOffsetFromCoords(coords); | ||
setActiveCell(coords); | ||
showEditor(); | ||
setValue(initialValue || getValue(coords) || ""); | ||
@@ -137,10 +144,13 @@ setPosition(pos); | ||
e.nativeEvent.ctrlKey || | ||
e.nativeEvent.shiftKey) | ||
e.nativeEvent.shiftKey || | ||
e.nativeEvent.metaKey) | ||
return; | ||
/* If user has not made any selection yet */ | ||
if (!selections.length) | ||
if (!activeCell) | ||
return; | ||
const { top: rowIndex, left: columnIndex } = selections[0]; | ||
const { rowIndex, columnIndex } = activeCell; | ||
if (keyCode === types_1.KeyCodes.Delete || keyCode === types_1.KeyCodes.BackSpace) { | ||
return onDelete(selections); | ||
// TODO: onbefore delete | ||
onDelete && onDelete(activeCell, selections); | ||
return; | ||
} | ||
@@ -151,3 +161,3 @@ const initialValue = keyCode === types_1.KeyCodes.Enter // Enter key | ||
makeEditable({ rowIndex, columnIndex }, initialValue); | ||
}, [selections]); | ||
}, [selections, activeCell]); | ||
/* Save the value */ | ||
@@ -167,3 +177,4 @@ const handleSubmit = react_1.useCallback((sourceKey) => { | ||
onSubmit && onSubmit(value, activeCell, nextActiveCell); | ||
setActiveCell(null); | ||
/* Show editor */ | ||
hideEditor(); | ||
/* Keep the focus */ | ||
@@ -180,21 +191,30 @@ gridRef.current.focus(); | ||
const handleHide = react_1.useCallback((e) => { | ||
setActiveCell(null); | ||
hideEditor(); | ||
onCancel && onCancel(); | ||
/* Keep the focus back in the grid */ | ||
gridRef.current.focus(); | ||
}, [activeCell]); | ||
}, []); | ||
const handleScroll = react_1.useCallback((scrollPos) => { | ||
setScrollPosition(scrollPos); | ||
}, []); | ||
/* Update value onBlur */ | ||
const handleBlur = react_1.useCallback(() => { | ||
if (!activeCell) | ||
const handleBlur = react_1.useCallback((e) => { | ||
if (!currentActiveCellRef.current) | ||
return; | ||
onSubmit && onSubmit(value, activeCell); | ||
setActiveCell(null); | ||
}, [value, activeCell]); | ||
/** | ||
* Event callstack | ||
* mouseDown => sets the activeCell | ||
* onBlur => reads the new activeCell, which is wrong. | ||
* Thats the reason by storing the active cell in Ref internally | ||
*/ | ||
onSubmit && onSubmit(value, currentActiveCellRef.current); | ||
hideEditor(); | ||
}, [value]); | ||
/* Editor */ | ||
const Editor = react_1.useMemo(() => getEditor(activeCell), [activeCell]); | ||
const editorComponent = activeCell ? (react_1.default.createElement(Editor, { value: value, onChange: handleChange, onSubmit: handleSubmit, onBlur: handleBlur, onEscape: handleHide, position: position, scrollPosition: scrollPosition })) : null; | ||
const editorComponent = isEditorShown ? (react_1.default.createElement(Editor, { value: value, onChange: handleChange, onSubmit: handleSubmit, onBlur: handleBlur, onEscape: handleHide, position: position, scrollPosition: scrollPosition })) : null; | ||
return { | ||
editorComponent, | ||
onDoubleClick: handleDoubleClick, | ||
onScroll: setScrollPosition, | ||
onScroll: handleScroll, | ||
onKeyDown: handleKeyDown, | ||
@@ -201,0 +221,0 @@ }; |
import React from "react"; | ||
import { AreaProps, CellInterface, GridRef } from "./../Grid"; | ||
import { SelectionArea, CellInterface, GridRef } from "./../Grid"; | ||
export interface UseSelectionOptions { | ||
gridRef?: React.MutableRefObject<GridRef>; | ||
initialSelections?: AreaProps[]; | ||
initialSelections?: SelectionArea[]; | ||
columnCount?: number; | ||
@@ -10,4 +10,6 @@ rowCount?: number; | ||
export interface SelectionResults { | ||
activeCell: CellInterface | null; | ||
newSelection: (coords: CellInterface) => void; | ||
selections: AreaProps[]; | ||
setActiveCell: (coords: CellInterface | null) => void; | ||
selections: SelectionArea[]; | ||
onMouseDown: (e: React.MouseEvent<HTMLDivElement>) => void; | ||
@@ -14,0 +16,0 @@ onMouseMove: (e: React.MouseEvent<HTMLDivElement>) => void; |
@@ -5,2 +5,6 @@ "use strict"; | ||
const types_1 = require("./../types"); | ||
const initialActiveCell = { | ||
rowIndex: 3, | ||
columnIndex: 3, | ||
}; | ||
/** | ||
@@ -12,2 +16,3 @@ * useSelection hook to enable selection in datagrid | ||
const { gridRef, initialSelections = [], columnCount = 0, rowCount = 0 } = options || {}; | ||
const [activeCell, setActiveCell] = react_1.useState(initialActiveCell); | ||
const [selections, setSelections] = react_1.useState(initialSelections); | ||
@@ -18,9 +23,10 @@ const selectionStart = react_1.useRef(); | ||
/* New selection */ | ||
const newSelection = (coords) => { | ||
selectionStart.current = coords; | ||
selectionEnd.current = coords; | ||
const selection = selectionFromStartEnd(coords, coords); | ||
if (!selection) | ||
const newSelection = (start, end = start) => { | ||
selectionStart.current = start; | ||
selectionEnd.current = end; | ||
const bounds = selectionFromStartEnd(start, end); | ||
if (!bounds) | ||
return; | ||
setSelections([selection]); | ||
setActiveCell({ rowIndex: bounds.top, columnIndex: bounds.left }); | ||
setSelections([]); | ||
}; | ||
@@ -41,10 +47,25 @@ /* selection object from start, end */ | ||
/* Modify current selection */ | ||
const modifySelection = (coords) => { | ||
const modifySelection = (coords, setInProgress) => { | ||
if (!selectionStart.current) | ||
return; | ||
selectionEnd.current = coords; | ||
const selection = selectionFromStartEnd(selectionStart.current, coords); | ||
if (!selection) | ||
const bounds = selectionFromStartEnd(selectionStart.current, coords); | ||
if (!bounds) | ||
return; | ||
setSelections([selection]); | ||
/** | ||
* 1. Multiple selections on mousedown/mousemove | ||
* 2. Move the activeCell to newly selection. Done by appendSelection | ||
*/ | ||
setSelections((prevSelection) => { | ||
const len = prevSelection.length; | ||
if (!len) { | ||
return [{ bounds, inProgress: setInProgress ? true : false }]; | ||
} | ||
return prevSelection.map((sel, i) => { | ||
if (len - 1 === i) { | ||
return Object.assign(Object.assign({}, sel), { bounds, inProgress: setInProgress ? true : false }); | ||
} | ||
return sel; | ||
}); | ||
}); | ||
}; | ||
@@ -55,6 +76,7 @@ /* Adds a new selection, CMD key */ | ||
selectionEnd.current = coords; | ||
const selection = selectionFromStartEnd(coords, coords); | ||
if (!selection) | ||
const bounds = selectionFromStartEnd(coords, coords); | ||
if (!bounds) | ||
return; | ||
setSelections((prev) => [...prev, selection]); | ||
setActiveCell({ rowIndex: bounds.top, columnIndex: bounds.left }); | ||
setSelections((prev) => [...prev, { bounds }]); | ||
}; | ||
@@ -97,4 +119,10 @@ /** | ||
const { rowIndex, columnIndex } = gridRef.current.getCellCoordsFromOffset(e.clientX, e.clientY); | ||
modifySelection({ rowIndex, columnIndex }); | ||
}, [isSelectionMode]); | ||
/** | ||
* If the user is moving across the Active Cell, lets not add it to selection | ||
*/ | ||
if ((activeCell === null || activeCell === void 0 ? void 0 : activeCell.rowIndex) === rowIndex && | ||
(activeCell === null || activeCell === void 0 ? void 0 : activeCell.columnIndex) === columnIndex) | ||
return; | ||
modifySelection({ rowIndex, columnIndex }, true); | ||
}, [activeCell]); | ||
/** | ||
@@ -106,2 +134,12 @@ * Mouse up handler | ||
isSelectionMode.current = false; | ||
/* Update last selection */ | ||
setSelections((prevSelection) => { | ||
const len = prevSelection.length; | ||
return prevSelection.map((sel, i) => { | ||
if (len - 1 === i) { | ||
return Object.assign(Object.assign({}, sel), { inProgress: false }); | ||
} | ||
return sel; | ||
}); | ||
}); | ||
}, []); | ||
@@ -113,6 +151,11 @@ /** | ||
*/ | ||
const keyNavigate = (direction, modify) => { | ||
if (!selectionEnd.current || !gridRef) | ||
const keyNavigate = react_1.useCallback((direction, modify) => { | ||
if (!selectionStart.current || | ||
!selectionEnd.current || | ||
!gridRef || | ||
!activeCell) | ||
return; | ||
var { rowIndex, columnIndex } = selectionEnd.current; | ||
var { rowIndex, columnIndex } = modify | ||
? selectionEnd.current | ||
: activeCell; | ||
const isMergedCell = gridRef === null || gridRef === void 0 ? void 0 : gridRef.current.isMergedCell({ | ||
@@ -145,2 +188,7 @@ rowIndex, | ||
} | ||
const scrollToCell = modify | ||
? selectionEnd.current.rowIndex === rowIndex | ||
? { columnIndex } | ||
: { rowIndex } | ||
: { rowIndex, columnIndex }; | ||
if (modify) { | ||
@@ -153,22 +201,71 @@ modifySelection({ rowIndex, columnIndex }); | ||
/* Keep the item in view */ | ||
gridRef.current.scrollToItem({ rowIndex, columnIndex }); | ||
gridRef.current.scrollToItem(scrollToCell); | ||
}, [activeCell]); | ||
// ⌘A or ⌘+Shift+Space | ||
const selectAll = () => { | ||
selectionStart.current = { rowIndex: 0, columnIndex: 0 }; | ||
modifySelection({ rowIndex: rowCount - 1, columnIndex: columnCount - 1 }); | ||
}; | ||
// Shift+Space | ||
const selectColumn = () => { | ||
if (!selectionEnd.current || !selectionStart.current) | ||
return; | ||
selectionStart.current = { | ||
rowIndex: 0, | ||
columnIndex: selectionStart.current.columnIndex, | ||
}; | ||
modifySelection({ | ||
rowIndex: rowCount - 1, | ||
columnIndex: selectionEnd.current.columnIndex, | ||
}); | ||
}; | ||
// Shift+Space | ||
const selectRow = () => { | ||
if (!selectionEnd.current || !selectionStart.current) | ||
return; | ||
selectionStart.current = { | ||
rowIndex: selectionStart.current.rowIndex, | ||
columnIndex: 0, | ||
}; | ||
modifySelection({ | ||
rowIndex: selectionEnd.current.rowIndex, | ||
columnIndex: columnCount - 1, | ||
}); | ||
}; | ||
const handleKeyDown = react_1.useCallback((e) => { | ||
const modify = e.nativeEvent.shiftKey; | ||
const isShiftKey = e.nativeEvent.shiftKey; | ||
const isMetaKey = e.nativeEvent.ctrlKey || e.nativeEvent.metaKey; | ||
switch (e.nativeEvent.which) { | ||
case types_1.KeyCodes.Right: | ||
keyNavigate(types_1.Direction.Right, modify); | ||
keyNavigate(types_1.Direction.Right, isShiftKey); | ||
break; | ||
case types_1.KeyCodes.Left: | ||
keyNavigate(types_1.Direction.Left, modify); | ||
keyNavigate(types_1.Direction.Left, isShiftKey); | ||
break; | ||
// Up | ||
case types_1.KeyCodes.Up: | ||
keyNavigate(types_1.Direction.Up, modify); | ||
keyNavigate(types_1.Direction.Up, isShiftKey); | ||
break; | ||
case types_1.KeyCodes.Down: | ||
keyNavigate(types_1.Direction.Down, modify); | ||
keyNavigate(types_1.Direction.Down, isShiftKey); | ||
break; | ||
case types_1.KeyCodes.A: | ||
// Select All | ||
if (isMetaKey) { | ||
selectAll(); | ||
} | ||
break; | ||
case types_1.KeyCodes.SPACE: | ||
if (isMetaKey && isShiftKey) { | ||
selectAll(); | ||
} | ||
else if (isMetaKey) { | ||
selectColumn(); | ||
} | ||
else if (isShiftKey) { | ||
selectRow(); | ||
} | ||
break; | ||
case types_1.KeyCodes.Tab: | ||
if (modify) { | ||
if (isShiftKey) { | ||
keyNavigate(types_1.Direction.Left); | ||
@@ -182,4 +279,5 @@ } | ||
} | ||
}, [rowCount, columnCount]); | ||
}, [rowCount, columnCount, activeCell]); | ||
return { | ||
activeCell, | ||
selections, | ||
@@ -191,2 +289,3 @@ onMouseDown: handleMouseDown, | ||
newSelection, | ||
setActiveCell, | ||
}; | ||
@@ -193,0 +292,0 @@ }; |
@@ -11,3 +11,5 @@ export declare enum KeyCodes { | ||
BackSpace = 8, | ||
Enter = 13 | ||
Enter = 13, | ||
A = 65, | ||
SPACE = 32 | ||
} | ||
@@ -14,0 +16,0 @@ export declare enum Direction { |
@@ -16,2 +16,4 @@ "use strict"; | ||
KeyCodes[KeyCodes["Enter"] = 13] = "Enter"; | ||
KeyCodes[KeyCodes["A"] = 65] = "A"; | ||
KeyCodes[KeyCodes["SPACE"] = 32] = "SPACE"; | ||
})(KeyCodes = exports.KeyCodes || (exports.KeyCodes = {})); | ||
@@ -18,0 +20,0 @@ var Direction; |
{ | ||
"name": "react-konva-grid", | ||
"description": "Declarative React Canvas Grid primitive for Data table, Pivot table, Excel Worksheets", | ||
"version": "2.6.3", | ||
"version": "2.6.4", | ||
"main": "dist/index.js", | ||
@@ -6,0 +6,0 @@ "license": "MIT", |
import React, { useState, useCallback, useRef } from "react"; | ||
import { AreaProps, CellInterface, GridRef } from "./../Grid"; | ||
import { SelectionArea, AreaProps, CellInterface, GridRef } from "./../Grid"; | ||
import { KeyCodes, Direction } from "./../types"; | ||
@@ -7,3 +7,3 @@ | ||
gridRef?: React.MutableRefObject<GridRef>; | ||
initialSelections?: AreaProps[]; | ||
initialSelections?: SelectionArea[]; | ||
columnCount?: number; | ||
@@ -14,4 +14,6 @@ rowCount?: number; | ||
export interface SelectionResults { | ||
activeCell: CellInterface | null; | ||
newSelection: (coords: CellInterface) => void; | ||
selections: AreaProps[]; | ||
setActiveCell: (coords: CellInterface | null) => void; | ||
selections: SelectionArea[]; | ||
onMouseDown: (e: React.MouseEvent<HTMLDivElement>) => void; | ||
@@ -23,2 +25,7 @@ onMouseMove: (e: React.MouseEvent<HTMLDivElement>) => void; | ||
const initialActiveCell = { | ||
rowIndex: 3, | ||
columnIndex: 3, | ||
}; | ||
/** | ||
@@ -31,3 +38,8 @@ * useSelection hook to enable selection in datagrid | ||
options || {}; | ||
const [selections, setSelections] = useState<AreaProps[]>(initialSelections); | ||
const [activeCell, setActiveCell] = useState<CellInterface | null>( | ||
initialActiveCell | ||
); | ||
const [selections, setSelections] = useState<SelectionArea[]>( | ||
initialSelections | ||
); | ||
const selectionStart = useRef<CellInterface>(); | ||
@@ -38,8 +50,9 @@ const selectionEnd = useRef<CellInterface>(); | ||
/* New selection */ | ||
const newSelection = (coords: CellInterface) => { | ||
selectionStart.current = coords; | ||
selectionEnd.current = coords; | ||
const selection = selectionFromStartEnd(coords, coords); | ||
if (!selection) return; | ||
setSelections([selection]); | ||
const newSelection = (start: CellInterface, end: CellInterface = start) => { | ||
selectionStart.current = start; | ||
selectionEnd.current = end; | ||
const bounds = selectionFromStartEnd(start, end); | ||
if (!bounds) return; | ||
setActiveCell({ rowIndex: bounds.top, columnIndex: bounds.left }); | ||
setSelections([]); | ||
}; | ||
@@ -61,8 +74,28 @@ | ||
/* Modify current selection */ | ||
const modifySelection = (coords: CellInterface) => { | ||
const modifySelection = (coords: CellInterface, setInProgress?: boolean) => { | ||
if (!selectionStart.current) return; | ||
selectionEnd.current = coords; | ||
const selection = selectionFromStartEnd(selectionStart.current, coords); | ||
if (!selection) return; | ||
setSelections([selection]); | ||
const bounds = selectionFromStartEnd(selectionStart.current, coords); | ||
if (!bounds) return; | ||
/** | ||
* 1. Multiple selections on mousedown/mousemove | ||
* 2. Move the activeCell to newly selection. Done by appendSelection | ||
*/ | ||
setSelections((prevSelection) => { | ||
const len = prevSelection.length; | ||
if (!len) { | ||
return [{ bounds, inProgress: setInProgress ? true : false }]; | ||
} | ||
return prevSelection.map((sel, i) => { | ||
if (len - 1 === i) { | ||
return { | ||
...sel, | ||
bounds, | ||
inProgress: setInProgress ? true : false, | ||
}; | ||
} | ||
return sel; | ||
}); | ||
}); | ||
}; | ||
@@ -74,5 +107,6 @@ | ||
selectionEnd.current = coords; | ||
const selection = selectionFromStartEnd(coords, coords); | ||
if (!selection) return; | ||
setSelections((prev) => [...prev, selection]); | ||
const bounds = selectionFromStartEnd(coords, coords); | ||
if (!bounds) return; | ||
setActiveCell({ rowIndex: bounds.top, columnIndex: bounds.left }); | ||
setSelections((prev) => [...prev, { bounds }]); | ||
}; | ||
@@ -130,5 +164,14 @@ | ||
modifySelection({ rowIndex, columnIndex }); | ||
/** | ||
* If the user is moving across the Active Cell, lets not add it to selection | ||
*/ | ||
if ( | ||
activeCell?.rowIndex === rowIndex && | ||
activeCell?.columnIndex === columnIndex | ||
) | ||
return; | ||
modifySelection({ rowIndex, columnIndex }, true); | ||
}, | ||
[isSelectionMode] | ||
[activeCell] | ||
); | ||
@@ -141,2 +184,16 @@ /** | ||
isSelectionMode.current = false; | ||
/* Update last selection */ | ||
setSelections((prevSelection) => { | ||
const len = prevSelection.length; | ||
return prevSelection.map((sel, i) => { | ||
if (len - 1 === i) { | ||
return { | ||
...sel, | ||
inProgress: false, | ||
}; | ||
} | ||
return sel; | ||
}); | ||
}); | ||
}, []); | ||
@@ -149,54 +206,104 @@ | ||
*/ | ||
const keyNavigate = (direction: Direction, modify?: boolean) => { | ||
if (!selectionEnd.current || !gridRef) return; | ||
var { rowIndex, columnIndex } = selectionEnd.current; | ||
const isMergedCell = gridRef?.current.isMergedCell({ | ||
rowIndex, | ||
columnIndex, | ||
}); | ||
const keyNavigate = useCallback( | ||
(direction: Direction, modify?: boolean) => { | ||
if ( | ||
!selectionStart.current || | ||
!selectionEnd.current || | ||
!gridRef || | ||
!activeCell | ||
) | ||
return; | ||
var { rowIndex, columnIndex } = modify | ||
? selectionEnd.current | ||
: activeCell; | ||
const isMergedCell = gridRef?.current.isMergedCell({ | ||
rowIndex, | ||
columnIndex, | ||
}); | ||
const bounds = gridRef.current.getCellBounds({ rowIndex, columnIndex }); | ||
const bounds = gridRef.current.getCellBounds({ rowIndex, columnIndex }); | ||
switch (direction) { | ||
case Direction.Up: | ||
if (isMergedCell) rowIndex = bounds.top; | ||
rowIndex = Math.max(rowIndex - 1, 0); | ||
break; | ||
switch (direction) { | ||
case Direction.Up: | ||
if (isMergedCell) rowIndex = bounds.top; | ||
rowIndex = Math.max(rowIndex - 1, 0); | ||
break; | ||
case Direction.Down: | ||
if (isMergedCell) rowIndex = bounds.bottom; | ||
rowIndex = Math.min(rowIndex + 1, rowCount - 1); | ||
break; | ||
case Direction.Down: | ||
if (isMergedCell) rowIndex = bounds.bottom; | ||
rowIndex = Math.min(rowIndex + 1, rowCount - 1); | ||
break; | ||
case Direction.Left: | ||
if (isMergedCell) columnIndex = bounds.left; | ||
columnIndex = Math.max(columnIndex - 1, 0); | ||
break; | ||
case Direction.Left: | ||
if (isMergedCell) columnIndex = bounds.left; | ||
columnIndex = Math.max(columnIndex - 1, 0); | ||
break; | ||
case Direction.Right: | ||
if (isMergedCell) columnIndex = bounds.right; | ||
columnIndex = Math.min(columnIndex + 1, columnCount - 1); | ||
break; | ||
} | ||
case Direction.Right: | ||
if (isMergedCell) columnIndex = bounds.right; | ||
columnIndex = Math.min(columnIndex + 1, columnCount - 1); | ||
break; | ||
} | ||
if (modify) { | ||
modifySelection({ rowIndex, columnIndex }); | ||
} else { | ||
newSelection({ rowIndex, columnIndex }); | ||
} | ||
const scrollToCell = modify | ||
? selectionEnd.current.rowIndex === rowIndex | ||
? { columnIndex } | ||
: { rowIndex } | ||
: { rowIndex, columnIndex }; | ||
/* Keep the item in view */ | ||
gridRef.current.scrollToItem({ rowIndex, columnIndex }); | ||
if (modify) { | ||
modifySelection({ rowIndex, columnIndex }); | ||
} else { | ||
newSelection({ rowIndex, columnIndex }); | ||
} | ||
/* Keep the item in view */ | ||
gridRef.current.scrollToItem(scrollToCell); | ||
}, | ||
[activeCell] | ||
); | ||
// ⌘A or ⌘+Shift+Space | ||
const selectAll = () => { | ||
selectionStart.current = { rowIndex: 0, columnIndex: 0 }; | ||
modifySelection({ rowIndex: rowCount - 1, columnIndex: columnCount - 1 }); | ||
}; | ||
// Shift+Space | ||
const selectColumn = () => { | ||
if (!selectionEnd.current || !selectionStart.current) return; | ||
selectionStart.current = { | ||
rowIndex: 0, | ||
columnIndex: selectionStart.current.columnIndex, | ||
}; | ||
modifySelection({ | ||
rowIndex: rowCount - 1, | ||
columnIndex: selectionEnd.current.columnIndex, | ||
}); | ||
}; | ||
// Shift+Space | ||
const selectRow = () => { | ||
if (!selectionEnd.current || !selectionStart.current) return; | ||
selectionStart.current = { | ||
rowIndex: selectionStart.current.rowIndex, | ||
columnIndex: 0, | ||
}; | ||
modifySelection({ | ||
rowIndex: selectionEnd.current.rowIndex, | ||
columnIndex: columnCount - 1, | ||
}); | ||
}; | ||
const handleKeyDown = useCallback( | ||
(e: React.KeyboardEvent) => { | ||
const modify = e.nativeEvent.shiftKey; | ||
const isShiftKey = e.nativeEvent.shiftKey; | ||
const isMetaKey = e.nativeEvent.ctrlKey || e.nativeEvent.metaKey; | ||
switch (e.nativeEvent.which) { | ||
case KeyCodes.Right: | ||
keyNavigate(Direction.Right, modify); | ||
keyNavigate(Direction.Right, isShiftKey); | ||
break; | ||
case KeyCodes.Left: | ||
keyNavigate(Direction.Left, modify); | ||
keyNavigate(Direction.Left, isShiftKey); | ||
break; | ||
@@ -206,11 +313,28 @@ | ||
case KeyCodes.Up: | ||
keyNavigate(Direction.Up, modify); | ||
keyNavigate(Direction.Up, isShiftKey); | ||
break; | ||
case KeyCodes.Down: | ||
keyNavigate(Direction.Down, modify); | ||
keyNavigate(Direction.Down, isShiftKey); | ||
break; | ||
case KeyCodes.A: | ||
// Select All | ||
if (isMetaKey) { | ||
selectAll(); | ||
} | ||
break; | ||
case KeyCodes.SPACE: | ||
if (isMetaKey && isShiftKey) { | ||
selectAll(); | ||
} else if (isMetaKey) { | ||
selectColumn(); | ||
} else if (isShiftKey) { | ||
selectRow(); | ||
} | ||
break; | ||
case KeyCodes.Tab: | ||
if (modify) { | ||
if (isShiftKey) { | ||
keyNavigate(Direction.Left); | ||
@@ -224,6 +348,7 @@ } else { | ||
}, | ||
[rowCount, columnCount] | ||
[rowCount, columnCount, activeCell] | ||
); | ||
return { | ||
activeCell, | ||
selections, | ||
@@ -235,2 +360,3 @@ onMouseDown: handleMouseDown, | ||
newSelection, | ||
setActiveCell, | ||
}; | ||
@@ -237,0 +363,0 @@ }; |
@@ -12,2 +12,4 @@ export enum KeyCodes { | ||
Enter = 13, | ||
A = 65, | ||
SPACE = 32, | ||
} | ||
@@ -14,0 +16,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
14782607
21155