Add stickyHeader prop to table component
This commit is contained in:
parent
92d7560362
commit
9d18384b2d
2 changed files with 93 additions and 67 deletions
|
@ -1,42 +1,54 @@
|
||||||
import { useEffect, useRef } from 'react';
|
|
||||||
import { useInView } from 'framer-motion';
|
import { useInView } from 'framer-motion';
|
||||||
|
import { useEffect, useRef } from 'react';
|
||||||
import { useWindowSettings } from '/@/renderer/store/settings.store';
|
import { useWindowSettings } from '/@/renderer/store/settings.store';
|
||||||
import { Platform } from '/@/renderer/types';
|
import { Platform } from '/@/renderer/types';
|
||||||
|
|
||||||
export const useFixedTableHeader = () => {
|
export const useFixedTableHeader = ({ enabled }: { enabled: boolean }) => {
|
||||||
const intersectRef = useRef<HTMLDivElement | null>(null);
|
const tableHeaderRef = useRef<HTMLDivElement | null>(null);
|
||||||
const tableContainerRef = useRef<HTMLDivElement | null>(null);
|
const tableContainerRef = useRef<HTMLDivElement | null>(null);
|
||||||
const { windowBarStyle } = useWindowSettings();
|
const { windowBarStyle } = useWindowSettings();
|
||||||
|
|
||||||
const isNotPastTableIntersection = useInView(intersectRef, {
|
const topMargin =
|
||||||
margin:
|
windowBarStyle === Platform.WINDOWS || windowBarStyle === Platform.MACOS
|
||||||
windowBarStyle === Platform.WEB || windowBarStyle === Platform.LINUX
|
? '-128px'
|
||||||
? '-68px 0px 0px 0px'
|
: '-98px';
|
||||||
: '-98px 0px 0px 0px',
|
|
||||||
|
const isTableHeaderInView = useInView(tableHeaderRef, {
|
||||||
|
margin: `${topMargin} 0px 0px 0px`,
|
||||||
});
|
});
|
||||||
|
|
||||||
const tableInView = useInView(tableContainerRef, {
|
const isTableInView = useInView(tableContainerRef, {
|
||||||
margin: '-128px 0px 0px 0px',
|
margin: `${topMargin} 0px 0px 0px`,
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const header = document.querySelector('main .ag-header');
|
const header = document.querySelector('main .ag-header');
|
||||||
const root = document.querySelector('main .ag-root');
|
const root = document.querySelector('main .ag-root');
|
||||||
|
|
||||||
if (isNotPastTableIntersection || !tableInView) {
|
if (!isTableHeaderInView && isTableInView) {
|
||||||
if (windowBarStyle === Platform.WINDOWS || windowBarStyle === Platform.MACOS) {
|
|
||||||
header?.classList.remove('window-frame');
|
|
||||||
}
|
|
||||||
header?.classList.remove('ag-header-fixed');
|
|
||||||
root?.classList.remove('ag-header-fixed-margin');
|
|
||||||
} else {
|
|
||||||
if (windowBarStyle === Platform.WINDOWS || windowBarStyle === Platform.MACOS) {
|
if (windowBarStyle === Platform.WINDOWS || windowBarStyle === Platform.MACOS) {
|
||||||
header?.classList.add('window-frame');
|
header?.classList.add('window-frame');
|
||||||
}
|
}
|
||||||
header?.classList.add('ag-header-fixed');
|
header?.classList.add('ag-header-fixed');
|
||||||
root?.classList.add('ag-header-fixed-margin');
|
root?.classList.add('ag-header-fixed-margin');
|
||||||
|
} else if (!isTableInView) {
|
||||||
|
if (windowBarStyle === Platform.WINDOWS || windowBarStyle === Platform.MACOS) {
|
||||||
|
header?.classList.remove('window-frame');
|
||||||
|
}
|
||||||
|
header?.classList.remove('ag-header-fixed');
|
||||||
|
root?.classList.remove('ag-header-fixed-margin');
|
||||||
|
} else if (isTableHeaderInView) {
|
||||||
|
if (windowBarStyle === Platform.WINDOWS || windowBarStyle === Platform.MACOS) {
|
||||||
|
header?.classList.remove('window-frame');
|
||||||
|
}
|
||||||
|
header?.classList.remove('ag-header-fixed');
|
||||||
|
root?.classList.remove('ag-header-fixed-margin');
|
||||||
}
|
}
|
||||||
}, [isNotPastTableIntersection, tableInView, windowBarStyle]);
|
}, [enabled, isTableHeaderInView, isTableInView, windowBarStyle]);
|
||||||
|
|
||||||
return { intersectRef, tableContainerRef };
|
return { tableContainerRef, tableHeaderRef };
|
||||||
};
|
};
|
||||||
|
|
|
@ -35,6 +35,7 @@ import { RatingCell } from '/@/renderer/components/virtual-table/cells/rating-ce
|
||||||
import { TablePagination } from '/@/renderer/components/virtual-table/table-pagination';
|
import { TablePagination } from '/@/renderer/components/virtual-table/table-pagination';
|
||||||
import { ActionsCell } from '/@/renderer/components/virtual-table/cells/actions-cell';
|
import { ActionsCell } from '/@/renderer/components/virtual-table/cells/actions-cell';
|
||||||
import { TitleCell } from '/@/renderer/components/virtual-table/cells/title-cell';
|
import { TitleCell } from '/@/renderer/components/virtual-table/cells/title-cell';
|
||||||
|
import { useFixedTableHeader } from '/@/renderer/components/virtual-table/hooks/use-fixed-table-header';
|
||||||
|
|
||||||
export * from './table-config-dropdown';
|
export * from './table-config-dropdown';
|
||||||
export * from './table-pagination';
|
export * from './table-pagination';
|
||||||
|
@ -43,12 +44,18 @@ export * from './hooks/use-click-outside-deselect';
|
||||||
export * from './utils';
|
export * from './utils';
|
||||||
|
|
||||||
const TableWrapper = styled.div`
|
const TableWrapper = styled.div`
|
||||||
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const DummyHeader = styled.div<{ height?: number }>`
|
||||||
|
position: absolute;
|
||||||
|
height: ${({ height }) => height || 36}px};
|
||||||
|
`;
|
||||||
|
|
||||||
dayjs.extend(relativeTime);
|
dayjs.extend(relativeTime);
|
||||||
|
|
||||||
const tableColumns: { [key: string]: ColDef } = {
|
const tableColumns: { [key: string]: ColDef } = {
|
||||||
|
@ -371,6 +378,7 @@ export interface VirtualTableProps extends AgGridReactProps {
|
||||||
pagination: TablePaginationType;
|
pagination: TablePaginationType;
|
||||||
setPagination: any;
|
setPagination: any;
|
||||||
};
|
};
|
||||||
|
stickyHeader?: boolean;
|
||||||
transparentHeader?: boolean;
|
transparentHeader?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -380,6 +388,7 @@ export const VirtualTable = forwardRef(
|
||||||
autoFitColumns,
|
autoFitColumns,
|
||||||
deselectOnClickOutside,
|
deselectOnClickOutside,
|
||||||
autoHeight,
|
autoHeight,
|
||||||
|
stickyHeader,
|
||||||
transparentHeader,
|
transparentHeader,
|
||||||
onColumnMoved,
|
onColumnMoved,
|
||||||
onNewColumnsLoaded,
|
onNewColumnsLoaded,
|
||||||
|
@ -467,55 +476,60 @@ export const VirtualTable = forwardRef(
|
||||||
[autoFitColumns],
|
[autoFitColumns],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { tableHeaderRef, tableContainerRef } = useFixedTableHeader({
|
||||||
|
enabled: stickyHeader || false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const mergedWrapperRef = useMergedRef(deselectRef, tableContainerRef);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<TableWrapper
|
||||||
<TableWrapper
|
ref={mergedWrapperRef}
|
||||||
ref={deselectRef}
|
className={
|
||||||
className={
|
transparentHeader
|
||||||
transparentHeader
|
? 'ag-theme-alpine-dark ag-header-transparent'
|
||||||
? 'ag-header-transparent ag-theme-alpine-dark'
|
: 'ag-theme-alpine-dark'
|
||||||
: 'ag-theme-alpine-dark'
|
}
|
||||||
}
|
>
|
||||||
>
|
<DummyHeader ref={tableHeaderRef} />
|
||||||
<AgGridReact
|
<AgGridReact
|
||||||
ref={mergedRef}
|
ref={mergedRef}
|
||||||
animateRows
|
animateRows
|
||||||
maintainColumnOrder
|
maintainColumnOrder
|
||||||
suppressAsyncEvents
|
suppressAsyncEvents
|
||||||
suppressContextMenu
|
suppressContextMenu
|
||||||
suppressCopyRowsToClipboard
|
suppressCopyRowsToClipboard
|
||||||
suppressMoveWhenRowDragging
|
suppressMoveWhenRowDragging
|
||||||
suppressPaginationPanel
|
suppressPaginationPanel
|
||||||
suppressScrollOnNewData
|
suppressScrollOnNewData
|
||||||
blockLoadDebounceMillis={200}
|
blockLoadDebounceMillis={200}
|
||||||
cacheBlockSize={300}
|
cacheBlockSize={300}
|
||||||
cacheOverflowSize={1}
|
cacheOverflowSize={1}
|
||||||
defaultColDef={defaultColumnDefs}
|
defaultColDef={defaultColumnDefs}
|
||||||
enableCellChangeFlash={false}
|
enableCellChangeFlash={false}
|
||||||
headerHeight={36}
|
headerHeight={36}
|
||||||
rowBuffer={30}
|
rowBuffer={30}
|
||||||
rowSelection="multiple"
|
rowSelection="multiple"
|
||||||
{...rest}
|
{...rest}
|
||||||
onColumnMoved={handleColumnMoved}
|
onColumnMoved={handleColumnMoved}
|
||||||
onGridReady={handleGridReady}
|
onGridReady={handleGridReady}
|
||||||
onGridSizeChanged={handleGridSizeChanged}
|
onGridSizeChanged={handleGridSizeChanged}
|
||||||
onModelUpdated={handleModelUpdated}
|
onModelUpdated={handleModelUpdated}
|
||||||
onNewColumnsLoaded={handleNewColumnsLoaded}
|
onNewColumnsLoaded={handleNewColumnsLoaded}
|
||||||
/>
|
/>
|
||||||
{paginationProps && (
|
{paginationProps && (
|
||||||
<AnimatePresence
|
<AnimatePresence
|
||||||
presenceAffectsLayout
|
presenceAffectsLayout
|
||||||
initial={false}
|
initial={false}
|
||||||
mode="wait"
|
mode="wait"
|
||||||
>
|
>
|
||||||
<TablePagination
|
<TablePagination
|
||||||
{...paginationProps}
|
{...paginationProps}
|
||||||
tableRef={tableRef}
|
tableRef={tableRef}
|
||||||
/>
|
/>
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
)}
|
)}
|
||||||
</TableWrapper>
|
</TableWrapper>
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
Reference in a new issue