diff --git a/common-react/@dbeaver/react-data-grid/src/DataGrid.tsx b/common-react/@dbeaver/react-data-grid/src/DataGrid.tsx index 26f8eadaf6..03afecd9ff 100644 --- a/common-react/@dbeaver/react-data-grid/src/DataGrid.tsx +++ b/common-react/@dbeaver/react-data-grid/src/DataGrid.tsx @@ -70,6 +70,7 @@ export const DataGrid = forwardRef(function DataGrid onFocus, onCellChange, onColumnSort, + onHeaderKeyDown, children, className, onCellKeyDown, @@ -151,7 +152,7 @@ export const DataGrid = forwardRef(function DataGrid - + ; columnSortable?: IGridReactiveValue; onColumnSort?: (colIdx: number, order: 'asc' | 'desc' | null, isMultiple: boolean) => void; + onHeaderKeyDown?: (event: React.KeyboardEvent) => void; } export const DataGridCellHeaderContext = createContext(null); diff --git a/common-react/@dbeaver/react-data-grid/src/renderers/HeaderCellContentRenderer.tsx b/common-react/@dbeaver/react-data-grid/src/renderers/HeaderCellContentRenderer.tsx index 275d038cd8..0e18d26ae0 100644 --- a/common-react/@dbeaver/react-data-grid/src/renderers/HeaderCellContentRenderer.tsx +++ b/common-react/@dbeaver/react-data-grid/src/renderers/HeaderCellContentRenderer.tsx @@ -1,4 +1,4 @@ -import { memo, use, useRef } from 'react'; +import { memo, use } from 'react'; import { useDrag, useDrop, type DnDStoreProvider } from '@dbeaver/react-dnd'; import { DataGridCellHeaderContext } from '../DataGridHeaderCellContext.js'; import { useGridReactiveValue } from '../useGridReactiveValue.js'; @@ -9,6 +9,7 @@ interface Props { colIdx: number; tabIndex?: number; } + export const HeaderCellContentRenderer = memo(function HeaderCellContentRenderer({ colIdx, tabIndex }: Props) { const dndHeaderContext = use(HeaderDnDContext); const cellHeaderContext = use(DataGridCellHeaderContext); @@ -16,11 +17,9 @@ export const HeaderCellContentRenderer = memo(function HeaderCellContentRenderer const getHeaderText = useGridReactiveValue(headerElement ? undefined : cellHeaderContext?.headerText, colIdx); const isColumnSortable = useGridReactiveValue(cellHeaderContext?.columnSortable, colIdx); const onColumnSort = cellHeaderContext?.onColumnSort; + const onHeaderKeyDown = cellHeaderContext?.onHeaderKeyDown; const sortingState = useGridReactiveValue(cellHeaderContext?.columnSortingState, colIdx); - const orderButtonRef = useRef(null); - const containerRef = useRef(null); - const draggable = dndHeaderContext?.getCanDrag?.(colIdx) ?? false; const drag = useDrag({ draggable, @@ -55,26 +54,32 @@ export const HeaderCellContentRenderer = memo(function HeaderCellContentRenderer }, }); - const handleKeyDown = (e: React.KeyboardEvent) => { - if (e.key === 'Tab' && !e.shiftKey && isColumnSortable && onColumnSort && orderButtonRef.current !== document.activeElement) { - e.preventDefault(); - e.stopPropagation(); - containerRef.current?.parentElement?.setAttribute('aria-selected', 'false'); - orderButtonRef.current?.focus(); + function handleSort(e: React.MouseEvent | React.KeyboardEvent) { + if (!onColumnSort) return; + + const nextSortState = sortingState === 'asc' ? 'desc' : sortingState === 'desc' ? null : 'asc'; + onColumnSort(colIdx, nextSortState, e.ctrlKey || e.metaKey); + } + + function onKeyDown(event: React.KeyboardEvent) { + onHeaderKeyDown?.(event); + + if ((event.key === 'Enter' || event.key === ' ') && isColumnSortable && onColumnSort) { + event.preventDefault(); + handleSort(event); } - }; + } return (
{headerElement ?? getHeaderText ?? ''} - {isColumnSortable && onColumnSort && } + {isColumnSortable && onColumnSort && }
); }); diff --git a/common-react/@dbeaver/react-data-grid/src/renderers/OrderButton.tsx b/common-react/@dbeaver/react-data-grid/src/renderers/OrderButton.tsx index 272866d11e..65bac1c825 100644 --- a/common-react/@dbeaver/react-data-grid/src/renderers/OrderButton.tsx +++ b/common-react/@dbeaver/react-data-grid/src/renderers/OrderButton.tsx @@ -1,19 +1,12 @@ import { clsx, IconButton, Icon } from '@dbeaver/ui-kit'; interface OrderButtonProps { - colIdx: number; sortState?: 'asc' | 'desc' | null; - onSort: (attributePosition: number, order: 'asc' | 'desc' | null, isMultiple: boolean) => void; + onClick: (e: React.MouseEvent) => void; tabIndex?: number; - ref: React.Ref; } -export function OrderButton({ colIdx, sortState, onSort, tabIndex, ref }: OrderButtonProps) { - function handleClick(e: React.MouseEvent) { - const nextSortState = sortState === 'asc' ? 'desc' : sortState === 'desc' ? null : 'asc'; - onSort(colIdx, nextSortState, e.ctrlKey || e.metaKey); - } - +export function OrderButton({ sortState, onClick, tabIndex }: OrderButtonProps) { const svgSortAsc = 'sort-asc'; const svgSortDesc = 'sort-desc'; const svgSortUnknown = 'sort-unknown'; @@ -24,9 +17,8 @@ export function OrderButton({ colIdx, sortState, onSort, tabIndex, ref }: OrderB (function DataGridT const tableData = useTableData(model as unknown as IDatabaseDataModel, resultIndex, dataGridDivRef); const gridSelectionContext = useGridSelectionContext(tableData, selectionAction); - function restoreFocus() { - const gridDiv = gridContainerRef.current; - const focusSink = gridDiv?.querySelector('[aria-selected="true"]'); - focusSink?.focus(); - } + const restoreFocus = useCallback( + function restoreFocus() { + const gridDiv = gridContainerRef.current; + const focusSink = gridDiv?.querySelector('[aria-selected="true"]'); + focusSink?.focus(); + }, + [gridContainerRef], + ); function isGridInFocus(): boolean { const gridDiv = gridContainerRef.current; @@ -544,6 +547,7 @@ export const DataGridTable = observer(function DataGridT onColumnSort={handleSort} onCellChange={handleCellChange} onCellKeyDown={handleCellKeyDown} + onHeaderKeyDown={gridSelectedCellCopy.onKeydownHandler} /> diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/TableColumnHeader/TableIndexColumnHeader.module.css b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/TableColumnHeader/TableIndexColumnHeader.module.css index 156d4c9d66..c60f08c603 100644 --- a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/TableColumnHeader/TableIndexColumnHeader.module.css +++ b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/TableColumnHeader/TableIndexColumnHeader.module.css @@ -6,9 +6,16 @@ * you may not use this file except in compliance with the License. */ -.container { +.clickAreaOverlay { cursor: pointer; + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; } + .iconOrImage { cursor: auto; width: 10px; diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/TableColumnHeader/TableIndexColumnHeader.tsx b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/TableColumnHeader/TableIndexColumnHeader.tsx index 3fcc2aebab..60574fcb8f 100644 --- a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/TableColumnHeader/TableIndexColumnHeader.tsx +++ b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/TableColumnHeader/TableIndexColumnHeader.tsx @@ -36,10 +36,16 @@ export const TableIndexColumnHeader = observer(function TableIndexColumnHeader() } return ( -
+ <> {readonly && ( )} -
+
+ ); });