mirror of
https://github.com/jeffvli/feishin.git
synced 2026-07-16 07:30:03 +02:00
fix item table scroll sync when pinned columns change
This commit is contained in:
@@ -7,6 +7,26 @@ import { useEffect } from 'react';
|
|||||||
|
|
||||||
import { ItemListStateActions } from '/@/renderer/components/item-list/helpers/item-list-state';
|
import { ItemListStateActions } from '/@/renderer/components/item-list/helpers/item-list-state';
|
||||||
|
|
||||||
|
const getPaneScrollViewport = (ref: React.RefObject<HTMLDivElement | null>) =>
|
||||||
|
ref.current?.firstElementChild as HTMLDivElement | undefined;
|
||||||
|
|
||||||
|
const getPaneElements = ({
|
||||||
|
pinnedLeftColumnRef,
|
||||||
|
pinnedRightColumnRef,
|
||||||
|
pinnedRowRef,
|
||||||
|
rowRef,
|
||||||
|
}: {
|
||||||
|
pinnedLeftColumnRef: React.RefObject<HTMLDivElement | null>;
|
||||||
|
pinnedRightColumnRef: React.RefObject<HTMLDivElement | null>;
|
||||||
|
pinnedRowRef: React.RefObject<HTMLDivElement | null>;
|
||||||
|
rowRef: React.RefObject<HTMLDivElement | null>;
|
||||||
|
}) => ({
|
||||||
|
header: getPaneScrollViewport(pinnedRowRef),
|
||||||
|
pinnedLeft: getPaneScrollViewport(pinnedLeftColumnRef),
|
||||||
|
pinnedRight: getPaneScrollViewport(pinnedRightColumnRef),
|
||||||
|
row: getPaneScrollViewport(rowRef),
|
||||||
|
});
|
||||||
|
|
||||||
export const useTablePaneSync = ({
|
export const useTablePaneSync = ({
|
||||||
enableDrag,
|
enableDrag,
|
||||||
enableDragScroll,
|
enableDragScroll,
|
||||||
@@ -21,6 +41,7 @@ export const useTablePaneSync = ({
|
|||||||
rowRef,
|
rowRef,
|
||||||
scrollContainerRef,
|
scrollContainerRef,
|
||||||
scrollShadowStore,
|
scrollShadowStore,
|
||||||
|
scrollSyncKey,
|
||||||
}: {
|
}: {
|
||||||
enableDrag: boolean | undefined;
|
enableDrag: boolean | undefined;
|
||||||
enableDragScroll: boolean | undefined;
|
enableDragScroll: boolean | undefined;
|
||||||
@@ -37,6 +58,7 @@ export const useTablePaneSync = ({
|
|||||||
rowRef: React.RefObject<HTMLDivElement | null>;
|
rowRef: React.RefObject<HTMLDivElement | null>;
|
||||||
scrollContainerRef: React.RefObject<HTMLDivElement | null>;
|
scrollContainerRef: React.RefObject<HTMLDivElement | null>;
|
||||||
scrollShadowStore: TableScrollShadowStore;
|
scrollShadowStore: TableScrollShadowStore;
|
||||||
|
scrollSyncKey: string;
|
||||||
}) => {
|
}) => {
|
||||||
// Main grid overlayscrollbars - only handle X-axis if right-pinned columns exist
|
// Main grid overlayscrollbars - only handle X-axis if right-pinned columns exist
|
||||||
const [initialize, osInstance] = useOverlayScrollbars({
|
const [initialize, osInstance] = useOverlayScrollbars({
|
||||||
@@ -144,6 +166,7 @@ export const useTablePaneSync = ({
|
|||||||
osInstance,
|
osInstance,
|
||||||
pinnedRightColumnCount,
|
pinnedRightColumnCount,
|
||||||
scrollContainerRef,
|
scrollContainerRef,
|
||||||
|
scrollSyncKey,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -235,222 +258,294 @@ export const useTablePaneSync = ({
|
|||||||
osInstanceRightPinned,
|
osInstanceRightPinned,
|
||||||
pinnedRightColumnCount,
|
pinnedRightColumnCount,
|
||||||
pinnedRightColumnRef,
|
pinnedRightColumnRef,
|
||||||
|
scrollSyncKey,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const header = pinnedRowRef.current?.childNodes[0] as HTMLDivElement;
|
let disposed = false;
|
||||||
const row = rowRef.current?.childNodes[0] as HTMLDivElement;
|
let cleanup: (() => void) | undefined;
|
||||||
const pinnedLeft = pinnedLeftColumnRef.current?.childNodes[0] as HTMLDivElement;
|
let setupFrameId = 0;
|
||||||
const pinnedRight = pinnedRightColumnRef.current?.childNodes[0] as HTMLDivElement;
|
|
||||||
|
|
||||||
if (!row) return;
|
const resolvePaneRefs = () =>
|
||||||
|
getPaneElements({
|
||||||
|
pinnedLeftColumnRef,
|
||||||
|
pinnedRightColumnRef,
|
||||||
|
pinnedRowRef,
|
||||||
|
rowRef,
|
||||||
|
});
|
||||||
|
|
||||||
// Ensure all containers have the same height
|
const isVerticalScrollHostReady = (
|
||||||
const syncHeights = () => {
|
row: HTMLDivElement,
|
||||||
const rowHeight = row.scrollHeight;
|
pinnedRight: HTMLDivElement | undefined,
|
||||||
let targetHeight = rowHeight;
|
) => {
|
||||||
|
const verticalScrollHost = pinnedRightColumnCount > 0 ? pinnedRight : row;
|
||||||
|
|
||||||
if (pinnedLeft) {
|
if (!verticalScrollHost) {
|
||||||
const pinnedLeftHeight = pinnedLeft.scrollHeight;
|
return false;
|
||||||
targetHeight = Math.max(targetHeight, pinnedLeftHeight);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pinnedRight) {
|
if (pinnedRightColumnCount > 0) {
|
||||||
const pinnedRightHeight = pinnedRight.scrollHeight;
|
return verticalScrollHost.scrollHeight > verticalScrollHost.clientHeight;
|
||||||
targetHeight = Math.max(targetHeight, pinnedRightHeight);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pinnedLeft && pinnedLeft.style.height !== `${targetHeight}px`) {
|
return verticalScrollHost.scrollHeight > 0;
|
||||||
pinnedLeft.style.height = `${targetHeight}px`;
|
|
||||||
}
|
|
||||||
if (pinnedRight && pinnedRight.style.height !== `${targetHeight}px`) {
|
|
||||||
pinnedRight.style.height = `${targetHeight}px`;
|
|
||||||
}
|
|
||||||
if (row.style.height !== `${targetHeight}px`) {
|
|
||||||
row.style.height = `${targetHeight}px`;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const timeoutId = setTimeout(syncHeights, 0);
|
const setupScrollSync = () => {
|
||||||
|
if (disposed) {
|
||||||
const activeElement = { element: null } as { element: HTMLDivElement | null };
|
return;
|
||||||
const scrollingElements = new Set<HTMLDivElement>();
|
|
||||||
const scrollTimeouts = new Map<HTMLDivElement, NodeJS.Timeout>();
|
|
||||||
|
|
||||||
const setActiveElement = (e: HTMLElementEventMap['pointermove']) => {
|
|
||||||
activeElement.element = e.currentTarget as HTMLDivElement;
|
|
||||||
};
|
|
||||||
const setActiveElementFromWheel = (e: HTMLElementEventMap['wheel']) => {
|
|
||||||
activeElement.element = e.currentTarget as HTMLDivElement;
|
|
||||||
};
|
|
||||||
|
|
||||||
const markElementAsScrolling = (element: HTMLDivElement) => {
|
|
||||||
scrollingElements.add(element);
|
|
||||||
|
|
||||||
const existingTimeout = scrollTimeouts.get(element);
|
|
||||||
if (existingTimeout) {
|
|
||||||
clearTimeout(existingTimeout);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const timeout = setTimeout(() => {
|
cleanup?.();
|
||||||
scrollingElements.delete(element);
|
|
||||||
|
|
||||||
const hasRightPinnedColumns = pinnedRightColumnCount > 0;
|
const { header, pinnedLeft, pinnedRight, row } = resolvePaneRefs();
|
||||||
const scrollElement = hasRightPinnedColumns && pinnedRight ? pinnedRight : row;
|
|
||||||
|
|
||||||
if (scrollElement && onScrollEndRef.current) {
|
if (!row || !isVerticalScrollHostReady(row, pinnedRight)) {
|
||||||
onScrollEndRef.current(
|
setupFrameId = requestAnimationFrame(setupScrollSync);
|
||||||
scrollElement.scrollTop,
|
return;
|
||||||
(handleRef.current?.internalState ??
|
}
|
||||||
(undefined as any)) as ItemListStateActions,
|
|
||||||
);
|
const syncHeights = () => {
|
||||||
|
const panes = resolvePaneRefs();
|
||||||
|
const syncRow = panes.row;
|
||||||
|
const syncPinnedLeft = panes.pinnedLeft;
|
||||||
|
const syncPinnedRight = panes.pinnedRight;
|
||||||
|
|
||||||
|
if (!syncRow) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
scrollTimeouts.delete(element);
|
const rowHeight = syncRow.scrollHeight;
|
||||||
}, 150);
|
let targetHeight = rowHeight;
|
||||||
|
|
||||||
scrollTimeouts.set(element, timeout);
|
if (syncPinnedLeft) {
|
||||||
};
|
targetHeight = Math.max(targetHeight, syncPinnedLeft.scrollHeight);
|
||||||
|
}
|
||||||
|
|
||||||
const syncScroll = (e: HTMLElementEventMap['scroll']) => {
|
if (syncPinnedRight) {
|
||||||
const currentElement = e.currentTarget as HTMLDivElement;
|
targetHeight = Math.max(targetHeight, syncPinnedRight.scrollHeight);
|
||||||
markElementAsScrolling(currentElement);
|
}
|
||||||
|
|
||||||
const shouldSync =
|
if (targetHeight <= 0) {
|
||||||
currentElement === activeElement.element || scrollingElements.has(currentElement);
|
return;
|
||||||
if (!shouldSync) return;
|
}
|
||||||
|
|
||||||
const scrollTop = (e.currentTarget as HTMLDivElement).scrollTop;
|
if (syncPinnedLeft && syncPinnedLeft.style.height !== `${targetHeight}px`) {
|
||||||
const scrollLeft = (e.currentTarget as HTMLDivElement).scrollLeft;
|
syncPinnedLeft.style.height = `${targetHeight}px`;
|
||||||
|
}
|
||||||
const isScrolling = {
|
if (syncPinnedRight && syncPinnedRight.style.height !== `${targetHeight}px`) {
|
||||||
header: false,
|
syncPinnedRight.style.height = `${targetHeight}px`;
|
||||||
pinnedLeft: false,
|
}
|
||||||
pinnedRight: false,
|
if (rowHeight > 0 && syncRow.style.height !== `${targetHeight}px`) {
|
||||||
row: false,
|
syncRow.style.height = `${targetHeight}px`;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const hasRightPinnedColumns = pinnedRightColumnCount > 0;
|
const timeoutId = setTimeout(syncHeights, 0);
|
||||||
|
|
||||||
if (header && e.currentTarget === header && !isScrolling.row) {
|
const activeElement = { element: null } as { element: HTMLDivElement | null };
|
||||||
isScrolling.row = true;
|
const scrollingElements = new Set<HTMLDivElement>();
|
||||||
row.scrollTo({ behavior: 'instant', left: scrollLeft });
|
const scrollTimeouts = new Map<HTMLDivElement, NodeJS.Timeout>();
|
||||||
isScrolling.row = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
const setActiveElement = (e: HTMLElementEventMap['pointermove']) => {
|
||||||
e.currentTarget === row &&
|
activeElement.element = e.currentTarget as HTMLDivElement;
|
||||||
!isScrolling.header &&
|
};
|
||||||
!isScrolling.pinnedLeft &&
|
const setActiveElementFromWheel = (e: HTMLElementEventMap['wheel']) => {
|
||||||
!isScrolling.pinnedRight
|
activeElement.element = e.currentTarget as HTMLDivElement;
|
||||||
) {
|
};
|
||||||
if (header) {
|
|
||||||
isScrolling.header = true;
|
const markElementAsScrolling = (element: HTMLDivElement) => {
|
||||||
header.scrollTo({ behavior: 'instant', left: scrollLeft });
|
scrollingElements.add(element);
|
||||||
|
|
||||||
|
const existingTimeout = scrollTimeouts.get(element);
|
||||||
|
if (existingTimeout) {
|
||||||
|
clearTimeout(existingTimeout);
|
||||||
}
|
}
|
||||||
if (hasRightPinnedColumns && pinnedRight) {
|
|
||||||
isScrolling.pinnedRight = true;
|
|
||||||
pinnedRight.scrollTo({ behavior: 'instant', top: scrollTop });
|
|
||||||
isScrolling.pinnedRight = false;
|
|
||||||
} else {
|
|
||||||
if (pinnedLeft) {
|
|
||||||
isScrolling.pinnedLeft = true;
|
|
||||||
pinnedLeft.scrollTo({ behavior: 'instant', top: scrollTop });
|
|
||||||
}
|
|
||||||
if (pinnedRight) {
|
|
||||||
isScrolling.pinnedRight = true;
|
|
||||||
pinnedRight.scrollTo({ behavior: 'instant', top: scrollTop });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
isScrolling.header = false;
|
|
||||||
isScrolling.pinnedLeft = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pinnedLeft && e.currentTarget === pinnedLeft && !isScrolling.row) {
|
const timeout = setTimeout(() => {
|
||||||
if (hasRightPinnedColumns && pinnedRight) {
|
scrollingElements.delete(element);
|
||||||
isScrolling.pinnedRight = true;
|
|
||||||
pinnedRight.scrollTo({ behavior: 'instant', top: scrollTop });
|
const panes = resolvePaneRefs();
|
||||||
isScrolling.pinnedRight = false;
|
const hasRightPinnedColumns = pinnedRightColumnCount > 0;
|
||||||
} else {
|
const scrollElement =
|
||||||
|
hasRightPinnedColumns && panes.pinnedRight ? panes.pinnedRight : panes.row;
|
||||||
|
|
||||||
|
if (scrollElement && onScrollEndRef.current) {
|
||||||
|
onScrollEndRef.current(
|
||||||
|
scrollElement.scrollTop,
|
||||||
|
(handleRef.current?.internalState ??
|
||||||
|
(undefined as any)) as ItemListStateActions,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollTimeouts.delete(element);
|
||||||
|
}, 150);
|
||||||
|
|
||||||
|
scrollTimeouts.set(element, timeout);
|
||||||
|
};
|
||||||
|
|
||||||
|
const syncScroll = (e: HTMLElementEventMap['scroll']) => {
|
||||||
|
const currentElement = e.currentTarget as HTMLDivElement;
|
||||||
|
markElementAsScrolling(currentElement);
|
||||||
|
|
||||||
|
const panes = resolvePaneRefs();
|
||||||
|
const syncHeader = panes.header;
|
||||||
|
const syncRow = panes.row;
|
||||||
|
const syncPinnedLeft = panes.pinnedLeft;
|
||||||
|
const syncPinnedRight = panes.pinnedRight;
|
||||||
|
|
||||||
|
if (!syncRow) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const shouldSync =
|
||||||
|
currentElement === activeElement.element ||
|
||||||
|
scrollingElements.has(currentElement);
|
||||||
|
|
||||||
|
if (!shouldSync) return;
|
||||||
|
|
||||||
|
const scrollTop = currentElement.scrollTop;
|
||||||
|
const scrollLeft = currentElement.scrollLeft;
|
||||||
|
|
||||||
|
const isScrolling = {
|
||||||
|
header: false,
|
||||||
|
pinnedLeft: false,
|
||||||
|
pinnedRight: false,
|
||||||
|
row: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
const hasRightPinnedColumns = pinnedRightColumnCount > 0;
|
||||||
|
|
||||||
|
if (syncHeader && currentElement === syncHeader && !isScrolling.row) {
|
||||||
isScrolling.row = true;
|
isScrolling.row = true;
|
||||||
row.scrollTo({ behavior: 'instant', top: scrollTop });
|
syncRow.scrollTo({ behavior: 'instant', left: scrollLeft });
|
||||||
isScrolling.row = false;
|
isScrolling.row = false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (pinnedRight && e.currentTarget === pinnedRight && !isScrolling.row) {
|
if (
|
||||||
isScrolling.row = true;
|
currentElement === syncRow &&
|
||||||
row.scrollTo({ behavior: 'instant', top: scrollTop });
|
!isScrolling.header &&
|
||||||
isScrolling.row = false;
|
!isScrolling.pinnedLeft &&
|
||||||
if (pinnedLeft) {
|
!isScrolling.pinnedRight
|
||||||
isScrolling.pinnedLeft = true;
|
) {
|
||||||
pinnedLeft.scrollTo({ behavior: 'instant', top: scrollTop });
|
if (syncHeader) {
|
||||||
|
isScrolling.header = true;
|
||||||
|
syncHeader.scrollTo({ behavior: 'instant', left: scrollLeft });
|
||||||
|
}
|
||||||
|
if (hasRightPinnedColumns && syncPinnedRight) {
|
||||||
|
isScrolling.pinnedRight = true;
|
||||||
|
syncPinnedRight.scrollTo({ behavior: 'instant', top: scrollTop });
|
||||||
|
isScrolling.pinnedRight = false;
|
||||||
|
} else {
|
||||||
|
if (syncPinnedLeft) {
|
||||||
|
isScrolling.pinnedLeft = true;
|
||||||
|
syncPinnedLeft.scrollTo({ behavior: 'instant', top: scrollTop });
|
||||||
|
}
|
||||||
|
if (syncPinnedRight) {
|
||||||
|
isScrolling.pinnedRight = true;
|
||||||
|
syncPinnedRight.scrollTo({ behavior: 'instant', top: scrollTop });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isScrolling.header = false;
|
||||||
isScrolling.pinnedLeft = false;
|
isScrolling.pinnedLeft = false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (header) {
|
if (syncPinnedLeft && currentElement === syncPinnedLeft && !isScrolling.row) {
|
||||||
header.addEventListener('pointermove', setActiveElement);
|
if (hasRightPinnedColumns && syncPinnedRight) {
|
||||||
header.addEventListener('wheel', setActiveElementFromWheel);
|
isScrolling.pinnedRight = true;
|
||||||
header.addEventListener('scroll', syncScroll);
|
syncPinnedRight.scrollTo({ behavior: 'instant', top: scrollTop });
|
||||||
}
|
isScrolling.pinnedRight = false;
|
||||||
row.addEventListener('pointermove', setActiveElement);
|
} else {
|
||||||
row.addEventListener('wheel', setActiveElementFromWheel);
|
isScrolling.row = true;
|
||||||
row.addEventListener('scroll', syncScroll);
|
syncRow.scrollTo({ behavior: 'instant', top: scrollTop });
|
||||||
if (pinnedLeft) {
|
isScrolling.row = false;
|
||||||
pinnedLeft.addEventListener('pointermove', setActiveElement);
|
}
|
||||||
pinnedLeft.addEventListener('wheel', setActiveElementFromWheel);
|
}
|
||||||
pinnedLeft.addEventListener('scroll', syncScroll);
|
|
||||||
}
|
|
||||||
if (pinnedRight) {
|
|
||||||
pinnedRight.addEventListener('pointermove', setActiveElement);
|
|
||||||
pinnedRight.addEventListener('wheel', setActiveElementFromWheel);
|
|
||||||
pinnedRight.addEventListener('scroll', syncScroll);
|
|
||||||
}
|
|
||||||
|
|
||||||
let heightSyncDebounceTimeout: NodeJS.Timeout | null = null;
|
if (syncPinnedRight && currentElement === syncPinnedRight && !isScrolling.row) {
|
||||||
const resizeObserver = new ResizeObserver(() => {
|
isScrolling.row = true;
|
||||||
if (heightSyncDebounceTimeout) {
|
syncRow.scrollTo({ behavior: 'instant', top: scrollTop });
|
||||||
clearTimeout(heightSyncDebounceTimeout);
|
isScrolling.row = false;
|
||||||
}
|
if (syncPinnedLeft) {
|
||||||
heightSyncDebounceTimeout = setTimeout(() => {
|
isScrolling.pinnedLeft = true;
|
||||||
syncHeights();
|
syncPinnedLeft.scrollTo({ behavior: 'instant', top: scrollTop });
|
||||||
}, 100);
|
isScrolling.pinnedLeft = false;
|
||||||
});
|
}
|
||||||
|
}
|
||||||
resizeObserver.observe(row);
|
};
|
||||||
if (pinnedLeft) resizeObserver.observe(pinnedLeft);
|
|
||||||
if (pinnedRight) resizeObserver.observe(pinnedRight);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
clearTimeout(timeoutId);
|
|
||||||
scrollTimeouts.forEach((timeout) => clearTimeout(timeout));
|
|
||||||
scrollTimeouts.clear();
|
|
||||||
scrollingElements.clear();
|
|
||||||
|
|
||||||
if (header) {
|
if (header) {
|
||||||
header.removeEventListener('pointermove', setActiveElement);
|
header.addEventListener('pointermove', setActiveElement);
|
||||||
header.removeEventListener('wheel', setActiveElementFromWheel);
|
header.addEventListener('wheel', setActiveElementFromWheel);
|
||||||
header.removeEventListener('scroll', syncScroll);
|
header.addEventListener('scroll', syncScroll);
|
||||||
}
|
}
|
||||||
row.removeEventListener('pointermove', setActiveElement);
|
row.addEventListener('pointermove', setActiveElement);
|
||||||
row.removeEventListener('wheel', setActiveElementFromWheel);
|
row.addEventListener('wheel', setActiveElementFromWheel);
|
||||||
row.removeEventListener('scroll', syncScroll);
|
row.addEventListener('scroll', syncScroll);
|
||||||
if (pinnedLeft) {
|
if (pinnedLeft) {
|
||||||
pinnedLeft.removeEventListener('pointermove', setActiveElement);
|
pinnedLeft.addEventListener('pointermove', setActiveElement);
|
||||||
pinnedLeft.removeEventListener('wheel', setActiveElementFromWheel);
|
pinnedLeft.addEventListener('wheel', setActiveElementFromWheel);
|
||||||
pinnedLeft.removeEventListener('scroll', syncScroll);
|
pinnedLeft.addEventListener('scroll', syncScroll);
|
||||||
}
|
}
|
||||||
if (pinnedRight) {
|
if (pinnedRight) {
|
||||||
pinnedRight.removeEventListener('pointermove', setActiveElement);
|
pinnedRight.addEventListener('pointermove', setActiveElement);
|
||||||
pinnedRight.removeEventListener('wheel', setActiveElementFromWheel);
|
pinnedRight.addEventListener('wheel', setActiveElementFromWheel);
|
||||||
pinnedRight.removeEventListener('scroll', syncScroll);
|
pinnedRight.addEventListener('scroll', syncScroll);
|
||||||
}
|
}
|
||||||
if (heightSyncDebounceTimeout) {
|
|
||||||
clearTimeout(heightSyncDebounceTimeout);
|
let heightSyncDebounceTimeout: NodeJS.Timeout | null = null;
|
||||||
}
|
const resizeObserver = new ResizeObserver(() => {
|
||||||
resizeObserver.disconnect();
|
if (heightSyncDebounceTimeout) {
|
||||||
|
clearTimeout(heightSyncDebounceTimeout);
|
||||||
|
}
|
||||||
|
heightSyncDebounceTimeout = setTimeout(() => {
|
||||||
|
syncHeights();
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
|
||||||
|
resizeObserver.observe(row);
|
||||||
|
if (pinnedLeft) resizeObserver.observe(pinnedLeft);
|
||||||
|
if (pinnedRight) resizeObserver.observe(pinnedRight);
|
||||||
|
|
||||||
|
cleanup = () => {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
scrollTimeouts.forEach((timeout) => clearTimeout(timeout));
|
||||||
|
scrollTimeouts.clear();
|
||||||
|
scrollingElements.clear();
|
||||||
|
|
||||||
|
if (header) {
|
||||||
|
header.removeEventListener('pointermove', setActiveElement);
|
||||||
|
header.removeEventListener('wheel', setActiveElementFromWheel);
|
||||||
|
header.removeEventListener('scroll', syncScroll);
|
||||||
|
}
|
||||||
|
row.removeEventListener('pointermove', setActiveElement);
|
||||||
|
row.removeEventListener('wheel', setActiveElementFromWheel);
|
||||||
|
row.removeEventListener('scroll', syncScroll);
|
||||||
|
if (pinnedLeft) {
|
||||||
|
pinnedLeft.removeEventListener('pointermove', setActiveElement);
|
||||||
|
pinnedLeft.removeEventListener('wheel', setActiveElementFromWheel);
|
||||||
|
pinnedLeft.removeEventListener('scroll', syncScroll);
|
||||||
|
}
|
||||||
|
if (pinnedRight) {
|
||||||
|
pinnedRight.removeEventListener('pointermove', setActiveElement);
|
||||||
|
pinnedRight.removeEventListener('wheel', setActiveElementFromWheel);
|
||||||
|
pinnedRight.removeEventListener('scroll', syncScroll);
|
||||||
|
}
|
||||||
|
if (heightSyncDebounceTimeout) {
|
||||||
|
clearTimeout(heightSyncDebounceTimeout);
|
||||||
|
}
|
||||||
|
resizeObserver.disconnect();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
setupFrameId = requestAnimationFrame(() => {
|
||||||
|
setupFrameId = requestAnimationFrame(setupScrollSync);
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
disposed = true;
|
||||||
|
cancelAnimationFrame(setupFrameId);
|
||||||
|
cleanup?.();
|
||||||
};
|
};
|
||||||
}, [
|
}, [
|
||||||
handleRef,
|
handleRef,
|
||||||
@@ -461,6 +556,7 @@ export const useTablePaneSync = ({
|
|||||||
pinnedRightColumnRef,
|
pinnedRightColumnRef,
|
||||||
pinnedRowRef,
|
pinnedRowRef,
|
||||||
rowRef,
|
rowRef,
|
||||||
|
scrollSyncKey,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Handle left and right shadow visibility based on horizontal scroll
|
// Handle left and right shadow visibility based on horizontal scroll
|
||||||
|
|||||||
@@ -1557,6 +1557,17 @@ const BaseItemTableList = ({
|
|||||||
scrollCellProps,
|
scrollCellProps,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const scrollSyncKey = useMemo(
|
||||||
|
() =>
|
||||||
|
parsedColumns
|
||||||
|
.map(
|
||||||
|
(col) =>
|
||||||
|
`${col.id}:${col.pinned ?? 'none'}:${col.width}:${col.isEnabled !== false}`,
|
||||||
|
)
|
||||||
|
.join('|'),
|
||||||
|
[parsedColumns],
|
||||||
|
);
|
||||||
|
|
||||||
useTablePaneSync({
|
useTablePaneSync({
|
||||||
enableDrag,
|
enableDrag,
|
||||||
enableDragScroll,
|
enableDragScroll,
|
||||||
@@ -1571,6 +1582,7 @@ const BaseItemTableList = ({
|
|||||||
rowRef,
|
rowRef,
|
||||||
scrollContainerRef,
|
scrollContainerRef,
|
||||||
scrollShadowStore,
|
scrollShadowStore,
|
||||||
|
scrollSyncKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create a wrapper for getRowHeight that doesn't require cellProps (for sticky group rows hook)
|
// Create a wrapper for getRowHeight that doesn't require cellProps (for sticky group rows hook)
|
||||||
|
|||||||
Reference in New Issue
Block a user