Skip to content

Commit

Permalink
🐛 Add resizing context in order to account for different column orders
Browse files Browse the repository at this point in the history
  • Loading branch information
Steffen Slavetinsky committed Sep 18, 2023
1 parent 3620f0f commit 9b248c0
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 118 deletions.
20 changes: 9 additions & 11 deletions src/widgets/DataGrid/Cell/HeaderCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ import Tooltip from '../../../components/ui/Tooltip';
import dataformat from '../../../dataformat';
import { useColorTransferFunction } from '../../../hooks/useColorTransferFunction';
import * as React from 'react';
import { FunctionComponent, useCallback, useMemo } from 'react';
import { FunctionComponent, useCallback, useContext, useMemo } from 'react';
import { HiChevronDown, HiChevronUp } from 'react-icons/hi';
import type { GridChildComponentProps as CellProps } from 'react-window';
import { Dataset, Sorting, useDataset } from '../../../stores/dataset';
import tw from 'twin.macro';
import { useColumn } from '../context/columnContext';
import { useSortByColumn } from '../context/sortingContext';
import RelevanceIndicator from '../RelevanceIndicator';
import { ResizingContext } from '../context/resizeContext';

interface SortingIndicatorProps {
sorting?: Sorting;
Expand Down Expand Up @@ -39,17 +40,14 @@ const stopPropagation: React.MouseEventHandler<HTMLButtonElement> = (event) => {
event.stopPropagation();
};

type ItemData = {
onStartResize: (columnIndex: number) => void;
resizedIndex?: number;
};

type Props = CellProps<ItemData>;
type Props = CellProps;

const HeaderCell: FunctionComponent<Props> = ({ data, style, columnIndex }) => {
const HeaderCell: FunctionComponent<Props> = ({ style, columnIndex }) => {
const column = useColumn(columnIndex);
const [columnSorting, sortBy, resetSorting] = useSortByColumn(column.key);

const { startResizing, resizedIndex } = useContext(ResizingContext);

const tags = useDataset(tagsSelector);
const tagColorTransferFunction = useColorTransferFunction(tags, {
kind: 'str',
Expand Down Expand Up @@ -130,9 +128,9 @@ const HeaderCell: FunctionComponent<Props> = ({ data, style, columnIndex }) => {
const onStartResize: React.MouseEventHandler<HTMLButtonElement> = useCallback(
(event) => {
stopPropagation(event);
data.onStartResize(columnIndex);
startResizing(columnIndex);
},
[columnIndex, data]
[columnIndex, startResizing]
);

return (
Expand Down Expand Up @@ -163,7 +161,7 @@ const HeaderCell: FunctionComponent<Props> = ({ data, style, columnIndex }) => {
onMouseDown={onStartResize}
css={[
tw`h-full w-[3px] border-r bg-none hover:border-r-0 hover:bg-gray-400 transition transform cursor-col-resize`,
data.resizedIndex === columnIndex && tw`bg-gray-400 border-r-0`,
resizedIndex.current === columnIndex && tw`bg-gray-400 border-r-0`,
]}
/>
</div>
Expand Down
123 changes: 29 additions & 94 deletions src/widgets/DataGrid/DataGrid.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'twin.macro';
import TableIcon from '../../icons/Table';
import { Widget } from '../types';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useCallback, useMemo, useRef } from 'react';
import AutoSizer from 'react-virtualized-auto-sizer';
import { VariableSizeGrid as Grid } from 'react-window';
import { Dataset, Sorting, useDataset } from '../../stores/dataset';
Expand All @@ -16,10 +16,8 @@ import HeaderGrid from './HeaderGrid';
import MenuBar from './MenuBar';
import TableGrid, { Ref as TableGridRef } from './TableGrid';
import GridContextMenu from './GridContextMenu';
import columnWidthByType from './columnWidthByType';
import { WidgetContainer, WidgetContent } from '../../lib';

const MIN_COLUMN_WIDTH = 50;
import { ColumnResizeProvider } from './context/resizeContext';

const headerHeight = 24;

Expand Down Expand Up @@ -50,73 +48,12 @@ const DataGrid: Widget = () => {
'visibleColumns',
defaultVisibleColumnKeys
);
const [columnWidths, persistColumnWidths] = useWidgetConfig<Record<string, number>>(
'columnWidths',
allColumns.reduce((acc: Record<string, number>, column: DataColumn) => {
acc[column.key] = columnWidthByType[column.type.kind];
return acc;
}, {} as Record<string, number>)
);

const columnWidthsRef = useRef(columnWidths);

useEffect(() => {
columnWidthsRef.current = columnWidths;
}, [columnWidths]);

const resetGridsAfterIndex = useCallback((index: number) => {
tableGrid.current?.resetAfterColumnIndex(index);
headerGrid.current?.resetAfterColumnIndex(index);
}, []);

const [resizedIndex, setResizedIndex] = useState<number>();
const lastResizePosition = useRef<number | null>(null);

const onStartResize = useCallback(
(columnIndex: number) => {
const columnKey = visibleColumns[columnIndex];
const onMouseMoveWhileResize = (event: MouseEvent) => {
if (lastResizePosition.current !== null) {
const delta = event.clientX - lastResizePosition.current;
if (delta > 0 || delta < 0) {
const oldWidths = columnWidthsRef.current;
const newColumnWidth = Math.max(
MIN_COLUMN_WIDTH,
oldWidths[columnKey] + delta
);
const newWidths = {
...oldWidths,
[columnKey]: newColumnWidth,
};
columnWidthsRef.current = newWidths;
resetGridsAfterIndex(columnIndex);
}
} else {
setResizedIndex(columnIndex);
}
lastResizePosition.current = event.clientX;
};

window.addEventListener('mousemove', onMouseMoveWhileResize);

const onMouseUpWhileResize = () => {
window.removeEventListener('mousemove', onMouseMoveWhileResize);
window.removeEventListener('mouseup', onMouseUpWhileResize);
lastResizePosition.current = null;
setResizedIndex(undefined);
persistColumnWidths(columnWidthsRef.current);
};

window.addEventListener('mouseup', onMouseUpWhileResize);
},
[visibleColumns, resetGridsAfterIndex, persistColumnWidths]
);

const columnWidth = useCallback(
(index: number) => columnWidthsRef.current[visibleColumns[index]],
[visibleColumns]
);

const resetVisibleColumns = useCallback(
() => setVisibleColumns(defaultVisibleColumnKeys),
[defaultVisibleColumnKeys, setVisibleColumns]
Expand Down Expand Up @@ -145,36 +82,34 @@ const DataGrid: Widget = () => {
setAreOrderedByRelevance={setAreOrderedByRelevance}
resetColumns={resetVisibleColumns}
>
<SortingProvider sorting={sorting} setSorting={setSorting}>
<MenuBar />
<WidgetContent>
<AutoSizer>
{({ width, height }) => (
<div tw="bg-white" style={{ width, height }}>
<GridContextMenu>
<div tw="bg-gray-100 w-full">
<HeaderGrid
width={width - scrollbarWidth}
height={headerHeight}
ref={headerGrid}
columnWidth={columnWidth}
onStartResize={onStartResize}
resizedIndex={resizedIndex}
<ColumnResizeProvider onResize={resetGridsAfterIndex}>
<SortingProvider sorting={sorting} setSorting={setSorting}>
<MenuBar />
<WidgetContent>
<AutoSizer>
{({ width, height }) => (
<div tw="bg-white" style={{ width, height }}>
<GridContextMenu>
<div tw="bg-gray-100 w-full">
<HeaderGrid
width={width - scrollbarWidth}
height={headerHeight}
ref={headerGrid}
/>
</div>
<TableGrid
width={width}
height={height - headerHeight}
onScroll={handleScroll}
ref={tableGrid}
/>
</div>
<TableGrid
width={width}
height={height - headerHeight}
onScroll={handleScroll}
columnWidth={columnWidth}
ref={tableGrid}
/>
</GridContextMenu>
</div>
)}
</AutoSizer>
</WidgetContent>
</SortingProvider>
</GridContextMenu>
</div>
)}
</AutoSizer>
</WidgetContent>
</SortingProvider>
</ColumnResizeProvider>
</ColumnProvider>
</TableViewProvider>
</WidgetContainer>
Expand Down
13 changes: 6 additions & 7 deletions src/widgets/DataGrid/HeaderGrid.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
import usePrevious from '../../hooks/usePrevious';
import * as React from 'react';
import { useCallback, useEffect } from 'react';
import { useCallback, useContext, useEffect } from 'react';
import { VariableSizeGrid as Grid } from 'react-window';
import 'twin.macro';
import HeaderCell from './Cell/HeaderCell';
import { useColumnCount, useVisibleColumns } from './context/columnContext';
import { ResizingContext } from './context/resizeContext';

interface Props {
height: number;
width: number;
columnWidth: (index: number) => number;
onStartResize: (columnIndex: number) => void;
resizedIndex?: number;
}

const HeaderGrid: React.ForwardRefRenderFunction<Grid, Props> = (
{ height, width, columnWidth, onStartResize, resizedIndex },
{ height, width },
ref
) => {
const rowHeight = useCallback(() => height, [height]);
const columnCount = useColumnCount();

const { getColumnWidth } = useContext(ResizingContext);

const [displayedColumns] = useVisibleColumns();

const previousColumnKeys = usePrevious(displayedColumns.map(({ key }) => key));
Expand All @@ -39,12 +39,11 @@ const HeaderGrid: React.ForwardRefRenderFunction<Grid, Props> = (
return (
<Grid
columnCount={columnCount}
columnWidth={columnWidth}
columnWidth={getColumnWidth}
rowCount={1}
rowHeight={rowHeight}
height={height}
width={width}
itemData={{ onStartResize, resizedIndex }}
style={{
overflow: 'hidden',
}}
Expand Down
15 changes: 9 additions & 6 deletions src/widgets/DataGrid/TableGrid.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import usePrevious from '../../hooks/usePrevious';
import {
ForwardRefRenderFunction,
forwardRef,
ForwardRefRenderFunction,
useCallback,
useContext,
useEffect,
useRef,
useImperativeHandle,
useRef,
} from 'react';
import { VariableSizeGrid as Grid } from 'react-window';
import type { GridOnScrollProps, GridProps } from 'react-window';
import { VariableSizeGrid as Grid } from 'react-window';
import Cell from './Cell';
import CellPlaceholder from './Cell/CellPlaceholder';
import { useColumnCount, useVisibleColumns } from './context/columnContext';
Expand All @@ -18,20 +19,20 @@ import useRowCount from './hooks/useRowCount';
import useSort from './hooks/useSort';
import KeyboardControls from './KeyboardControls';
import MouseControls from './MouseControls';
import { ResizingContext } from './context/resizeContext';

interface Props {
width: number;
height: number;
onScroll: GridProps['onScroll'];
columnWidth: (index: number) => number;
}

export type Ref = {
resetAfterColumnIndex: (index: number) => void;
};

const TableGrid: ForwardRefRenderFunction<Ref, Props> = (
{ width, height, onScroll, columnWidth },
{ width, height, onScroll },
fwdRef
) => {
const ref = useRef<Grid>(null);
Expand All @@ -46,6 +47,8 @@ const TableGrid: ForwardRefRenderFunction<Ref, Props> = (

const [displayedColumns] = useVisibleColumns();

const { getColumnWidth } = useContext(ResizingContext);

const { getOriginalIndex } = useSort();
const rowCount = useRowCount();

Expand Down Expand Up @@ -96,7 +99,7 @@ const TableGrid: ForwardRefRenderFunction<Ref, Props> = (
height={height}
columnCount={columnCount}
rowCount={Math.max(1, rowCount)}
columnWidth={columnWidth}
columnWidth={getColumnWidth}
estimatedColumnWidth={100}
rowHeight={getRowHeight}
itemKey={rowCount ? itemKey : undefined}
Expand Down
Loading

0 comments on commit 9b248c0

Please sign in to comment.