mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-09 20:29:36 +02:00
support both left and right column pinning
This commit is contained in:
@@ -23,7 +23,7 @@
|
|||||||
min-height: 0;
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-table-sticky-rows-container {
|
.item-table-pinned-rows-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -31,26 +31,33 @@
|
|||||||
min-height: 0;
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-table-sticky-rows-grid-container {
|
.item-table-pinned-rows-grid-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
flex: 0 1 auto;
|
flex: 0 1 auto;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-table-sticky-columns-grid-container {
|
.item-table-pinned-columns-grid-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 0 1 auto;
|
flex: 0 1 auto;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-table-sticky-intersection-grid-container {
|
.item-table-pinned-intersection-grid-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
flex: 0 1 auto;
|
flex: 0 1 auto;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-table-sticky-columns-container {
|
.item-table-pinned-columns-container {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-width: 0;
|
||||||
|
height: 100%;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-table-pinned-right-columns-container {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@@ -65,7 +72,7 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-table-sticky-header-shadow {
|
.item-table-pinned-header-shadow {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 100%;
|
top: 100%;
|
||||||
right: 0;
|
right: 0;
|
||||||
@@ -97,6 +104,22 @@
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.item-table-right-scroll-shadow {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 1;
|
||||||
|
width: 8px;
|
||||||
|
pointer-events: none;
|
||||||
|
background: linear-gradient(
|
||||||
|
to left,
|
||||||
|
rgb(0 0 0 / 50%) 0%,
|
||||||
|
rgb(0 0 0 / 5%) 50%,
|
||||||
|
transparent 100%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
.list-expanded-container {
|
.list-expanded-container {
|
||||||
height: 500px;
|
height: 500px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,10 +64,11 @@ interface ItemTableListProps {
|
|||||||
onScroll?: (event: UIEvent<HTMLDivElement>) => void;
|
onScroll?: (event: UIEvent<HTMLDivElement>) => void;
|
||||||
onScrollEnd?: () => void;
|
onScrollEnd?: () => void;
|
||||||
onStartReached?: (index: number) => void;
|
onStartReached?: (index: number) => void;
|
||||||
|
pinnedLeftColumnCount: number;
|
||||||
|
pinnedRightColumnCount?: number;
|
||||||
ref?: Ref<GridImperativeAPI>;
|
ref?: Ref<GridImperativeAPI>;
|
||||||
rowHeight: ((index: number, cellProps: CellProps) => number) | number;
|
rowHeight: ((index: number, cellProps: CellProps) => number) | number;
|
||||||
size?: 'compact' | 'default';
|
size?: 'compact' | 'default';
|
||||||
stickyColumnCount: number;
|
|
||||||
totalItemCount: number;
|
totalItemCount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,21 +105,25 @@ export const ItemTableList = ({
|
|||||||
onScroll,
|
onScroll,
|
||||||
onScrollEnd,
|
onScrollEnd,
|
||||||
onStartReached,
|
onStartReached,
|
||||||
|
pinnedLeftColumnCount,
|
||||||
|
pinnedRightColumnCount = 0,
|
||||||
ref,
|
ref,
|
||||||
rowHeight,
|
rowHeight,
|
||||||
size = 'default',
|
size = 'default',
|
||||||
stickyColumnCount,
|
|
||||||
totalItemCount,
|
totalItemCount,
|
||||||
}: ItemTableListProps) => {
|
}: ItemTableListProps) => {
|
||||||
const stickyRowCount = enableHeader ? 1 : 0;
|
const pinnedRowCount = enableHeader ? 1 : 0;
|
||||||
const totalRowCount = totalItemCount - (stickyRowCount ?? 0);
|
const totalRowCount = totalItemCount - (pinnedRowCount ?? 0);
|
||||||
const totalColumnCount = columnCount - (stickyColumnCount ?? 0);
|
const totalColumnCount =
|
||||||
const stickyRowRef = useRef<HTMLDivElement>(null);
|
columnCount - (pinnedLeftColumnCount ?? 0) - (pinnedRightColumnCount ?? 0);
|
||||||
|
const pinnedRowRef = useRef<HTMLDivElement>(null);
|
||||||
const rowRef = useRef<HTMLDivElement>(null);
|
const rowRef = useRef<HTMLDivElement>(null);
|
||||||
const stickyColumnRef = useRef<HTMLDivElement>(null);
|
const pinnedLeftColumnRef = useRef<HTMLDivElement>(null);
|
||||||
|
const pinnedRightColumnRef = useRef<HTMLDivElement>(null);
|
||||||
const scrollContainerRef = useRef<HTMLDivElement | null>(null);
|
const scrollContainerRef = useRef<HTMLDivElement | null>(null);
|
||||||
const mergedRowRef = useMergedRef(rowRef, scrollContainerRef);
|
const mergedRowRef = useMergedRef(rowRef, scrollContainerRef);
|
||||||
const [showLeftShadow, setShowLeftShadow] = useState(false);
|
const [showLeftShadow, setShowLeftShadow] = useState(false);
|
||||||
|
const [showRightShadow, setShowRightShadow] = useState(false);
|
||||||
|
|
||||||
const [initialize] = useOverlayScrollbars({
|
const [initialize] = useOverlayScrollbars({
|
||||||
defer: true,
|
defer: true,
|
||||||
@@ -156,27 +161,38 @@ export const ItemTableList = ({
|
|||||||
}, [initialize]);
|
}, [initialize]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const header = stickyRowRef.current?.childNodes[0] as HTMLDivElement;
|
const header = pinnedRowRef.current?.childNodes[0] as HTMLDivElement;
|
||||||
const row = rowRef.current?.childNodes[0] as HTMLDivElement;
|
const row = rowRef.current?.childNodes[0] as HTMLDivElement;
|
||||||
const sticky = stickyColumnRef.current?.childNodes[0] as HTMLDivElement;
|
const pinnedLeft = pinnedLeftColumnRef.current?.childNodes[0] as HTMLDivElement;
|
||||||
|
const pinnedRight = pinnedRightColumnRef.current?.childNodes[0] as HTMLDivElement;
|
||||||
|
|
||||||
// At minimum, we need the main row element
|
// At minimum, we need the main row element
|
||||||
if (row) {
|
if (row) {
|
||||||
// Ensure all containers have the same height
|
// Ensure all containers have the same height
|
||||||
const syncHeights = () => {
|
const syncHeights = () => {
|
||||||
if (sticky) {
|
|
||||||
const rowHeight = row.scrollHeight;
|
const rowHeight = row.scrollHeight;
|
||||||
const stickyHeight = sticky.scrollHeight;
|
let targetHeight = rowHeight;
|
||||||
|
|
||||||
// Set consistent heights - use the larger of the two
|
if (pinnedLeft) {
|
||||||
const targetHeight = Math.max(rowHeight, stickyHeight);
|
const pinnedLeftHeight = pinnedLeft.scrollHeight;
|
||||||
if (sticky.style.height !== `${targetHeight}px`) {
|
targetHeight = Math.max(targetHeight, pinnedLeftHeight);
|
||||||
sticky.style.height = `${targetHeight}px`;
|
}
|
||||||
|
|
||||||
|
if (pinnedRight) {
|
||||||
|
const pinnedRightHeight = pinnedRight.scrollHeight;
|
||||||
|
targetHeight = Math.max(targetHeight, pinnedRightHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set consistent heights for all elements
|
||||||
|
if (pinnedLeft && pinnedLeft.style.height !== `${targetHeight}px`) {
|
||||||
|
pinnedLeft.style.height = `${targetHeight}px`;
|
||||||
|
}
|
||||||
|
if (pinnedRight && pinnedRight.style.height !== `${targetHeight}px`) {
|
||||||
|
pinnedRight.style.height = `${targetHeight}px`;
|
||||||
}
|
}
|
||||||
if (row.style.height !== `${targetHeight}px`) {
|
if (row.style.height !== `${targetHeight}px`) {
|
||||||
row.style.height = `${targetHeight}px`;
|
row.style.height = `${targetHeight}px`;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const timeoutId = setTimeout(syncHeights, 0);
|
const timeoutId = setTimeout(syncHeights, 0);
|
||||||
@@ -231,7 +247,12 @@ export const ItemTableList = ({
|
|||||||
const scrollLeft = (e.currentTarget as HTMLDivElement).scrollLeft;
|
const scrollLeft = (e.currentTarget as HTMLDivElement).scrollLeft;
|
||||||
|
|
||||||
// Prevent recursive scroll events
|
// Prevent recursive scroll events
|
||||||
const isScrolling = { header: false, row: false, sticky: false };
|
const isScrolling = {
|
||||||
|
header: false,
|
||||||
|
pinnedLeft: false,
|
||||||
|
pinnedRight: false,
|
||||||
|
row: false,
|
||||||
|
};
|
||||||
|
|
||||||
// Sync horizontal scroll between header and main content (only if header exists)
|
// Sync horizontal scroll between header and main content (only if header exists)
|
||||||
if (header && e.currentTarget === header && !isScrolling.row) {
|
if (header && e.currentTarget === header && !isScrolling.row) {
|
||||||
@@ -245,8 +266,13 @@ export const ItemTableList = ({
|
|||||||
}, 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync from main content to header and sticky column
|
// Sync from main content to header and sticky columns
|
||||||
if (e.currentTarget === row && !isScrolling.header && !isScrolling.sticky) {
|
if (
|
||||||
|
e.currentTarget === row &&
|
||||||
|
!isScrolling.header &&
|
||||||
|
!isScrolling.pinnedLeft &&
|
||||||
|
!isScrolling.pinnedRight
|
||||||
|
) {
|
||||||
if (header) {
|
if (header) {
|
||||||
isScrolling.header = true;
|
isScrolling.header = true;
|
||||||
header.scrollTo({
|
header.scrollTo({
|
||||||
@@ -254,21 +280,41 @@ export const ItemTableList = ({
|
|||||||
left: scrollLeft,
|
left: scrollLeft,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (sticky) {
|
if (pinnedLeft) {
|
||||||
isScrolling.sticky = true;
|
isScrolling.pinnedLeft = true;
|
||||||
sticky.scrollTo({
|
pinnedLeft.scrollTo({
|
||||||
|
behavior: 'instant',
|
||||||
|
top: scrollTop,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (pinnedRight) {
|
||||||
|
isScrolling.pinnedRight = true;
|
||||||
|
pinnedRight.scrollTo({
|
||||||
behavior: 'instant',
|
behavior: 'instant',
|
||||||
top: scrollTop,
|
top: scrollTop,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
isScrolling.header = false;
|
isScrolling.header = false;
|
||||||
isScrolling.sticky = false;
|
isScrolling.pinnedLeft = false;
|
||||||
|
isScrolling.pinnedRight = false;
|
||||||
}, 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync vertical scroll between sticky column and main content (only if sticky exists)
|
// Sync vertical scroll between left pinned column and main content (only if pinnedLeft exists)
|
||||||
if (sticky && e.currentTarget === sticky && !isScrolling.row) {
|
if (pinnedLeft && e.currentTarget === pinnedLeft && !isScrolling.row) {
|
||||||
|
isScrolling.row = true;
|
||||||
|
row.scrollTo({
|
||||||
|
behavior: 'instant',
|
||||||
|
top: scrollTop,
|
||||||
|
});
|
||||||
|
setTimeout(() => {
|
||||||
|
isScrolling.row = false;
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync vertical scroll between right pinned column and main content (only if pinnedRight exists)
|
||||||
|
if (pinnedRight && e.currentTarget === pinnedRight && !isScrolling.row) {
|
||||||
isScrolling.row = true;
|
isScrolling.row = true;
|
||||||
row.scrollTo({
|
row.scrollTo({
|
||||||
behavior: 'instant',
|
behavior: 'instant',
|
||||||
@@ -289,10 +335,15 @@ export const ItemTableList = ({
|
|||||||
row.addEventListener('pointermove', setActiveElement);
|
row.addEventListener('pointermove', setActiveElement);
|
||||||
row.addEventListener('wheel', setActiveElementFromWheel);
|
row.addEventListener('wheel', setActiveElementFromWheel);
|
||||||
row.addEventListener('scroll', syncScroll);
|
row.addEventListener('scroll', syncScroll);
|
||||||
if (sticky) {
|
if (pinnedLeft) {
|
||||||
sticky.addEventListener('pointermove', setActiveElement);
|
pinnedLeft.addEventListener('pointermove', setActiveElement);
|
||||||
sticky.addEventListener('wheel', setActiveElementFromWheel);
|
pinnedLeft.addEventListener('wheel', setActiveElementFromWheel);
|
||||||
sticky.addEventListener('scroll', syncScroll);
|
pinnedLeft.addEventListener('scroll', syncScroll);
|
||||||
|
}
|
||||||
|
if (pinnedRight) {
|
||||||
|
pinnedRight.addEventListener('pointermove', setActiveElement);
|
||||||
|
pinnedRight.addEventListener('wheel', setActiveElementFromWheel);
|
||||||
|
pinnedRight.addEventListener('scroll', syncScroll);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add resize observer to maintain height sync
|
// Add resize observer to maintain height sync
|
||||||
@@ -301,8 +352,11 @@ export const ItemTableList = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
resizeObserver.observe(row);
|
resizeObserver.observe(row);
|
||||||
if (sticky) {
|
if (pinnedLeft) {
|
||||||
resizeObserver.observe(sticky);
|
resizeObserver.observe(pinnedLeft);
|
||||||
|
}
|
||||||
|
if (pinnedRight) {
|
||||||
|
resizeObserver.observe(pinnedRight);
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
@@ -319,10 +373,15 @@ export const ItemTableList = ({
|
|||||||
row.removeEventListener('pointermove', setActiveElement);
|
row.removeEventListener('pointermove', setActiveElement);
|
||||||
row.removeEventListener('wheel', setActiveElementFromWheel);
|
row.removeEventListener('wheel', setActiveElementFromWheel);
|
||||||
row.removeEventListener('scroll', syncScroll);
|
row.removeEventListener('scroll', syncScroll);
|
||||||
if (sticky) {
|
if (pinnedLeft) {
|
||||||
sticky.removeEventListener('pointermove', setActiveElement);
|
pinnedLeft.removeEventListener('pointermove', setActiveElement);
|
||||||
sticky.removeEventListener('wheel', setActiveElementFromWheel);
|
pinnedLeft.removeEventListener('wheel', setActiveElementFromWheel);
|
||||||
sticky.removeEventListener('scroll', syncScroll);
|
pinnedLeft.removeEventListener('scroll', syncScroll);
|
||||||
|
}
|
||||||
|
if (pinnedRight) {
|
||||||
|
pinnedRight.removeEventListener('pointermove', setActiveElement);
|
||||||
|
pinnedRight.removeEventListener('wheel', setActiveElementFromWheel);
|
||||||
|
pinnedRight.removeEventListener('scroll', syncScroll);
|
||||||
}
|
}
|
||||||
resizeObserver.disconnect();
|
resizeObserver.disconnect();
|
||||||
};
|
};
|
||||||
@@ -331,18 +390,22 @@ export const ItemTableList = ({
|
|||||||
return undefined;
|
return undefined;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Handle left shadow visibility based on horizontal scroll
|
// Handle left and right shadow visibility based on horizontal scroll
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const row = rowRef.current?.childNodes[0] as HTMLDivElement;
|
const row = rowRef.current?.childNodes[0] as HTMLDivElement;
|
||||||
|
|
||||||
if (!row || !stickyColumnCount) {
|
if (!row) {
|
||||||
setShowLeftShadow(false);
|
setShowLeftShadow(false);
|
||||||
|
setShowRightShadow(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const checkScrollPosition = () => {
|
const checkScrollPosition = () => {
|
||||||
const scrollLeft = row.scrollLeft;
|
const scrollLeft = row.scrollLeft;
|
||||||
setShowLeftShadow(scrollLeft > 0);
|
const maxScrollLeft = row.scrollWidth - row.clientWidth;
|
||||||
|
|
||||||
|
setShowLeftShadow(pinnedLeftColumnCount > 0 && scrollLeft > 0);
|
||||||
|
setShowRightShadow(pinnedRightColumnCount > 0 && scrollLeft < maxScrollLeft);
|
||||||
};
|
};
|
||||||
|
|
||||||
checkScrollPosition();
|
checkScrollPosition();
|
||||||
@@ -352,7 +415,7 @@ export const ItemTableList = ({
|
|||||||
return () => {
|
return () => {
|
||||||
row.removeEventListener('scroll', checkScrollPosition);
|
row.removeEventListener('scroll', checkScrollPosition);
|
||||||
};
|
};
|
||||||
}, [stickyColumnCount]);
|
}, [pinnedLeftColumnCount, pinnedRightColumnCount]);
|
||||||
|
|
||||||
const getRowHeight = useCallback(
|
const getRowHeight = useCallback(
|
||||||
(index: number, cellProps: CellProps) => {
|
(index: number, cellProps: CellProps) => {
|
||||||
@@ -360,13 +423,13 @@ export const ItemTableList = ({
|
|||||||
typeof rowHeight === 'number' ? rowHeight : rowHeight(index, cellProps);
|
typeof rowHeight === 'number' ? rowHeight : rowHeight(index, cellProps);
|
||||||
|
|
||||||
// If enableHeader is true and this is the first sticky row, use fixed header height
|
// If enableHeader is true and this is the first sticky row, use fixed header height
|
||||||
if (enableHeader && index === 0 && stickyRowCount > 0) {
|
if (enableHeader && index === 0 && pinnedRowCount > 0) {
|
||||||
return headerHeight;
|
return headerHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
return baseHeight;
|
return baseHeight;
|
||||||
},
|
},
|
||||||
[enableHeader, headerHeight, rowHeight, stickyRowCount],
|
[enableHeader, headerHeight, rowHeight, pinnedRowCount],
|
||||||
);
|
);
|
||||||
|
|
||||||
const internalState = useItemListState();
|
const internalState = useItemListState();
|
||||||
@@ -402,41 +465,70 @@ export const ItemTableList = ({
|
|||||||
? ({ columnStartIndex, columnStopIndex, rowStartIndex, rowStopIndex }) => {
|
? ({ columnStartIndex, columnStopIndex, rowStartIndex, rowStopIndex }) => {
|
||||||
return onCellsRendered!(
|
return onCellsRendered!(
|
||||||
{
|
{
|
||||||
columnStartIndex: columnStartIndex + (stickyColumnCount ?? 0),
|
columnStartIndex: columnStartIndex + (pinnedLeftColumnCount ?? 0),
|
||||||
columnStopIndex: columnStopIndex + (stickyColumnCount ?? 0),
|
columnStopIndex: columnStopIndex + (pinnedLeftColumnCount ?? 0),
|
||||||
rowStartIndex: rowStartIndex + (stickyRowCount ?? 0),
|
rowStartIndex: rowStartIndex + (pinnedRowCount ?? 0),
|
||||||
rowStopIndex: rowStopIndex + (stickyRowCount ?? 0),
|
rowStopIndex: rowStopIndex + (pinnedRowCount ?? 0),
|
||||||
},
|
},
|
||||||
cells,
|
cells,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
: undefined;
|
: undefined;
|
||||||
},
|
},
|
||||||
[onCellsRendered, onRangeChanged, stickyColumnCount, stickyRowCount],
|
[onCellsRendered, onRangeChanged, pinnedLeftColumnCount, pinnedRowCount],
|
||||||
);
|
);
|
||||||
|
|
||||||
const StickyRowCell = useCallback(
|
const PinnedRowCell = useCallback(
|
||||||
(cellProps: CellComponentProps & CellProps) => {
|
(cellProps: CellComponentProps & CellProps) => {
|
||||||
return (
|
return (
|
||||||
<CellComponent
|
<CellComponent
|
||||||
{...cellProps}
|
{...cellProps}
|
||||||
columnIndex={cellProps.columnIndex + (stickyColumnCount ?? 0)}
|
columnIndex={cellProps.columnIndex + (pinnedLeftColumnCount ?? 0)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[stickyColumnCount, CellComponent],
|
[pinnedLeftColumnCount, CellComponent],
|
||||||
);
|
);
|
||||||
|
|
||||||
const StickyColumnCell = useCallback(
|
const PinnedColumnCell = useCallback(
|
||||||
(cellProps: CellComponentProps & CellProps) => {
|
(cellProps: CellComponentProps & CellProps) => {
|
||||||
return (
|
return (
|
||||||
<CellComponent
|
<CellComponent
|
||||||
{...cellProps}
|
{...cellProps}
|
||||||
rowIndex={cellProps.rowIndex + (stickyRowCount ?? 0)}
|
rowIndex={cellProps.rowIndex + (pinnedRowCount ?? 0)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[stickyRowCount, CellComponent],
|
[pinnedRowCount, CellComponent],
|
||||||
|
);
|
||||||
|
|
||||||
|
const PinnedRightColumnCell = useCallback(
|
||||||
|
(cellProps: CellComponentProps & CellProps) => {
|
||||||
|
return (
|
||||||
|
<CellComponent
|
||||||
|
{...cellProps}
|
||||||
|
columnIndex={
|
||||||
|
cellProps.columnIndex + (pinnedLeftColumnCount ?? 0) + totalColumnCount
|
||||||
|
}
|
||||||
|
rowIndex={cellProps.rowIndex + (pinnedRowCount ?? 0)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[pinnedLeftColumnCount, pinnedRowCount, totalColumnCount, CellComponent],
|
||||||
|
);
|
||||||
|
|
||||||
|
const PinnedRightIntersectionCell = useCallback(
|
||||||
|
(cellProps: CellComponentProps & CellProps) => {
|
||||||
|
return (
|
||||||
|
<CellComponent
|
||||||
|
{...cellProps}
|
||||||
|
columnIndex={
|
||||||
|
cellProps.columnIndex + (pinnedLeftColumnCount ?? 0) + totalColumnCount
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[pinnedLeftColumnCount, totalColumnCount, CellComponent],
|
||||||
);
|
);
|
||||||
|
|
||||||
const RowCell = useCallback(
|
const RowCell = useCallback(
|
||||||
@@ -444,15 +536,15 @@ export const ItemTableList = ({
|
|||||||
return (
|
return (
|
||||||
<CellComponent
|
<CellComponent
|
||||||
{...cellProps}
|
{...cellProps}
|
||||||
columnIndex={cellProps.columnIndex + (stickyColumnCount ?? 0)}
|
columnIndex={cellProps.columnIndex + (pinnedLeftColumnCount ?? 0)}
|
||||||
// onClick={(e) => {
|
// onClick={(e) => {
|
||||||
// onItemClick?.(cellProps.data[cellProps.rowIndex], cellProps.rowIndex, e);
|
// onItemClick?.(cellProps.data[cellProps.rowIndex], cellProps.rowIndex, e);
|
||||||
// }}
|
// }}
|
||||||
rowIndex={cellProps.rowIndex + (stickyRowCount ?? 0)}
|
rowIndex={cellProps.rowIndex + (pinnedRowCount ?? 0)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[stickyColumnCount, stickyRowCount, CellComponent],
|
[pinnedLeftColumnCount, pinnedRowCount, CellComponent],
|
||||||
);
|
);
|
||||||
|
|
||||||
const cellProps = {
|
const cellProps = {
|
||||||
@@ -478,9 +570,12 @@ export const ItemTableList = ({
|
|||||||
>
|
>
|
||||||
<div className={styles.itemTableContainer}>
|
<div className={styles.itemTableContainer}>
|
||||||
<div
|
<div
|
||||||
className={styles.itemTableStickyColumnsGridContainer}
|
className={styles.itemTablePinnedColumnsGridContainer}
|
||||||
style={{
|
style={{
|
||||||
minWidth: `${Array.from({ length: stickyColumnCount ?? 0 }, () => 0).reduce(
|
minWidth: `${Array.from(
|
||||||
|
{ length: pinnedLeftColumnCount ?? 0 },
|
||||||
|
() => 0,
|
||||||
|
).reduce(
|
||||||
(a, _, i) =>
|
(a, _, i) =>
|
||||||
a +
|
a +
|
||||||
(typeof columnWidth === 'number'
|
(typeof columnWidth === 'number'
|
||||||
@@ -490,12 +585,12 @@ export const ItemTableList = ({
|
|||||||
)}px`,
|
)}px`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{!!(stickyColumnCount || stickyRowCount) && (
|
{!!(pinnedLeftColumnCount || pinnedRowCount) && (
|
||||||
<div
|
<div
|
||||||
className={styles.itemTableStickyIntersectionGridContainer}
|
className={styles.itemTablePinnedIntersectionGridContainer}
|
||||||
style={{
|
style={{
|
||||||
minHeight: `${Array.from(
|
minHeight: `${Array.from(
|
||||||
{ length: stickyRowCount ?? 0 },
|
{ length: pinnedRowCount ?? 0 },
|
||||||
() => 0,
|
() => 0,
|
||||||
).reduce((a, _, i) => a + getRowHeight(i, cellProps), 0)}px`,
|
).reduce((a, _, i) => a + getRowHeight(i, cellProps), 0)}px`,
|
||||||
}}
|
}}
|
||||||
@@ -504,64 +599,67 @@ export const ItemTableList = ({
|
|||||||
cellComponent={CellComponent as any}
|
cellComponent={CellComponent as any}
|
||||||
cellProps={cellProps}
|
cellProps={cellProps}
|
||||||
className={styles.noScrollbar}
|
className={styles.noScrollbar}
|
||||||
columnCount={stickyColumnCount}
|
columnCount={pinnedLeftColumnCount}
|
||||||
columnWidth={columnWidth}
|
columnWidth={columnWidth}
|
||||||
rowCount={stickyRowCount}
|
rowCount={pinnedRowCount}
|
||||||
rowHeight={getRowHeight}
|
rowHeight={getRowHeight}
|
||||||
/>
|
/>
|
||||||
{enableHeader && <div className={styles.itemTableStickyHeaderShadow} />}
|
{enableHeader && <div className={styles.itemTablePinnedHeaderShadow} />}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{!!stickyColumnCount && (
|
{!!pinnedLeftColumnCount && (
|
||||||
<div
|
<div
|
||||||
className={styles.itemTableStickyColumnsContainer}
|
className={styles.itemTablePinnedColumnsContainer}
|
||||||
ref={stickyColumnRef}
|
ref={pinnedLeftColumnRef}
|
||||||
>
|
>
|
||||||
<Grid
|
<Grid
|
||||||
cellComponent={StickyColumnCell}
|
cellComponent={PinnedColumnCell}
|
||||||
cellProps={cellProps}
|
cellProps={cellProps}
|
||||||
className={clsx(styles.noScrollbar, styles.height100)}
|
className={clsx(styles.noScrollbar, styles.height100)}
|
||||||
columnCount={stickyColumnCount}
|
columnCount={pinnedLeftColumnCount}
|
||||||
columnWidth={columnWidth}
|
columnWidth={columnWidth}
|
||||||
rowCount={totalRowCount}
|
rowCount={totalRowCount}
|
||||||
rowHeight={(index, cellProps) => {
|
rowHeight={(index, cellProps) => {
|
||||||
return getRowHeight(index + (stickyRowCount ?? 0), cellProps);
|
return getRowHeight(index + (pinnedRowCount ?? 0), cellProps);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.itemTableStickyRowsContainer}>
|
<div className={styles.itemTablePinnedRowsContainer}>
|
||||||
{!!stickyRowCount && (
|
{!!pinnedRowCount && (
|
||||||
<div
|
<div
|
||||||
className={styles.itemTableStickyRowsGridContainer}
|
className={styles.itemTablePinnedRowsGridContainer}
|
||||||
ref={stickyRowRef}
|
ref={pinnedRowRef}
|
||||||
style={
|
style={
|
||||||
{
|
{
|
||||||
'--header-height': `${headerHeight}px`,
|
'--header-height': `${headerHeight}px`,
|
||||||
minHeight: `${Array.from(
|
minHeight: `${Array.from(
|
||||||
{ length: stickyRowCount ?? 0 },
|
{ length: pinnedRowCount ?? 0 },
|
||||||
() => 0,
|
() => 0,
|
||||||
).reduce((a, _, i) => a + getRowHeight(i, cellProps), 0)}px`,
|
).reduce((a, _, i) => a + getRowHeight(i, cellProps), 0)}px`,
|
||||||
} as React.CSSProperties
|
} as React.CSSProperties
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Grid
|
<Grid
|
||||||
cellComponent={StickyRowCell}
|
cellComponent={PinnedRowCell}
|
||||||
cellProps={cellProps}
|
cellProps={cellProps}
|
||||||
className={styles.noScrollbar}
|
className={styles.noScrollbar}
|
||||||
columnCount={totalColumnCount}
|
columnCount={totalColumnCount}
|
||||||
columnWidth={(index, cellProps) => {
|
columnWidth={(index, cellProps) => {
|
||||||
return typeof columnWidth === 'number'
|
return typeof columnWidth === 'number'
|
||||||
? columnWidth
|
? columnWidth
|
||||||
: columnWidth(index + (stickyColumnCount ?? 0), cellProps);
|
: columnWidth(
|
||||||
|
index + (pinnedLeftColumnCount ?? 0),
|
||||||
|
cellProps,
|
||||||
|
);
|
||||||
}}
|
}}
|
||||||
rowCount={
|
rowCount={
|
||||||
Array.from({ length: stickyRowCount ?? 0 }, () => 0).length
|
Array.from({ length: pinnedRowCount ?? 0 }, () => 0).length
|
||||||
}
|
}
|
||||||
rowHeight={getRowHeight}
|
rowHeight={getRowHeight}
|
||||||
/>
|
/>
|
||||||
{enableHeader && <div className={styles.itemTableStickyHeaderShadow} />}
|
{enableHeader && <div className={styles.itemTablePinnedHeaderShadow} />}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className={styles.itemTableGridContainer} ref={mergedRowRef}>
|
<div className={styles.itemTableGridContainer} ref={mergedRowRef}>
|
||||||
@@ -573,19 +671,98 @@ export const ItemTableList = ({
|
|||||||
columnWidth={(index, cellProps) => {
|
columnWidth={(index, cellProps) => {
|
||||||
return typeof columnWidth === 'number'
|
return typeof columnWidth === 'number'
|
||||||
? columnWidth
|
? columnWidth
|
||||||
: columnWidth(index + (stickyColumnCount ?? 0), cellProps);
|
: columnWidth(index + (pinnedLeftColumnCount ?? 0), cellProps);
|
||||||
}}
|
}}
|
||||||
onCellsRendered={handleOnCellsRendered}
|
onCellsRendered={handleOnCellsRendered}
|
||||||
rowCount={totalRowCount}
|
rowCount={totalRowCount}
|
||||||
rowHeight={(index, cellProps) => {
|
rowHeight={(index, cellProps) => {
|
||||||
return getRowHeight(index + (stickyRowCount ?? 0), cellProps);
|
return getRowHeight(index + (pinnedRowCount ?? 0), cellProps);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{stickyColumnCount > 0 && showLeftShadow && (
|
{pinnedLeftColumnCount > 0 && showLeftShadow && (
|
||||||
<div className={styles.itemTableLeftScrollShadow} />
|
<div className={styles.itemTableLeftScrollShadow} />
|
||||||
)}
|
)}
|
||||||
|
{pinnedRightColumnCount > 0 && showRightShadow && (
|
||||||
|
<div className={styles.itemTableRightScrollShadow} />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{!!pinnedRightColumnCount && (
|
||||||
|
<div
|
||||||
|
className={styles.itemTablePinnedColumnsGridContainer}
|
||||||
|
style={{
|
||||||
|
minWidth: `${Array.from(
|
||||||
|
{ length: pinnedRightColumnCount ?? 0 },
|
||||||
|
() => 0,
|
||||||
|
).reduce(
|
||||||
|
(a, _, i) =>
|
||||||
|
a +
|
||||||
|
(typeof columnWidth === 'number'
|
||||||
|
? columnWidth
|
||||||
|
: columnWidth(
|
||||||
|
i + pinnedLeftColumnCount + totalColumnCount,
|
||||||
|
cellProps,
|
||||||
|
)),
|
||||||
|
0,
|
||||||
|
)}px`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{!!(pinnedRightColumnCount || pinnedRowCount) && (
|
||||||
|
<div
|
||||||
|
className={styles.itemTablePinnedIntersectionGridContainer}
|
||||||
|
style={{
|
||||||
|
minHeight: `${Array.from(
|
||||||
|
{ length: pinnedRowCount ?? 0 },
|
||||||
|
() => 0,
|
||||||
|
).reduce((a, _, i) => a + getRowHeight(i, cellProps), 0)}px`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Grid
|
||||||
|
cellComponent={PinnedRightIntersectionCell}
|
||||||
|
cellProps={cellProps}
|
||||||
|
className={styles.noScrollbar}
|
||||||
|
columnCount={pinnedRightColumnCount}
|
||||||
|
columnWidth={(index, cellProps) => {
|
||||||
|
return typeof columnWidth === 'number'
|
||||||
|
? columnWidth
|
||||||
|
: columnWidth(
|
||||||
|
index + pinnedLeftColumnCount + totalColumnCount,
|
||||||
|
cellProps,
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
rowCount={pinnedRowCount}
|
||||||
|
rowHeight={getRowHeight}
|
||||||
|
/>
|
||||||
|
{enableHeader && (
|
||||||
|
<div className={styles.itemTablePinnedHeaderShadow} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div
|
||||||
|
className={styles.itemTablePinnedRightColumnsContainer}
|
||||||
|
ref={pinnedRightColumnRef}
|
||||||
|
>
|
||||||
|
<Grid
|
||||||
|
cellComponent={PinnedRightColumnCell}
|
||||||
|
cellProps={cellProps}
|
||||||
|
className={clsx(styles.noScrollbar, styles.height100)}
|
||||||
|
columnCount={pinnedRightColumnCount}
|
||||||
|
columnWidth={(index, cellProps) => {
|
||||||
|
return typeof columnWidth === 'number'
|
||||||
|
? columnWidth
|
||||||
|
: columnWidth(
|
||||||
|
index + pinnedLeftColumnCount + totalColumnCount,
|
||||||
|
cellProps,
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
rowCount={totalRowCount}
|
||||||
|
rowHeight={(index, cellProps) => {
|
||||||
|
return getRowHeight(index + (pinnedRowCount ?? 0), cellProps);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<AnimatePresence initial={false}>
|
<AnimatePresence initial={false}>
|
||||||
{hasExpanded && (
|
{hasExpanded && (
|
||||||
|
|||||||
Reference in New Issue
Block a user