diff --git a/lib/bundle.cjs b/lib/bundle.cjs index 93c928a..7323cd0 100644 --- a/lib/bundle.cjs +++ b/lib/bundle.cjs @@ -126,8 +126,7 @@ function SelectCellFormatter({ disabled: disabled, checked: value, onChange: handleChange, - onClick: onClick, - onCopy: e => console.log(e) + onClick: onClick }), /*#__PURE__*/React.createElement("div", { className: "rdg-checkbox" })); @@ -640,13 +639,15 @@ function getNextSelectedCellPosition({ cellNavigationMode, columns, rowsCount, - nextPosition + nextPosition, + row }) { + const { + idx, + rowIdx + } = nextPosition; + if (cellNavigationMode !== 'NONE') { - const { - idx, - rowIdx - } = nextPosition; const columnsCount = columns.length; const isAfterLastColumn = idx === columnsCount; const isBeforeFirstColumn = idx === -1; @@ -686,6 +687,22 @@ function getNextSelectedCellPosition({ } } + const col = columns[idx]; + const nextCell = row[col.key]; + + if ((nextCell == null ? void 0 : nextCell.span) === 0) { + const newRow = Object.entries(row).find(r => { + var _r$; + + return ((_r$ = r[1]) == null ? void 0 : _r$.span) > 1; + }); + const newColIdx = columns.findIndex(c => newRow && c.key === newRow[0]); + return { + idx: newColIdx, + rowIdx + }; + } + return nextPosition; } function canExitGrid({ @@ -823,6 +840,7 @@ function Cell({ const error = typeof cell === 'object' && cell.error; const alert = typeof cell === 'object' && cell.alert; const warning = typeof cell === 'object' && cell.warning; + const span = typeof cell === 'object' && typeof cell.span === 'number' ? cell.span : 1; const { frozen } = column; @@ -831,7 +849,7 @@ function Cell({ const { cellClass } = column; - className = clsx('rdg-cell', typeof cellClass === 'function' ? cellClass(row) : cellClass, className, disabled ? 'rdg-cell-disabled' : isCopied && 'rdg-cell-copied', alert ? 'rdg-cell-alert' : checkIsDraggedOver(true) && 'rdg-cell-dragged-over', frozenRightAlign && 'rdg-cell-frozen-align-right' + (scrolledToEnd ? " rdg-cell-frozen-align-right-no-shadow" : ""), column.frozen && 'rdg-cell-frozen', column.isLastFrozenColumn && scrollLeft > 0 && 'rdg-cell-frozen-last', isCellSelected && 'rdg-cell-selected', error && 'rdg-cell-error', warning && 'rdg-cell-warning', hasChildren && 'rdg-cell-children', column.alignment === 'right' && 'rdg-cell-align-right'); + className = clsx('rdg-cell', typeof cellClass === 'function' ? cellClass(row) : cellClass, className, disabled ? 'rdg-cell-disabled' : isCopied && 'rdg-cell-copied', alert ? 'rdg-cell-alert' : checkIsDraggedOver(true) && 'rdg-cell-dragged-over', frozenRightAlign && 'rdg-cell-frozen-align-right' + (scrolledToEnd ? " rdg-cell-frozen-align-right-no-shadow" : ""), column.frozen && 'rdg-cell-frozen', column.isLastFrozenColumn && scrollLeft > 0 && 'rdg-cell-frozen-last', isCellSelected && 'rdg-cell-selected', error && 'rdg-cell-error', warning && 'rdg-cell-warning', hasChildren && 'rdg-cell-children', !span && 'rdg-cell-span-none', column.alignment === 'right' && 'rdg-cell-align-right'); const [showTooltip, setShowTooltip] = React.useState(false); const [reference, setReference] = React.useState(null); const [popper, setPopper] = React.useState(null); @@ -852,6 +870,10 @@ function Cell({ return false; } + if (span > 1) { + return false; + } + if (frozen != null ? frozen : !isDraggedOver) { return false; } @@ -908,7 +930,7 @@ function Cell({ } function handleDoubleClick() { - if (!disabled && !frozen && !frozenRightAlign) { + if (!disabled && !frozenRightAlign) { selectCellWrapper(true); } } @@ -990,14 +1012,17 @@ function Cell({ role: "gridcell", "aria-colindex": column.idx + 1, "aria-selected": isCellSelected, + "aria-colspan": span, ref: useCombinedRefs(cellRef, ref), className: className, style: column.frozenAlignment === 'right' ? { width: column.width, left: gridWidth - column.width } : { - width: column.width, - left: column.left + width: column.width * span, + padding: span ? '0 18px' : 0, + left: column.left, + textAlign: span > 1 ? 'center' : 'right' }, onMouseDown: handleMouseDown, onMouseEnter: handleMouseEnter, @@ -1016,7 +1041,7 @@ function Cell({ isRowSelected: isRowSelected, onRowSelectionChange: onRowSelectionChange, onRowChange: handleRowChange - }), dragHandleProps && !disabled && !frozenRightAlign && !frozen && /*#__PURE__*/React.createElement("div", { + }), dragHandleProps && !disabled && !frozenRightAlign && !frozen && span === 1 && /*#__PURE__*/React.createElement("div", { className: "rdg-cell-drag-handle", ...dragHandleProps })), (alert || warning) && showTooltip && /*#__PURE__*/reactDom.createPortal( /*#__PURE__*/React.createElement("div", { @@ -1061,9 +1086,11 @@ function EditCell({ row, rowIdx, editorProps, + cell, ...props }) { const [dimensions, setDimensions] = React.useState(null); + const span = typeof cell === 'object' && typeof cell.span === 'number' ? cell.span : 1; const cellRef = React.useCallback(node => { if (node !== null) { const { @@ -1110,7 +1137,7 @@ function EditCell({ ref: cellRef, className: className, style: { - width: column.width, + width: column.width * span, left: column.left }, ...props @@ -1210,6 +1237,7 @@ function Row({ rowIdx: rowIdx, column: column, row: row, + cell: cell, onKeyDown: selectedCellProps.onKeyDown, editorProps: selectedCellProps.editorProps }); @@ -2037,6 +2065,8 @@ function DataGrid({ } function navigate(event) { + var _nextPosition; + if (selectedPosition.mode === 'EDIT') { var _columns$selectedPosi2, _columns$selectedPosi3; @@ -2072,7 +2102,8 @@ function DataGrid({ columns, rowsCount: rows.length, cellNavigationMode: mode, - nextPosition + nextPosition, + row: rows[(_nextPosition = nextPosition) == null ? void 0 : _nextPosition.rowIdx] }); selectCell(nextPosition); } diff --git a/lib/bundle.cjs.map b/lib/bundle.cjs.map index 7a5c0e7..6fe88c8 100644 --- a/lib/bundle.cjs.map +++ b/lib/bundle.cjs.map @@ -1 +1 @@ -{"version":3,"file":"bundle.cjs","sources":["../src/hooks/useCombinedRefs.ts","../src/hooks/useClickOutside.ts","../src/hooks/useGridDimensions.ts","../src/hooks/useFocusRef.ts","../src/formatters/SelectCellFormatter.tsx","../src/formatters/ValueFormatter.tsx","../src/utils/domUtils.ts","../src/Columns.tsx","../src/hooks/useViewportColumns.ts","../src/hooks/useViewportRows.ts","../src/hooks/useLatestFunc.ts","../src/headerCells/SortableHeaderCell.tsx","../src/HeaderCell.tsx","../src/utils/columnUtils.ts","../src/utils/keyboardUtils.ts","../src/utils/selectedCellUtils.ts","../src/utils/index.ts","../src/HeaderRow.tsx","../src/Cell.tsx","../src/editors/EditorContainer.tsx","../src/EditCell.tsx","../src/Row.tsx","../src/DataGrid.tsx","../src/editors/TextEditor.tsx"],"sourcesContent":["import { useCallback } from 'react';\n\nexport function useCombinedRefs(...refs: readonly React.Ref[]) {\n return useCallback(\n (handle: T | null) => {\n for (const ref of refs) {\n if (typeof ref === 'function') {\n ref(handle);\n } else if (ref !== null) {\n // @ts-expect-error: https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31065\n ref.current = handle;\n }\n }\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps\n refs\n );\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 *
..
\n *
\n *
\n *
..
\n *
\n *
\n *\n * ComplexEditor for example Modals (using Portals)\n *
..
\n *
\n *
\n * // Nothing here\n *
\n *
\n *
\n *
..
\n *
\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();\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';\n\n// https://github.com/microsoft/TypeScript/issues/37861\ninterface ResizeObserverEntry {\n contentRect: {\n width: number;\n height: number;\n };\n}\n\ntype ResizeObserver = new (callback: (entries: readonly ResizeObserverEntry[]) => void) => {\n observe: (target: Element) => void;\n disconnect: () => void;\n};\n\nexport function useGridDimensions(): [React.RefObject, number, number] {\n const gridRef = useRef(null);\n const [gridWidth, setGridWidth] = useState(1);\n const [gridHeight, setGridHeight] = useState(1);\n\n useLayoutEffect(() => {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n const { ResizeObserver } = window as typeof window & { ResizeObserver: ResizeObserver };\n\n // don't break in jest/jsdom and browsers that don't support ResizeObserver\n if (ResizeObserver == null) return;\n\n const resizeObserver = new ResizeObserver(entries => {\n const { width, height } = entries[0].contentRect;\n setGridWidth(width);\n setGridHeight(height);\n });\n\n resizeObserver.observe(gridRef.current!);\n\n return () => {\n resizeObserver.disconnect();\n };\n }, []);\n\n return [gridRef, gridWidth, gridHeight];\n}\n","import { useRef, useLayoutEffect } from 'react';\n\nexport function useFocusRef(isCellSelected: boolean | undefined) {\n const ref = useRef(null);\n useLayoutEffect(() => {\n if (!isCellSelected) return;\n ref.current?.focus({ preventScroll: true });\n }, [isCellSelected]);\n\n return ref;\n}\n","import React from 'react';\nimport clsx from 'clsx';\n\nimport { useFocusRef } from '../hooks/useFocusRef';\n\ntype SharedInputProps = Pick,\n | 'disabled'\n | 'tabIndex'\n | 'onClick'\n | 'aria-label'\n | 'aria-labelledby'\n>;\n\ninterface SelectCellFormatterProps extends SharedInputProps {\n isCellSelected?: boolean;\n value: boolean;\n onChange: (value: boolean, isShiftClick: boolean) => void;\n}\n\nexport function SelectCellFormatter({\n value,\n tabIndex,\n isCellSelected,\n disabled,\n onClick,\n onChange,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy\n}: SelectCellFormatterProps) {\n const inputRef = useFocusRef(isCellSelected);\n\n function handleChange(e: React.ChangeEvent) {\n onChange(e.target.checked, (e.nativeEvent as MouseEvent).shiftKey);\n }\n\n return (\n