@oceanbase-odc/ob-react-data-grid
Version:
Excel-like grid component built with React, with editors, keyboard navigation, copy & paste, and the like
1 lines • 377 kB
Source Map (JSON)
{"version":3,"file":"bundle.cjs","sources":["../node_modules/_style-inject@0.3.0@style-inject/dist/style-inject.es.js","../src/style/cell.ts","../src/style/core.ts","../src/style/grouprow.ts","../src/style/header.ts","../src/style/row.ts","../src/hooks/useFocusRef.ts","../src/formatters/SelectCellFormatter.tsx","../src/formatters/ValueFormatter.tsx","../src/formatters/ToggleGroupFormatter.tsx","../src/utils/keyboardUtils.ts","../src/utils/selectedCellUtils.ts","../src/utils/index.ts","../src/formatters/RowIndexCellFormatter.tsx","../src/Columns.tsx","../src/editors/ReadOnlyEditor.tsx","../src/hooks/useCalculatedColumns.ts","../src/hooks/useClickOutside.ts","../src/hooks/useGridDimensions.ts","../src/hooks/useViewportColumns.ts","../src/hooks/useViewportRows.ts","../src/hooks/useLatestFunc.ts","../src/hooks/useMouse.ts","../src/types.ts","../src/utils/selectRange.ts","../src/hooks/useSelection.ts","../src/hooks/usePaste.ts","../src/hooks/useLatest.ts","../src/locales/index.ts","../src/headerCells/SortableHeaderCell.tsx","../src/hooks/useCombinedRefs.ts","../src/HeaderCell.tsx","../src/HeaderRow.tsx","../src/Cell.tsx","../src/editors/EditorContainer.tsx","../src/EditCell.tsx","../src/store/rowStore.ts","../src/store/contextMenu.ts","../src/Row.tsx","../src/store/gridStore.ts","../src/SummaryCell.tsx","../src/SummaryRow.tsx","../src/utils/copy.ts","../src/DefaultContextMenuRender.tsx","../src/DefaultEmptyRowsRenderer.tsx","../src/hooks/useDragHeader.ts","../src/hooks/useRowContext.tsx","../src/CustomScrollbar.tsx","../src/DataGrid.tsx","../src/editors/TextEditor.tsx","../src/utils/exportCsv.ts"],"sourcesContent":["function styleInject(css, ref) {\n if ( ref === void 0 ) ref = {};\n var insertAt = ref.insertAt;\n\n if (!css || typeof document === 'undefined') { return; }\n\n var head = document.head || document.getElementsByTagName('head')[0];\n var style = document.createElement('style');\n style.type = 'text/css';\n\n if (insertAt === 'top') {\n if (head.firstChild) {\n head.insertBefore(style, head.firstChild);\n } else {\n head.appendChild(style);\n }\n } else {\n head.appendChild(style);\n }\n\n if (style.styleSheet) {\n style.styleSheet.cssText = css;\n } else {\n style.appendChild(document.createTextNode(css));\n }\n}\n\nexport default styleInject;\n","import { css } from '@linaria/core';\n\nexport const cell = css`\n contain: strict;\n contain: size layout style paint;\n padding: 0 8px;\n border-right: 1px solid var(--border-color);\n border-bottom: 1px solid var(--border-color);\n background-color: inherit;\n position: relative;\n white-space: nowrap;\n overflow: hidden;\n overflow: clip;\n text-overflow: ellipsis;\n`;\n\nexport const cellClassname = `rdg-cell ${cell}`;\n\nconst cellFrozen = css`\n position: sticky;\n // Should have a higher value than 0 to show up above unfrozen cells\n z-index: 1;\n`;\n\nexport const cellFrozenClassname = `rdg-cell-frozen ${cellFrozen}`;\n\nexport const cellFrozenLast = css`\n box-shadow: 2px 0 5px -2px rgba(136, 136, 136, .3);\n`;\n\nexport const cellFrozenLastClassname = `rdg-cell-frozen-last ${cellFrozenLast}`;\n\nconst cellSelected = css`\n&.rdg-cell.rdg-cell-selected {\n background: var(--row-selected-background-color);\n color: var(--selected-color);\n}\n`;\n\nexport const cellSelectedClassname = `rdg-cell-selected ${cellSelected}`;\n","import { css } from '@linaria/core';\nimport darkTheme from './darkTheme';\nimport { row } from './row';\nimport whiteTheme from './whiteTheme';\n\n\nconst root = css`\n ${whiteTheme}\n --selected-color: #fff;\n &.rdg-dark {\n ${darkTheme}\n }\n\n --color: var(--text-color-primary);\n --border-color: var(--odc-border-color);\n --background-color: var(--background-primary-color);\n --header-background-color: var(--table-header-background-color);\n --row-hover-background-color: var(--table-focus-color);\n --row-selected-background-color: var(--table-hover-color);\n --row-selected-cell-background-color: var(--table-hover-color);\n --row-selected-hover-background-color: var(--table-focus-color);\n --selection-color: #66afe9;\n --font-size: 12px;\n\n // https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context\n // We set a stacking context so internal elements don't render on top of external components.\n contain: strict;\n contain: size layout style paint;\n content-visibility: auto;\n height: 350px;\n outline: 1px solid var(--border-color);\n box-sizing: border-box;\n user-select: none;\n background-color: var(--background-color);\n color: var(--color);\n font-size: var(--font-size);\n\n // set stacking context in safari\n @supports not (contain: strict) {\n position: relative;\n z-index: 0;\n }\n\n *,\n *::before,\n *::after {\n box-sizing: inherit;\n }\n`;\n\nexport const rootClassname = `rdg ${root}`;\n\nconst focusSink = css`\n position: sticky;\n top: 0;\n left: 0;\n height: 0;\n width: 0;\n outline: 0;\n`;\n\nexport const focusSinkClassname = `rdg-focus-sink ${focusSink}`;\n\nconst viewportDragging = css`\n &.${row} {\n cursor: move;\n }\n`;\n\nexport const viewportDraggingClassname = `rdg-viewport-dragging ${viewportDragging}`;\n","import { css } from '@linaria/core';\nimport { rowSelected } from './row';\nimport { cell, cellFrozenLast } from './cell';\n\nconst groupRow = css`\n &:not(.${rowSelected}) {\n background-color: var(--header-background-color);\n }\n\n > .${cell}:not(:last-child):not(.${cellFrozenLast}) {\n border-right: none;\n }\n`;\n\nexport const groupRowClassname = `rdg-group-row ${groupRow}`;\n\nconst groupRowSelected = css`\n &::after {\n content: \"\";\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n box-shadow: inset 0 0 0 2px var(--selection-color);\n pointer-events: none;\n z-index: 2;\n }\n\n > .${cell}:first-child {\n // preserve left border on the first frozen cell after scrolling to the right\n box-shadow: inset 2px 0 0 0 var(--selection-color);\n }\n`;\n\n// TODO: rename class\nexport const groupRowSelectedClassname = `rdg-group-row-selected ${groupRowSelected}`;\n","import { css } from '@linaria/core';\n\nconst headerRowAndFilterRow = css`\n contain: strict;\n contain: size layout style paint;\n display: grid;\n grid-template-columns: var(--template-columns);\n width: var(--row-width);\n position: sticky;\n font-weight: bold;\n z-index: 3;\n`;\n\nconst headerRow = css`\n grid-template-rows: var(--header-row-height);\n height: var(--header-row-height); // needed on Firefox\n line-height: var(--header-row-height);\n top: 0;\n touch-action: none;\n`;\n\nexport const headerRowClassname = `rdg-header-row ${headerRowAndFilterRow} ${headerRow}`;\n\nconst filterRow = css`\n grid-template-rows: var(--filter-row-height);\n height: var(--filter-row-height); // needed on Firefox\n line-height: var(--filter-row-height);\n top: var(--header-row-height);\n`;\n\nexport const filterRowClassname = `rdg-filter-row ${headerRowAndFilterRow} ${filterRow}`;\n","import { css } from '@linaria/core';\n\nexport const row = css`\n contain: strict;\n contain: size layout style paint;\n display: grid;\n grid-template-rows: var(--row-height);\n grid-template-columns: var(--template-columns);\n position: absolute;\n left: 0;\n width: var(--row-width);\n height: var(--row-height); // needed on Firefox\n line-height: var(--row-height);\n background-color: var(--background-color);\n color: var(--color);\n`;\n\nexport const rowCellShowMenuClassname = css`\n &.rdg-row {\n contain: none;\n }\n`;\n\nexport const rowClassname = `rdg-row ${row}`;\n\n\nexport const rowIsAddClassname = css`\n &.rdg-row {\n background-color: var(--table-new-color);\n }\n`;\n\nexport const rowIsDeleteClassname = css`\n &.rdg-row {\n background-color: var(--table-delete-color);\n }\n`;\n\nexport const rowSelected = css`\n &.rdg-row-selected.rdg-row {\n background-color: var(--row-selected-background-color);\n color: var(--selected-color);\n .rdg-cell {\n background-color: var(--row-selected-cell-background-color);\n color: var(--selected-color);\n }\n }\n`;\n\nexport const rowSelectedClassname = `rdg-row-selected ${rowSelected}`;\n\nconst summaryRow = css`\n position: sticky;\n z-index: 3;\n grid-template-rows: var(--summary-row-height);\n height: var(--summary-row-height); // needed on Firefox\n line-height: var(--summary-row-height);\n`;\n\nexport const lastSummaryRowClassname = css`\n .rdg-cell {\n border-bottom-width: 2px;\n }\n`;\n\nexport const summaryRowClassname = `rdg-summary-row ${summaryRow}`;\n\n\nexport const dropOverRowClassName = css`\n &:before {\n content: '';\n position: absolute;\n width: calc(100%);\n height: 2px;\n background: var(--row-selected-background-color);\n top: 0px;\n z-index: 1;\n }\n`;\n\nexport const dragingRowClassName = css`\n opacity: 0.6;\n`;\n\n\nexport const rdgDragPreviewClassname = css`\nposition: absolute;\nleft: 0px;\nheight: 100%;\nwidth: 100%;\npointer-events: none;\n`;\n\nexport const toolsOverlayClassname = css`\n .ant-popover-arrow {\n display: none;\n }\n .ant-popover-inner-content {\n padding: 0px;\n padding-left: 10px;\n }\n .ant-popover-inner {\n background-color: transparent;\n box-shadow: unset;\n }\n &.ant-popover {\n padding-top: 0px;\n }\n`;\n\n\nexport const rowMainSelectClassname = css`\n &.rdg-row > .rdg-cell div:nth-child(1) {\n font-weight: bold;\n }\n`;","import { useRef, useLayoutEffect } from 'react';\n\nexport function useFocusRef<T extends HTMLOrSVGElement>(isCellSelected: boolean | undefined) {\n const ref = useRef<T>(null);\n useLayoutEffect(() => {\n if (!isCellSelected) return;\n ref.current?.focus({ preventScroll: true });\n }, [isCellSelected]);\n\n return ref;\n}\n","import clsx from 'clsx';\nimport { css } from '@linaria/core';\nimport { useFocusRef } from '../hooks/useFocusRef';\n\nconst checkboxLabel = css`\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n margin-right: 1px; // align checkbox in row group cell\n`;\n\nconst checkboxLabelClassname = `rdg-checkbox-label ${checkboxLabel}`;\n\nconst checkboxInput = css`\n all: unset;\n width: 0;\n margin: 0;\n`;\n\nconst checkboxInputClassname = `rdg-checkbox-input ${checkboxInput}`;\n\nconst checkbox = css`\n content: '';\n width: 20px;\n height: 20px;\n border: 2px solid var(--border-color);\n background-color: var(--background-color);\n\n .${checkboxInput}:checked + & {\n background-color: var(--checkbox-color);\n box-shadow: inset 0px 0px 0px 4px var(--background-color);\n }\n\n .${checkboxInput}:focus + & {\n border-color: var(--checkbox-focus-color);\n }\n`;\n\nconst checkboxClassname = `rdg-checkbox ${checkbox}`;\n\nconst checkboxLabelDisabled = css`\n cursor: default;\n\n .${checkbox} {\n border-color: var(--checkbox-disabled-border-color);\n background-color: var(--checkbox-disabled-background-color);\n }\n`;\n\nconst checkboxLabelDisabledClassname = `rdg-checkbox-label-disabled ${checkboxLabelDisabled}`;\n\ntype SharedInputProps = Pick<React.InputHTMLAttributes<HTMLInputElement>,\n | 'disabled'\n | 'tabIndex'\n | 'onClick'\n>;\n\ninterface SelectCellFormatterProps extends SharedInputProps {\n isCellSelected?: boolean;\n value: boolean;\n onChange: (value: boolean, isShiftClick: boolean) => void;\n aria?: React.AriaAttributes;\n}\n\nexport function SelectCellFormatter({\n value,\n tabIndex,\n isCellSelected,\n disabled,\n onClick,\n onChange,\n aria,\n}: SelectCellFormatterProps) {\n const inputRef = useFocusRef<HTMLInputElement>(isCellSelected);\n\n function handleChange(e: React.ChangeEvent<HTMLInputElement>) {\n onChange(e.target.checked, (e.nativeEvent as MouseEvent).shiftKey);\n }\n\n return (\n <label className={clsx(checkboxLabelClassname, { [checkboxLabelDisabledClassname]: disabled })}>\n <input\n {...aria}\n tabIndex={tabIndex}\n ref={inputRef}\n type=\"checkbox\"\n className={checkboxInputClassname}\n disabled={disabled}\n checked={value}\n onChange={handleChange}\n onClick={onClick}\n />\n <div className={checkboxClassname} />\n </label>\n );\n}\n","import type { FormatterProps } from '../types';\nimport React from 'react';\n\nconst ValueFormatter = React.memo(function ValueFormatter<R, SR>(props: FormatterProps<R, SR>) {\n try {\n return <>{props.row[props.column.key as keyof R]}</>;\n } catch (e) {\n return null;\n }\n}, (prevProps, nextProps) => prevProps.row === nextProps.row && prevProps.column === nextProps.column);\n\nexport { ValueFormatter }\n","import { css } from '@linaria/core';\nimport type { GroupFormatterProps } from '../types';\nimport { useFocusRef } from '../hooks/useFocusRef';\n\nconst groupCellContent = css`\n outline: none;\n`;\n\nconst groupCellContentClassname = `rdg-group-cell-content ${groupCellContent}`;\n\nconst caret = css`\n margin-left: 4px;\n stroke: currentColor;\n stroke-width: 1.5px;\n fill: transparent;\n vertical-align: middle;\n\n > path {\n transition: d .1s;\n }\n`;\n\nconst caretClassname = `rdg-caret ${caret}`;\n\nexport function ToggleGroupFormatter<R, SR>({\n groupKey,\n isExpanded,\n isCellSelected,\n toggleGroup\n}: GroupFormatterProps<R, SR>) {\n const cellRef = useFocusRef<HTMLSpanElement>(isCellSelected);\n\n function handleKeyDown({ key }: React.KeyboardEvent<HTMLSpanElement>) {\n if (key === 'Enter') {\n toggleGroup();\n }\n }\n\n const d = isExpanded ? 'M1 1 L 7 7 L 13 1' : 'M1 7 L 7 1 L 13 7';\n\n return (\n <span\n ref={cellRef}\n className={groupCellContentClassname}\n tabIndex={-1}\n onKeyDown={handleKeyDown}\n >\n {groupKey}\n <svg viewBox=\"0 0 14 8\" width=\"14\" height=\"8\" className={caretClassname}>\n <path d={d} />\n </svg>\n </span>\n );\n}\n","import React from \"react\";\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\nexport function isDefaultCellInput(event: React.KeyboardEvent<HTMLDivElement>): boolean {\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 <input>, a <textarea>, or a <select> element.\n * - The editor element must be the only immediate child of the editor container/a label.\n */\nexport function onEditorNavigation({ key, target }: React.KeyboardEvent<HTMLDivElement>): boolean {\n if (key === 'Tab' && (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement || target instanceof HTMLSelectElement)) {\n return target.matches('.rdg-editor-container > :only-child, .rdg-editor-container > label:only-child > :only-child');\n }\n return false;\n}\n\n\nexport function isCtrlDown(event: React.MouseEvent) {\n var isMac = /macintosh|mac os x/i.test(navigator.userAgent);\n return isMac ? event.metaKey : event.ctrlKey;\n}","import type { CalculatedColumn, Column, Position, CellNavigationMode } from '../types';\n\ninterface IsSelectedCellEditableOpts<R, SR> {\n column: Column<R, SR>;\n row: R;\n}\n\nexport function isCellEditable<R, SR>({ column, row }: IsSelectedCellEditableOpts<R, SR>): boolean {\n return column?.editor != null\n && (typeof column?.editable === 'function' ? column?.editable(row) : column?.editable) !== false;\n}\n\ninterface GetNextSelectedCellPositionOpts<R, SR> {\n cellNavigationMode: CellNavigationMode;\n columns: readonly CalculatedColumn<R, SR>[];\n rows: readonly R[];\n nextPosition: Position;\n}\n\nexport function getNextSelectedCellPosition<R, SR>({\n cellNavigationMode,\n columns,\n rows,\n nextPosition,\n}: GetNextSelectedCellPositionOpts<R, SR>): Position {\n const rowsCount = rows.length;\n let position = nextPosition;\n\n if (cellNavigationMode !== 'NONE') {\n const { columnIdx, rowIdx } = nextPosition;\n const columnsCount = columns.length;\n const isAfterLastColumn = columnIdx === columnsCount;\n const isBeforeFirstColumn = columnIdx === -1;\n\n if (isAfterLastColumn) {\n if (cellNavigationMode === 'CHANGE_ROW') {\n const isLastRow = rowIdx === rowsCount - 1;\n if (!isLastRow) {\n position = {\n columnIdx: 0,\n rowIdx: rowIdx + 1,\n };\n }\n } else {\n position = {\n rowIdx,\n columnIdx: 0,\n };\n }\n } else if (isBeforeFirstColumn) {\n if (cellNavigationMode === 'CHANGE_ROW') {\n const isFirstRow = rowIdx === 0;\n if (!isFirstRow) {\n position = {\n rowIdx: rowIdx - 1,\n columnIdx: columnsCount - 1,\n };\n }\n } else {\n position = {\n rowIdx,\n columnIdx: columnsCount - 1,\n };\n }\n }\n }\n\n return position;\n}\n\ninterface CanExitGridOpts<R, SR> {\n cellNavigationMode: CellNavigationMode;\n columns: readonly CalculatedColumn<R, SR>[];\n rowsCount: number;\n selectedRange: Position;\n shiftKey: boolean;\n}\n\nexport function canExitGrid<R, SR>({ cellNavigationMode, columns, rowsCount, selectedRange: { rowIdx, columnIdx }, shiftKey }: CanExitGridOpts<R, SR>): boolean {\n // When the cellNavigationMode is 'none' or 'changeRow', you can exit the grid if you're at the first or last cell of the grid\n // When the cellNavigationMode is 'loopOverRow', there is no logical exit point so you can't exit the grid\n if (cellNavigationMode === 'NONE' || cellNavigationMode === 'CHANGE_ROW') {\n const atLastCellInRow = columnIdx === columns.length - 1;\n const atFirstCellInRow = columnIdx === 0;\n const atLastRow = rowIdx === rowsCount - 1;\n const atFirstRow = rowIdx === 0;\n\n return shiftKey ? atFirstCellInRow && atFirstRow : atLastCellInRow && atLastRow;\n }\n\n return false;\n}\n","import clsx from 'clsx';\n\nimport type { CalculatedColumn } from '../types';\nimport { cellClassname, cellFrozenClassname, cellFrozenLastClassname } from '../style';\n\nexport * from './domUtils';\nexport * from './keyboardUtils';\nexport * from './selectedCellUtils';\n\nexport function assertIsValidKeyGetter<R>(keyGetter: unknown): asserts keyGetter is (row: R) => React.Key {\n if (typeof keyGetter !== 'function') {\n throw new Error('Please specify the rowKeyGetter prop to use selection');\n }\n}\n\nexport function getCellStyle<R, SR>(column: CalculatedColumn<R, SR>): React.CSSProperties {\n return {\n gridColumnStart: column.idx + 1,\n left: column.frozen ? `var(--frozen-left-${column.key})` : undefined\n };\n}\n\nexport function getCellClassname<R, SR>(column: CalculatedColumn<R, SR>, ...extraClasses: Parameters<typeof clsx>): string {\n return clsx(\n cellClassname, {\n [cellFrozenClassname]: column.frozen,\n [cellFrozenLastClassname]: column.isLastFrozenColumn\n },\n ...extraClasses\n );\n}\n\n/**\n * 生成一个唯一key\n * @param suffixStr key后缀\n */\n\nexport const generateUniqKey = (function () {\n let key = 0;\n return function (suffixStr: string = ''): string {\n key = key + 1;\n return `${key}-${Date.now()}-${~~(Math.random() * 10000)}-${suffixStr || ''}`;\n };\n})();\n\nexport const createNewRow = function (key: string) {\n return {\n [key]: generateUniqKey(),\n _created: true,\n };\n};","import { useCallback } from 'react';\nimport { FormatterProps } from '../types';\nimport { css } from '@linaria/core';\nimport { isCtrlDown } from '../utils';\n\nexport const rowIndexCellClassName = css`\nfont-size: 12px;\ncursor: pointer;\ndisplay: flex;\nalign-items: center;\njustify-content: flex-end;\npadding-right: 5px;\n`\n\nconst selectedClassName = css`\n background: var(--row-selected-hover-background-color);\n color: var(--color);\n`\n\n\ninterface IProps extends FormatterProps {\n\n}\n\nconst RowIndexCellFormatter: React.FC<IProps> = function (props) {\n const { isRowSelected, onRowSelectionChange, rowIdx } = props;\n\n const onClick = useCallback((e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {\n onRowSelectionChange(!isRowSelected, e.nativeEvent.shiftKey, isCtrlDown(e));\n }, [isRowSelected, onRowSelectionChange])\n return <div\n onClick={onClick}\n className={`${rowIndexCellClassName} ${isRowSelected ? selectedClassName : ''}`}\n >\n {rowIdx + 1}\n </div>\n}\n\n\nexport default RowIndexCellFormatter;","import { css } from '@linaria/core';\nimport React from 'react';\nimport clsx from 'clsx';\nimport RowIndexCellFormatter from './formatters/RowIndexCellFormatter';\nimport type { Column } from './types';\n\nexport const SELECT_COLUMN_KEY = 'select-row';\n\nconst selectCellClassName = css`\n&.rdg-cell.rdg-cell-frozen {\n background-color: var(--header-background-color);\n padding: 0px;\n color: var(--color);\n &:hover {\n color: var(--color) !important;\n }\n}\n`\n\nconst rowCellSelectClassName = css`\n&.rdg-cell.${selectCellClassName} {\n background-color: var(--row-hover-background-color);\n}\n`\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport const SelectColumn: Column<any, any> = {\n key: SELECT_COLUMN_KEY,\n name: '',\n width: 70,\n resizable: false,\n sortable: false,\n clickable: false,\n frozen: true,\n cellClass: (row, isRowCellSelected) => {\n return clsx(selectCellClassName, { [rowCellSelectClassName]: isRowCellSelected })\n }, \n headerRenderer(props) {\n return (\n <div \n style={{ width: '100%', height: '100%' }}\n >\n </div>\n );\n },\n formatter: RowIndexCellFormatter\n};\n\n","import { css } from '@linaria/core';\nimport type { EditorProps } from '../types';\n\nconst textEditor = css`\n appearance: none;\n\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n padding: 0px 6px 0 6px;\n border: 2px solid #ccc;\n vertical-align: top;\n color: var(--color);\n background-color: var(--background-color);\n\n font-family: inherit;\n font-size: var(--font-size);\n\n &:focus {\n border-color: var(--selection-color);\n outline: none;\n }\n\n &::placeholder {\n color: #999;\n opacity: 1;\n }\n`;\n\nexport const textEditorClassname = `rdg-text-editor ${textEditor}`;\n\nexport default function ReadOnlyEditor<TRow, TSummaryRow = unknown>({\n row,\n column,\n onClose\n}: EditorProps<TRow, TSummaryRow>) {\n return (\n <input\n readOnly\n className={textEditorClassname}\n value={row?.[column.key as keyof TRow] as unknown as string || ''}\n onBlur={() => onClose(true)}\n />\n );\n}\n","import { useMemo } from 'react';\n\nimport type { CalculatedColumn, Column, ColumnMetric } from '../types';\nimport type { DataGridProps } from '../types';\nimport { ValueFormatter, ToggleGroupFormatter } from '../formatters';\nimport { SELECT_COLUMN_KEY } from '../Columns';\nimport ReadOnlyEditor from '../editors/ReadOnlyEditor';\n\ninterface CalculatedColumnsArgs<R, SR> extends Pick<DataGridProps<R, SR>, 'defaultColumnOptions'> {\n rawColumns: readonly Column<R, SR>[];\n rawGroupBy?: readonly string[];\n viewportWidth: number;\n scrollLeft: number;\n columnWidths: ReadonlyMap<string, number>;\n enableVirtualization: boolean;\n rowsCount: number;\n readonly?: boolean;\n}\n\nexport function useCalculatedColumns<R, SR>({\n rawColumns,\n columnWidths,\n viewportWidth,\n scrollLeft,\n defaultColumnOptions,\n rawGroupBy,\n enableVirtualization,\n rowsCount,\n readonly\n}: CalculatedColumnsArgs<R, SR>) {\n const minColumnWidth = defaultColumnOptions?.minWidth ?? 80;\n const defaultFormatter = defaultColumnOptions?.formatter ?? ValueFormatter;\n const defaultSortable = defaultColumnOptions?.sortable ?? true;\n const defaultResizable = defaultColumnOptions?.resizable ?? false;\n\n const selectColumnWidth = Math.floor(Math.floor((Math.log(rowsCount || 1) / Math.log(10))) * 10 + 35);\n\n const { columns, lastFrozenColumnIndex, groupBy } = useMemo(() => {\n // Filter rawGroupBy and ignore keys that do not match the columns prop\n const groupBy: string[] = [];\n let lastFrozenColumnIndex = -1;\n\n const columns = rawColumns.map(rawColumn => {\n const rowGroup = rawGroupBy?.includes(rawColumn.key) ?? false;\n // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing\n const frozen = rowGroup || rawColumn.frozen || false;\n\n const column: CalculatedColumn<R, SR> = {\n ...rawColumn,\n idx: 0,\n frozen,\n isLastFrozenColumn: false,\n rowGroup,\n sortable: rawColumn.sortable ?? defaultSortable,\n resizable: rawColumn.resizable ?? defaultResizable,\n clickable: rawColumn.clickable ?? true,\n formatter: rawColumn.formatter ?? defaultFormatter as any,\n editor: readonly ? ReadOnlyEditor : rawColumn.editor\n };\n\n if (rawColumn.key === SELECT_COLUMN_KEY) {\n column.width = selectColumnWidth\n }\n if (rowGroup) {\n column.groupFormatter ??= ToggleGroupFormatter;\n }\n\n if (frozen) {\n lastFrozenColumnIndex++;\n }\n\n return column;\n });\n\n columns.sort(({ key: aKey, frozen: frozenA }, { key: bKey, frozen: frozenB }) => {\n // Sort select column first:\n if (aKey === SELECT_COLUMN_KEY) return -1;\n if (bKey === SELECT_COLUMN_KEY) return 1;\n\n // Sort grouped columns second, following the groupBy order:\n if (rawGroupBy?.includes(aKey)) {\n if (rawGroupBy.includes(bKey)) {\n return rawGroupBy.indexOf(aKey) - rawGroupBy.indexOf(bKey);\n }\n return -1;\n }\n if (rawGroupBy?.includes(bKey)) return 1;\n\n // Sort frozen columns third:\n if (frozenA) {\n if (frozenB) return 0;\n return -1;\n }\n if (frozenB) return 1;\n\n // Sort other columns last:\n return 0;\n });\n\n columns.forEach((column, idx) => {\n column.idx = idx;\n });\n\n if (lastFrozenColumnIndex !== -1) {\n columns[lastFrozenColumnIndex].isLastFrozenColumn = true;\n }\n\n return {\n columns,\n lastFrozenColumnIndex,\n groupBy\n };\n }, [rawColumns, defaultFormatter, defaultResizable, defaultSortable, rawGroupBy, rowsCount, readonly]);\n\n const { layoutCssVars, totalColumnWidth, totalFrozenColumnWidth, columnMetrics } = useMemo(() => {\n const columnMetrics = new Map<CalculatedColumn<R, SR>, ColumnMetric>();\n let left = 0;\n let totalColumnWidth = 0;\n let totalFrozenColumnWidth = 0;\n let templateColumns = '';\n let allocatedWidth = 0;\n let unassignedColumnsCount = 0;\n\n // 根据 column 配置指定的 column.width 计算列宽\n for (const column of columns) {\n let width = getSpecifiedWidth(column, columnWidths, viewportWidth);\n\n if (width === undefined) {\n unassignedColumnsCount++;\n } else {\n width = clampColumnWidth(width, column, minColumnWidth);\n allocatedWidth += width;\n columnMetrics.set(column, { width, left: 0 });\n }\n }\n /**\n * 多-1来为了防止出现抖动的bug\n */\n const unallocatedWidth = viewportWidth - allocatedWidth - 1;\n const floorWidth = Math.floor(unallocatedWidth / unassignedColumnsCount);\n const lastValue = Math.floor(unallocatedWidth - floorWidth * (unassignedColumnsCount - 1));\n let unallocatedColumnWidth = [];\n if (unassignedColumnsCount > 0) {\n unallocatedColumnWidth = new Array(unassignedColumnsCount - 1).fill(floorWidth);\n unallocatedColumnWidth.push(lastValue)\n }\n\n // 计算未指定 column.width 的列宽和 column.left\n for (const column of columns) {\n let width: number;\n if (columnMetrics.has(column)) {\n const columnMetric = columnMetrics.get(column)!;\n columnMetric.left = left;\n ({ width } = columnMetric);\n } else {\n width = clampColumnWidth(unallocatedColumnWidth.pop(), column, minColumnWidth);\n columnMetrics.set(column, { width, left });\n }\n totalColumnWidth += width;\n left += width;\n templateColumns += `${width}px `;\n }\n\n if (lastFrozenColumnIndex !== -1) {\n const columnMetric = columnMetrics.get(columns[lastFrozenColumnIndex])!;\n totalFrozenColumnWidth = columnMetric.left + columnMetric.width;\n }\n\n const layoutCssVars: Record<string, string> = {\n '--template-columns': templateColumns\n };\n\n for (let i = 0; i <= lastFrozenColumnIndex; i++) {\n const column = columns[i];\n layoutCssVars[`--frozen-left-${column.key}`] = `${columnMetrics.get(column)!.left}px`;\n }\n\n return { layoutCssVars, totalColumnWidth, totalFrozenColumnWidth, columnMetrics };\n }, [columnWidths, columns, viewportWidth, minColumnWidth, lastFrozenColumnIndex]);\n\n const [colOverscanStartIdx, colOverscanEndIdx, colVisibleStartIdx, colVisibleEndIdx] = useMemo((): [number, number] | [number, number, number, number] => {\n if (!enableVirtualization) {\n return [0, columns.length - 1];\n }\n // get the viewport's left side and right side positions for non-frozen columns\n const viewportLeft = scrollLeft + totalFrozenColumnWidth;\n const viewportRight = scrollLeft + viewportWidth;\n // get first and last non-frozen column indexes\n const lastColIdx = columns.length - 1;\n const firstUnfrozenColumnIdx = Math.min(lastFrozenColumnIndex + 1, lastColIdx);\n\n // skip rendering non-frozen columns if the frozen columns cover the entire viewport\n if (viewportLeft >= viewportRight) {\n return [firstUnfrozenColumnIdx, firstUnfrozenColumnIdx];\n }\n\n // get the first visible non-frozen column index\n let colVisibleStartIdx = firstUnfrozenColumnIdx;\n while (colVisibleStartIdx < lastColIdx) {\n const { left, width } = columnMetrics.get(columns[colVisibleStartIdx])!;\n // if the right side of the columnn is beyond the left side of the available viewport,\n // then it is the first column that's at least partially visible\n if (left + width > viewportLeft) {\n break;\n }\n colVisibleStartIdx++;\n }\n\n // get the last visible non-frozen column index\n let colVisibleEndIdx = colVisibleStartIdx;\n while (colVisibleEndIdx < lastColIdx) {\n const { left, width } = columnMetrics.get(columns[colVisibleEndIdx])!;\n // if the right side of the column is beyond or equal to the right side of the available viewport,\n // then it the last column that's at least partially visible, as the previous column's right side is not beyond the viewport.\n if (left + width >= viewportRight) {\n break;\n }\n colVisibleEndIdx++;\n }\n\n const colOverscanStartIdx = Math.max(firstUnfrozenColumnIdx, colVisibleStartIdx - 10);\n const colOverscanEndIdx = Math.min(lastColIdx, colVisibleEndIdx + 10);\n\n return [colOverscanStartIdx, colOverscanEndIdx, colVisibleStartIdx, colVisibleEndIdx];\n }, [columnMetrics, columns, lastFrozenColumnIndex, scrollLeft, totalFrozenColumnWidth, viewportWidth, enableVirtualization]);\n\n return {\n columns,\n colOverscanStartIdx,\n colOverscanEndIdx,\n layoutCssVars,\n columnMetrics,\n totalColumnWidth,\n lastFrozenColumnIndex,\n totalFrozenColumnWidth,\n groupBy,\n colVisibleStartIdx,\n colVisibleEndIdx,\n };\n}\n\nfunction getSpecifiedWidth<R, SR>(\n { key, width }: Column<R, SR>,\n columnWidths: ReadonlyMap<string, number>,\n viewportWidth: number\n): number | undefined {\n if (columnWidths.has(key)) {\n // Use the resized width if available\n return columnWidths.get(key);\n }\n if (typeof width === 'number') {\n return width;\n }\n if (typeof width === 'string' && /^\\d+%$/.test(width)) {\n return Math.floor(viewportWidth * parseInt(width, 10) / 100);\n }\n return undefined;\n}\n\nfunction clampColumnWidth<R, SR>(\n width: number,\n { minWidth, maxWidth, key }: Column<R, SR>,\n minColumnWidth: number\n): number {\n if (key === SELECT_COLUMN_KEY) {\n return width\n }\n width = Math.max(width, minWidth ?? minColumnWidth);\n\n if (typeof maxWidth === 'number') {\n return Math.min(width, maxWidth);\n }\n\n return width;\n}\n","import { useRef, useEffect } from 'react';\n\n/**\n * Detecting outside click on a react component is surprisingly hard.\n * A general approach is to have a global click handler on the document\n * which checks if the click target is inside the editor container or\n * not using editorContainer.contains(e.target). This approach works well\n * until portals are used for editors. Portals render children into a DOM\n * node that exists outside the DOM hierarchy of the parent component so\n * editorContainer.contains(e.target) does not work. Here are some examples\n * of the DOM structure with different types of editors\n *\n *\n * SimpleEditor for example Texbox (No Portals)\n * <div data-grid>..</div>\n * <div portal-created-by-the-grid-for-editors>\n * <div editor-container>\n * <div simple-editor>..</div>\n * </div>\n * </div>\n *\n * ComplexEditor for example Modals (using Portals)\n * <div data-grid>..</div>\n * <div portal-created-by-the-grid-for-editors>\n * <div editor-container>\n * // Nothing here\n * </div>\n * </div>\n * <div portal-created-by-the-editor>\n * <div complex-editor>..</div>\n * </div>\n *\n *\n * One approach to detect outside click is to use synthetic event bubbling through\n * portals. An event fired from inside a portal will propagate to ancestors\n * in the containing React tree, even if those elements are not ancestors\n * in the DOM tree. This means a click handler can be attached on the window\n * and on the editor container. The editor container can set a flag to notify\n * that the click was inside the editor and the window click handler can use\n * this flag to call onClickOutside. This approach however has a few caveats\n * - Click handler on the window is set using window.addEventListener\n * - Click handler on the editor container is set using onClick prop\n *\n * This means if a child component inside the editor calls e.stopPropagation\n * then the click handler on the editor container will not be called whereas\n * the document click handler will be called.\n * https://github.com/facebook/react/issues/12518\n *\n * To solve this issue onClickCapture event is used.\n */\n\nexport function useClickOutside(onClick: () => void) {\n const frameRequestRef = useRef<number | undefined>();\n\n function cancelAnimationFrameRequest() {\n if (typeof frameRequestRef.current === 'number') {\n cancelAnimationFrame(frameRequestRef.current);\n frameRequestRef.current = undefined;\n }\n }\n\n // We need to prevent the `useEffect` from cleaning up between re-renders,\n // as `handleDocumentClick` might otherwise miss valid click events.\n // To that end we instead access the latest `onClick` prop via a ref.\n const onClickRef = useRef((): void => {\n throw new Error('Cannot call an event handler while rendering.');\n });\n\n useEffect(() => {\n onClickRef.current = onClick;\n });\n\n useEffect(() => {\n function onOutsideClick() {\n frameRequestRef.current = undefined;\n onClickRef.current();\n }\n\n function onWindowCaptureClick() {\n cancelAnimationFrameRequest();\n frameRequestRef.current = requestAnimationFrame(onOutsideClick);\n }\n\n window.addEventListener('click', onWindowCaptureClick, { capture: true });\n\n return () => {\n window.removeEventListener('click', onWindowCaptureClick, { capture: true });\n cancelAnimationFrameRequest();\n };\n }, []);\n\n return cancelAnimationFrameRequest;\n}\n","import { useRef, useState, useLayoutEffect } from 'react';\nimport { ResizeObserver as ResizeObserverPolyfills } from '@juggle/resize-observer';\n\nconst ResizeObserver = window.ResizeObserver || ResizeObserverPolyfills;\n\nexport function useGridDimensions(): [ref: React.RefObject<HTMLDivElement>, width: number, height: number] {\n const gridRef = useRef<HTMLDivElement>(null);\n const [gridWidth, setGridWidth] = useState(1);\n const [gridHeight, setGridHeight] = useState(1);\n\n useLayoutEffect(() => {\n\n // don't break in jest/jsdom and browsers that don't support ResizeObserver\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (ResizeObserver == null) return;\n \n\n const resizeObserver = new ResizeObserver(() => {\n // Get dimensions without scrollbars.\n // The dimensions given by the callback entries in Firefox do not substract the scrollbar sizes.\n const {clientWidth, clientHeight } = gridRef.current!;\n setGridWidth(clientWidth);\n setGridHeight(clientHeight);\n });\n\n resizeObserver.observe(gridRef.current!);\n\n return () => {\n resizeObserver.disconnect();\n };\n }, []);\n\n return [gridRef, gridWidth, gridHeight];\n}\n","import { useMemo } from 'react';\nimport type { CalculatedColumn } from '../types';\n\ninterface ViewportColumnsArgs<R, SR> {\n columns: readonly CalculatedColumn<R, SR>[];\n colOverscanStartIdx: number;\n colOverscanEndIdx: number;\n}\n\nexport function useViewportColumns<R, SR>({\n columns,\n colOverscanStartIdx,\n colOverscanEndIdx,\n}: ViewportColumnsArgs<R, SR>) {\n // find the column that spans over a column within the visible columns range and adjust colOverscanStartIdx\n const startIdx = colOverscanStartIdx;\n\n return useMemo((): readonly CalculatedColumn<R, SR>[] => {\n const viewportColumns: CalculatedColumn<R, SR>[] = [];\n for (let colIdx = 0; colIdx <= colOverscanEndIdx; colIdx++) {\n const column = columns[colIdx];\n\n if (colIdx < startIdx && !column.frozen) continue;\n viewportColumns.push(column);\n }\n\n return viewportColumns;\n }, [startIdx, colOverscanEndIdx, columns]);\n}\n","import { useCallback, useMemo } from 'react';\nimport { isString, reverse, sortBy, toNumber } from 'lodash';\nimport type { Column, Filters, SortInfo } from '../types';\nimport { FILTER_EMPTY_KEY, FILTER_NULL_KEY } from '../DataGrid';\n\nconst RENDER_BACTCH_SIZE = 8;\n\ninterface ViewportRowsArgs<R, SR> {\n rawRows: readonly R[];\n rowHeight: number;\n clientHeight: number;\n scrollTop: number;\n enableVirtualization: boolean;\n rawColumns: readonly Column<R, SR>[];\n sortInfo: SortInfo;\n rowFilters: Filters;\n searchKey?: string;\n}\n\nexport function useViewportRows<R, SR>({\n rawRows,\n rawColumns,\n rowHeight,\n clientHeight,\n scrollTop,\n enableVirtualization,\n sortInfo,\n rowFilters,\n searchKey\n}: ViewportRowsArgs<R, SR>) {\n const rowsCount = rawRows.length;\n\n const filterRows = useCallback((rawRows: readonly R[]) => {\n const filterColumnKeys = Object.keys(rowFilters).filter(columnKey => rowFilters[columnKey]?.size);\n if (!filterColumnKeys.length && !searchKey) return rawRows;\n return rawRows.filter((row: any) => {\n let isPass = true;\n if (searchKey) {\n /**\n * 查找过滤逻辑\n */\n let hit = false;\n for (const columnName of Object.keys(row)) {\n const columnValue = row[columnName] || '';\n if (\n isString(columnValue) &&\n columnValue.toLowerCase().indexOf(searchKey?.toLowerCase()) > -1\n ) {\n hit = true;\n break;\n }\n }\n if (!hit) {\n return false;\n }\n }\n for (const columnKey of filterColumnKeys) {\n let cellValue = row[columnKey];\n if (cellValue === null) {\n /**\n * 把null当作字符串来处理,从而支持搜索和筛选\n */\n cellValue = FILTER_NULL_KEY;\n } else if (typeof cellValue === 'string' && !cellValue.trim()) {\n cellValue = FILTER_EMPTY_KEY;\n }\n if(!rowFilters[columnKey]?.has(cellValue)) {\n isPass = false;\n break;\n }\n }\n return isPass;\n });\n }, [rowFilters, searchKey, rawColumns])\n\n const sortRows = useCallback(\n (sortedRawRows): R[] => {\n if (\n sortInfo.columnKey &&\n Array.isArray(sortedRawRows) &&\n sortInfo.direction !== \"NONE\"\n ) {\n const columnKey = sortInfo.columnKey;\n const column = rawColumns?.find((c) => c.key === columnKey);\n const columnType = column?.dataType ?? \"string\"; \n sortedRawRows = sortBy(sortedRawRows, (row) => {\n const value = row[columnKey];\n if (columnType === \"number\") {\n return toNumber(value);\n }\n return value;\n });\n if (sortInfo.direction === \"DESC\") {\n sortedRawRows = reverse(sortedRawRows);\n }\n }\n return sortedRawRows;\n },\n [sortInfo]\n );\n\n const rows = useMemo(() =>{\n return sortRows(filterRows(rawRows));\n }, [rawRows, searchKey, sortInfo, rowFilters])\n\n if (!enableVirtualization) {\n return {\n rowOverscanStartIdx: 0,\n rowOverscanEndIdx: rows.length - 1,\n rows,\n rowsCount,\n };\n }\n\n const overscanThreshold = 10;\n const rowVisibleStartIdx = Math.floor(scrollTop / rowHeight);\n const rowVisibleEndIdx = Math.min(rows.length - 1, Math.floor((scrollTop + clientHeight) / rowHeight));\n const rowOverscanStartIdx = Math.max(0, Math.floor((rowVisibleStartIdx - overscanThreshold) / RENDER_BACTCH_SIZE) * RENDER_BACTCH_SIZE);\n const rowOverscanEndIdx = Math.min(rows.length - 1, Math.ceil((rowVisibleEndIdx + overscanThreshold) / RENDER_BACTCH_SIZE) * RENDER_BACTCH_SIZE);\n\n return {\n rowOverscanStartIdx,\n rowOverscanEndIdx,\n rowVisibleStartIdx,\n rowVisibleEndIdx,\n rows,\n rowsCount,\n };\n}\n","import { useRef, useEffect, useCallback } from 'react';\n\n// https://reactjs.org/docs/hooks-faq.html#what-can-i-do-if-my-effect-dependencies-change-too-often\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function useLatestFunc<T extends (...args: any[]) => any>(fn: T) {\n const ref = useRef(fn);\n\n useEffect(() => {\n ref.current = fn;\n });\n\n return useCallback((...args: Parameters<T>) => {\n ref.current(...args);\n }, []);\n}\n","import { RefObject, useEffect, useState } from 'react';\n\nexport function useMouse<T extends HTMLElement>(ref: RefObject<T>, isMouseDown: boolean) {\n const [isMouseInElement, setIsMouseInElement] = useState(false)\n const [mouseLeaveEvent, setMouseLeaveEvent] = useState<MouseEvent | null>(null);\n useEffect(() => {\n function onMouseLeave(e: MouseEvent) {\n setIsMouseInElement(false)\n setMouseLeaveEvent(e)\n }\n function onMouseEnter(e: MouseEvent) {\n setIsMouseInElement(true)\n setMouseLeaveEvent(null)\n }\n if (ref.current && isMouseDown) {\n ref.current.addEventListener('mouseout', onMouseLeave)\n ref.current.addEventListener('mouseenter', onMouseEnter)\n }\n return () => {\n ref.current?.removeEventListener('mouseout', onMouseLeave)\n ref.current?.removeEventListener('mouseenter', onMouseEnter)\n }\n }, [ref.current, isMouseDown])\n return {\n isMouseInElement,\n mouseLeaveEvent,\n }\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport type { ReactElement } from 'react';\nimport React from 'react';\nimport { ConnectDropTarget, ConnectDragSource, ConnectDragPreview } from 'react-dnd';\n\nexport interface ISelectorEvent {\n rowIdx: number;\n columnIdx: number;\n isShiftClick?: boolean;\n isCtrlClick?: boolean;\n isUpdateIndex?: boolean;\n enableEditor?: boolean;\n mode?: RangeMode;\n}\n\nexport type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;\n\nexport interface Column<TRow = any, TSummaryRow = unknown> {\n /** The name of the column. By default it will be displayed in the header cell */\n name: string | ReactElement;\n /** A unique key to distinguish each column */\n key: string;\n /** Column width. If not specified, it will be determined automatically based on grid width and specified widths of other columns */\n width?: number | string;\n /** Minimum column width in px. */\n minWidth?: number;\n /** Maximum column width in px. */\n maxWidth?: number;\n dataType?: 'number' | 'string';\n cellClass?: string | ((row: TRow, isRowCellSelected?: boolean) => string | undefined);\n headerCellClass?: string;\n summaryCellClass?: string | ((row: TSummaryRow) => string);\n /** Formatter to be used to render the cell content */\n formatter?: React.ComponentType<FormatterProps<TRow, TSummaryRow>>;\n /** Formatter to be used to render the summary cell content */\n summaryFormatter?: React.ComponentType<SummaryFormatterProps<TSummaryRow, TRow>>;\n /** Formatter to be used to render the group cell content */\n groupFormatter?: React.ComponentType<GroupFormatterProps<TRow, TSummaryRow>>;\n /** Enables cell editing. If set and no editor property specified, then a textinput will be used as the cell editor */\n editable?: boolean | ((row: TRow) => boolean);\n /** Determines whether column is frozen or not */\n frozen?: boolean;\n /** Enable resizing of a column */\n resizable?: boolean;\n /** Enable sorting of a column */\n sortable?: boolean;\n /** Enable filter of a column */\n filterable?: boolean;\n clickable?: boolean;\n /** Sets the column sort order to be descending instead of ascending the first time the column is sorted */\n sortDescendingFirst?: boolean;\n /** Editor to be rendered when cell of column is being edited. If set, then the column is automatically set to be editable */\n editor?: React.ComponentType<EditorProps<TRow, TSummaryRow>>;\n editorOptions?: {\n /** @default false */\n createPortal?: boolean;\n /** @default false */\n editOnClick?: boolean;\n /** Prevent default to cancel editing */\n onCellKeyDown?: (event: React.KeyboardEvent<HTMLDivElement>) => void;\n /** Control the default cell navigation behavior while the editor is open */\n onNavigation?: (event: React.KeyboardEvent<HTMLDivElement>) => boolean;\n // TODO: Do we need these options\n // editOnDoubleClick?: boolean;\n /** @default false */\n // commitOnScroll?: boolean;\n };\n /** Header renderer for each header cell */\n headerRenderer?: React.ComponentType<HeaderRendererProps<TRow, TSummaryRow>>;\n /** Component to be used to filter the data of the column */\n filterRenderer?: React.ComponentType<FilterRendererProps<TRow, any, TSummaryRow>>;\n}\n\nexport interface CalculatedColumn<TRow, TSummaryRow = unknown> extends Column<TRow, TSummaryRow> {\n idx: number;\n resizable: boolean;\n sortable: boolean;\n frozen: boolean;\n isLastFrozenColumn: boolean;\n rowGroup: boolean;\n formatter: React.ComponentType<FormatterProps<TRow, TSummaryRow>>;\n}\n\nexport interface ColumnMetric {\n width: number;\n left: number;\n}\n\nexport interface Position {\n rowIdx: number;\n columnIdx: number;\n}\n\nexport interface IRange {\n rowIdx: number;\n columnIdx: number;\n endRowIdx: number;\n endColumnIdx: number;\n}\n\nexport enum RangeMode {\n SELECT = 'SELECT',\n EDIT = 'EDIT'\n}\n\nexport interface FormatterProps<TRow = any, TSummaryRow = any> {\n rowIdx: number;\n column: CalculatedColumn<TRow, TSummaryRow>;\n row: TRow;\n isCellSelected: boolean;\n isRowSelected: boolean;\n onRowSelectionChange: (checked: boolean, isShiftClick: boolean, isCtrlClick: boolean) => void;\n onRowChange: (row: Readonly<TRow>) => void;\n onRowReorder?: (sourceRowIndex: number, targetRowIndex: number) => void;\n enableRowRecord?: boolean;\n}\n\nexport interface SummaryFormatterProps<TSummaryRow, TRow = any> {\n column: CalculatedColumn<TRow, TSummaryRow>;\n row: TSummaryRow;\n}\n\nexport interface GroupFormatterProps<TRow, TSummaryRow = unknown> {\n groupKey: unknown;\n column: CalculatedColumn<TRow, TSummaryRow>;\n childRows: readonly TRow[];\n isExpanded: boolean;\n isCellSelected: boolean;\n isRowSelected: boolean;\n onRowSelectionChange: (checked: boolean) => void;\n toggleGroup: () => void;\n}\n\nexport interface SharedEditorProps<TRow> {\n row?: Readonly<TRow>;\n rowHeight: number;\n editorPortalTarget: Element;\n onRowChange: (row: TRow, commitChanges?: boolean) => void;\n onClose: (commitChanges?: boolean) => void;\n}\n\nexport interface EditorProps<TRow, TSummaryRow = unknown> extends SharedEditorProps<TRow> {\n rowIdx: number;\n column: Readonly<CalculatedColumn<TRow, TSummaryRow>>;\n top: number;\n left: number;\n width: number | undefined;\n}\n\ne