diff --git a/lib/index.d.ts b/lib/index.d.ts index 836660163e..a43dbf859b 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -303,6 +303,8 @@ interface Renderers { interface SelectCellOptions { enableEditor?: Maybe; shouldFocusCell?: Maybe; + /** Extend the selected range from the selected cell instead of moving the selection */ + extendRange?: Maybe; } interface ColumnWidth { readonly type: 'resized' | 'measured'; diff --git a/lib/index.js b/lib/index.js index b4adecfb54..2a636dfe04 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1035,7 +1035,11 @@ function Cell({ column, colSpan, isCellSelected, isDraggedOver, row: row$1, rowI } function handleMouseDown(event) { onMouseDown?.(event); - if (!handleMouseEvent(event, onCellMouseDown)) selectCellWrapper(); + if (!handleMouseEvent(event, onCellMouseDown)) if (rangeSelectionMode && event.shiftKey) selectCell({ + rowIdx, + idx: column.idx + }, { extendRange: true }); + else selectCellWrapper(); } function handleClick(event) { onClick?.(event); @@ -2200,6 +2204,20 @@ function DataGrid(props) { function selectCell(position, options) { if (!isCellWithinSelectionBounds(position)) return; commitEditorChanges(); + if (options?.extendRange && enableRangeSelection && selectedPosition.idx >= 0 && isRowIdxWithinViewportBounds(selectedPosition.rowIdx) && isRowIdxWithinViewportBounds(position.rowIdx)) { + if (selectedPosition.mode === "EDIT") setSelectedPosition({ + idx: selectedPosition.idx, + rowIdx: selectedPosition.rowIdx, + mode: "SELECT" + }); + setSelectedRangeWithBoundary({ + startColumnIdx: selectedPosition.idx, + startRowIdx: selectedPosition.rowIdx, + endColumnIdx: position.idx, + endRowIdx: position.rowIdx + }); + return; + } const samePosition = isSamePosition(selectedPosition, position); if (options?.enableEditor && isCellEditable(position)) { const row$1 = rows[position.rowIdx]; @@ -2209,8 +2227,15 @@ function DataGrid(props) { row: row$1, originalRow: row$1 }); - } else if (samePosition) scrollIntoView(getCellToScroll(gridRef.current)); - else { + } else if (samePosition) { + scrollIntoView(getCellToScroll(gridRef.current)); + setSelectedRange((range) => range.startColumnIdx === position.idx && range.endColumnIdx === position.idx && range.startRowIdx === position.rowIdx && range.endRowIdx === position.rowIdx ? range : { + startColumnIdx: position.idx, + startRowIdx: position.rowIdx, + endColumnIdx: position.idx, + endRowIdx: position.rowIdx + }); + } else { setShouldFocusCell(options?.shouldFocusCell === true); setSelectedPosition({ ...position, diff --git a/lib/index.js.map b/lib/index.js.map index 2fcc4aa026..b090cacbae 100644 --- a/lib/index.js.map +++ b/lib/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","names":["CalculatedColumn","measuringCellClassname","renderMeasuringCells","viewportColumns","R","SR","map","key","idx","minWidth","maxWidth","gridColumnStart","row","moveNext","cell","cellClassname","cellFrozen","cellFrozenClassname","cellDragHandle","cellDragHandleFrozenClassname","cellDragHandleClassname","RenderCheckboxProps","checkbox","checkboxClassname","renderCheckbox","onChange","indeterminate","props","handleChange","e","React","ChangeEvent","HTMLInputElement","target","checked","nativeEvent","MouseEvent","shiftKey","el","RenderGroupCellProps","groupCellContent","groupCellContentClassname","caret","caretClassname","renderToggleGroup","props","R","SR","ToggleGroup","groupKey","isExpanded","tabIndex","toggleGroup","handleKeyDown","key","React","KeyboardEvent","HTMLSpanElement","d","renderCheckbox","SelectColumn: Column","RenderHeaderCellProps","useDefaultRenderers","headerSortCellClassname","headerSortName","headerSortNameClassname","renderHeaderCell","column","sortDirection","priority","R","SR","sortable","name","SharedHeaderCellProps","Pick","SortableHeaderCellProps","children","React","ReactNode","SortableHeaderCell","renderSortStatus","defaultRenderCell","lastFrozenColumnIndex","headerRowsCount","columns: MutableCalculatedColumn[]","rawColumns","calculatedColumnParent: MutableCalculatedColumnParent","column: MutableCalculatedColumn","colSpanColumns: CalculatedColumn[]","columnMetrics","totalFrozenColumnWidth","templateColumns: string[]","layoutCssVars: Record","colOverscanStartIdx","colOverscanEndIdx","columnsCanFlex: boolean","columnsToMeasure: string[]","offsetHeight","clientHeight","startIdx","row","viewportColumns: CalculatedColumn[]","totalRowHeight","gridTemplateRows","currentHeight: number | null","row","memo","MouseEvent","useRovingTabIndex","createCellEvent","getCellClassname","getCellStyle","isCellEditableUtil","CellMouseEventHandler","CellRendererProps","cellDraggedOver","cellDraggedOverClassname","Cell","column","colSpan","isCellSelected","isDraggedOver","row","rowIdx","className","onMouseDown","onCellMouseDown","onClick","onCellClick","onDoubleClick","onCellDoubleClick","onContextMenu","onCellContextMenu","onRowChange","selectCell","style","rangeSelectionMode","onMouseDownCapture","onMouseUpCapture","onMouseEnter","props","R","SR","tabIndex","childTabIndex","onFocus","cellClass","isEditable","selectCellWrapper","enableEditor","idx","handleMouseEvent","event","React","HTMLDivElement","eventHandler","eventHandled","cellEvent","isGridDefaultPrevented","handleMouseDown","handleClick","handleDoubleClick","handleContextMenu","handleRowChange","newRow","undefined","e","renderCell","isCellEditable","CellComponent","JSX","Element","defaultRenderCell","key","Key","useEffectEvent","useLayoutEffect","useRef","createCellEvent","getCellClassname","getCellStyle","onEditorNavigation","CellKeyboardEvent","CellRendererProps","EditCellKeyDownArgs","Maybe","Omit","RenderEditCellProps","global","scheduler","Scheduler","postTask","callback","options","priority","signal","AbortSignal","delay","Promise","canUsePostTask","cellEditing","SharedCellRendererProps","Pick","R","SR","EditCellProps","rowIdx","onRowChange","row","commitChanges","shouldFocusCell","closeEditor","navigate","event","React","KeyboardEvent","HTMLDivElement","onKeyDown","args","EditCell","column","colSpan","captureEventRef","MouseEvent","undefined","abortControllerRef","AbortController","frameRequestRef","commitOnOutsideClick","editorOptions","commitOnOutsideMouseDown","onClose","onWindowCaptureMouseDown","current","abortController","catch","requestAnimationFrame","onWindowMouseDown","addEventListener","capture","removeEventListener","cancelTask","abort","cancelAnimationFrame","handleKeyDown","cellEvent","mode","isGridDefaultPrevented","key","onEditorRowChange","commitChangesAndFocus","cellClass","className","displayCellContent","idx","renderEditCell","renderCell","isCellEditable","tabIndex","useRef","useState","flushSync","useRovingTabIndex","clampColumnWidth","getCellClassname","getCellStyle","getHeaderCellRowSpan","getHeaderCellStyle","getLeftRightKey","isCtrlKeyHeldDown","stopPropagation","CalculatedColumn","SortColumn","HeaderRowProps","cellSortableClassname","cellResizable","cellResizableClassname","resizeHandle","resizeHandleClassname","cellDraggableClassname","cellDragging","cellDraggingClassname","cellOver","cellOverClassname","dragImageClassname","SharedHeaderRowProps","Pick","R","SR","React","Key","HeaderCellProps","column","colSpan","rowIdx","isCellSelected","draggedColumnKey","setDraggedColumnKey","HeaderCell","onColumnResize","onColumnResizeEnd","onColumnsReorder","sortColumns","onSortColumnsChange","selectCell","shouldFocusGrid","direction","isOver","setIsOver","dragImageRef","HTMLDivElement","isDragging","key","rowSpan","tabIndex","childTabIndex","onFocus","sortIndex","findIndex","sort","columnKey","sortColumn","undefined","sortDirection","priority","length","ariaSort","sortable","resizable","draggable","className","headerCellClass","onSort","ctrlClick","sortDescendingFirst","nextSort","nextSortColumn","nextSortColumns","splice","handleFocus","event","FocusEvent","idx","onMouseDown","onClick","MouseEvent","HTMLSpanElement","ctrlKey","metaKey","onKeyDown","KeyboardEvent","preventDefault","width","currentTarget","getBoundingClientRect","leftKey","offset","newWidth","onDragStart","DragEvent","dataTransfer","setDragImage","current","dropEffect","onDragEnd","onDragOver","onDrop","onDragEnter","isEventPertinent","onDragLeave","dragTargetProps","ComponentProps","dropTargetProps","style","CSSProperties","content","renderHeaderCell","ResizeHandleProps","ResizeHandle","resizingOffsetRef","isRtl","onPointerDown","PointerEvent","pointerType","buttons","pointerId","setPointerCapture","headerCell","parentElement","right","left","clientX","onPointerMove","onLostPointerCapture","onDoubleClick","relatedTarget","HTMLElement","contains","row","rowClassname","rowSelected","rowSelectedClassname","rowSelectedWithFrozenCell","topSummaryRowClassname","bottomSummaryRowClassname","memo","useState","classnames","getColSpan","CalculatedColumn","Direction","Maybe","Position","ResizedWidth","DataGridProps","HeaderCell","rowSelectedClassname","SharedDataGridProps","React","Key","Pick","R","SR","K","HeaderRowProps","rowIdx","columns","onColumnResize","column","width","onColumnResizeEnd","selectCell","position","lastFrozenColumnIndex","selectedCellIdx","shouldFocusGrid","direction","headerRowClass","headerRow","headerRowClassname","HeaderRow","onColumnsReorder","sortColumns","onSortColumnsChange","draggedColumnKey","setDraggedColumnKey","cells","index","length","colSpan","type","undefined","push","key","idx","props","JSX","Element","row","RenderSortIconProps","RenderSortPriorityProps","RenderSortStatusProps","arrow","arrowClassname","renderSortStatus","sortDirection","priority","renderSortIcon","renderSortPriority","undefined","root","rootClassname","viewportDragging","viewportDraggingClassname","focusSinkClassname","focusSinkHeaderAndSummaryClassname","memo","useRovingTabIndex","getCellClassname","getCellStyle","CellRendererProps","summaryCellClassname","SharedCellRendererProps","Pick","R","SR","SummaryCellProps","row","SummaryCell","column","colSpan","rowIdx","isCellSelected","selectCell","tabIndex","childTabIndex","onFocus","summaryCellClass","className","onMouseDown","idx","renderSummaryCell","props","React","JSX","Element","memo","classnames","getColSpan","getRowStyle","RenderRowProps","bottomSummaryRowClassname","rowClassname","rowSelectedClassname","topSummaryRowClassname","SummaryCell","SharedRenderRowProps","Pick","R","SR","SummaryRowProps","row","top","bottom","lastFrozenColumnIndex","selectedCellIdx","isTop","summaryRow","topSummaryRow","summaryRowClassname","SummaryRow","rowIdx","gridRowStart","viewportColumns","selectCell","ariaRowIndex","cells","index","length","column","colSpan","type","undefined","isCellSelected","idx","push","key","props","React","JSX","Element","initialSelectedRange: CellsRange","renderSortStatus","defaultRenderSortStatus","renderCheckbox","defaultRenderCheckbox","columnWidths","row","cell","scrollTop","scrollLeft","indexes: number[]","cellNavigationMode: CellNavigationMode","style","dragHandleStyle: React.CSSProperties","shouldFocusCell","rowElements: React.ReactNode[]","key: K | number","GroupedColumnHeaderRow","HeaderRow","SummaryRow","memo","useMemo","RowSelectionContext","RowSelectionContextValue","classnames","getRowStyle","BaseRenderRowProps","GroupRow","SELECT_COLUMN_KEY","GroupCell","rowClassname","rowSelectedClassname","groupRow","groupRowClassname","GroupRowRendererProps","R","SR","row","groupBy","toggleGroup","expandedGroupId","GroupedRow","className","rowIdx","viewportColumns","selectedCellIdx","isRowSelected","selectCell","gridRowStart","isRowSelectionDisabled","rangeSelectionMode","onMouseDownCapture","onMouseUpCapture","onMouseEnter","props","idx","key","level","handleSelectGroup","shouldFocusCell","selectionValue","setSize","posInSet","isExpanded","map","column","id","groupKey","childRows","includes","React","JSX","Element","columns","groupBy: string[]","groups: GroupByDictionary","rows","isGroupRow","flattenedRows: Array>","groupRow: GroupRow","groupRow","row","selectedRows","rawIndexes: number[]","GroupedRow","RenderEditCellProps","textEditorInternalClassname","textEditorClassname","autoFocusAndSelect","input","HTMLInputElement","focus","select","textEditor","row","column","onRowChange","onClose","TRow","TSummaryRow","key","event","target","value"],"sources":["../src/utils/colSpanUtils.ts","../src/utils/domUtils.ts","../src/utils/eventUtils.ts","../src/utils/keyboardUtils.ts","../src/utils/renderMeasuringCells.tsx","../src/utils/selectedCellUtils.ts","../src/style/cell.ts","../src/utils/styleUtils.ts","../src/utils/index.ts","../src/cellRenderers/renderCheckbox.tsx","../src/cellRenderers/renderToggleGroup.tsx","../src/cellRenderers/renderValue.tsx","../src/DataGridDefaultRenderersContext.ts","../src/cellRenderers/SelectCellFormatter.tsx","../src/hooks/useRowSelection.ts","../src/Columns.tsx","../src/renderHeaderCell.tsx","../src/hooks/useCalculatedColumns.ts","../src/hooks/useColumnWidths.ts","../src/hooks/useGridDimensions.ts","../src/hooks/useLatestFunc.ts","../src/hooks/useRovingTabIndex.ts","../src/hooks/useViewportColumns.ts","../src/hooks/useViewportRows.ts","../src/Cell.tsx","../src/EditCell.tsx","../src/GroupedColumnHeaderCell.tsx","../src/HeaderCell.tsx","../src/style/row.ts","../src/HeaderRow.tsx","../src/GroupedColumnHeaderRow.tsx","../src/Row.tsx","../src/ScrollToCell.tsx","../src/sortStatus.tsx","../src/style/core.ts","../src/SummaryCell.tsx","../src/SummaryRow.tsx","../src/DataGrid.tsx","../src/GroupCell.tsx","../src/GroupRow.tsx","../src/TreeDataGrid.tsx","../src/editors/textEditor.tsx"],"sourcesContent":["import type { CalculatedColumn, ColSpanArgs } from '../types';\n\nexport function getColSpan(\n column: CalculatedColumn,\n lastFrozenColumnIndex: number,\n args: ColSpanArgs\n): number | undefined {\n const colSpan = typeof column.colSpan === 'function' ? column.colSpan(args) : 1;\n if (\n Number.isInteger(colSpan) &&\n colSpan! > 1 &&\n // ignore colSpan if it spans over both frozen and regular columns\n (!column.frozen || column.idx + colSpan! - 1 <= lastFrozenColumnIndex)\n ) {\n return colSpan!;\n }\n return undefined;\n}\n","import type { Maybe } from '../types';\n\nexport function stopPropagation(event: React.SyntheticEvent) {\n event.stopPropagation();\n}\n\nexport function scrollIntoView(element: Maybe, behavior: ScrollBehavior = 'instant') {\n element?.scrollIntoView({ inline: 'nearest', block: 'nearest', behavior });\n}\n","import type { CellEvent } from '../types';\n\nexport function createCellEvent>(\n event: E\n): CellEvent {\n let defaultPrevented = false;\n const cellEvent = {\n ...event,\n preventGridDefault() {\n defaultPrevented = true;\n },\n isGridDefaultPrevented() {\n return defaultPrevented;\n }\n };\n\n Object.setPrototypeOf(cellEvent, Object.getPrototypeOf(event));\n\n return cellEvent;\n}\n","import type { Direction, Maybe } from '../types';\n\n// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values\nconst nonInputKeys = new Set([\n // Special keys\n 'Unidentified',\n // Modifier keys\n 'Alt',\n 'AltGraph',\n 'CapsLock',\n 'Control',\n 'Fn',\n 'FnLock',\n 'Meta',\n 'NumLock',\n 'ScrollLock',\n 'Shift',\n // Whitespace keys\n 'Tab',\n // Navigation keys\n 'ArrowDown',\n 'ArrowLeft',\n 'ArrowRight',\n 'ArrowUp',\n 'End',\n 'Home',\n 'PageDown',\n 'PageUp',\n // Editing\n 'Insert',\n // UI keys\n 'ContextMenu',\n 'Escape',\n 'Pause',\n 'Play',\n // Device keys\n 'PrintScreen',\n // Function keys\n 'F1',\n // 'F2', /!\\ specifically allowed, do not edit\n 'F3',\n 'F4',\n 'F5',\n 'F6',\n 'F7',\n 'F8',\n 'F9',\n 'F10',\n 'F11',\n 'F12'\n]);\n\nexport function isCtrlKeyHeldDown(e: React.KeyboardEvent): boolean {\n return (e.ctrlKey || e.metaKey) && e.key !== 'Control';\n}\n\n// event.key may differ by keyboard input language, so we use event.keyCode instead\n// event.nativeEvent.code cannot be used either as it would break copy/paste for the DVORAK layout\nconst vKey = 86;\n\nexport function isDefaultCellInput(\n event: React.KeyboardEvent,\n isUserHandlingPaste: boolean\n): boolean {\n // eslint-disable-next-line @typescript-eslint/no-deprecated\n if (isCtrlKeyHeldDown(event) && (event.keyCode !== vKey || isUserHandlingPaste)) return false;\n return !nonInputKeys.has(event.key);\n}\n\n/**\n * By default, the following navigation keys are enabled while an editor is open, under specific conditions:\n * - Tab:\n * - The editor must be an , a