mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-10 04:30:25 +02:00
add sticky disc group rows for album detail
This commit is contained in:
+177
@@ -0,0 +1,177 @@
|
|||||||
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
|
import { useWindowSettings } from '/@/renderer/store/settings.store';
|
||||||
|
import { Platform } from '/@/shared/types/types';
|
||||||
|
|
||||||
|
export interface GroupRowInfo {
|
||||||
|
groupIndex: number;
|
||||||
|
rowIndex: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useStickyTableGroupRows = ({
|
||||||
|
containerRef,
|
||||||
|
enabled,
|
||||||
|
getGroupRowHeight,
|
||||||
|
getRowHeight,
|
||||||
|
groups,
|
||||||
|
headerHeight,
|
||||||
|
mainGridRef,
|
||||||
|
shouldShowStickyHeader,
|
||||||
|
stickyHeaderTop,
|
||||||
|
}: {
|
||||||
|
containerRef: React.RefObject<HTMLDivElement | null>;
|
||||||
|
enabled: boolean;
|
||||||
|
getGroupRowHeight?: (groupIndex: number) => number;
|
||||||
|
getRowHeight: (index: number) => number;
|
||||||
|
groups?: Array<{ itemCount: number; rowHeight?: ((index: number) => number) | number }>;
|
||||||
|
headerHeight: number;
|
||||||
|
mainGridRef: React.RefObject<HTMLDivElement | null>;
|
||||||
|
shouldShowStickyHeader?: boolean;
|
||||||
|
stickyHeaderTop?: number;
|
||||||
|
}) => {
|
||||||
|
const { windowBarStyle } = useWindowSettings();
|
||||||
|
const [stickyGroupIndex, setStickyGroupIndex] = useState<null | number>(null);
|
||||||
|
|
||||||
|
const stickyTop = useMemo(() => {
|
||||||
|
// If sticky header is showing, position group row below it with 1px offset to avoid conflict
|
||||||
|
// Otherwise, use the base sticky position
|
||||||
|
if (shouldShowStickyHeader && stickyHeaderTop !== undefined) {
|
||||||
|
return stickyHeaderTop + headerHeight + 1;
|
||||||
|
}
|
||||||
|
return windowBarStyle === Platform.WINDOWS || windowBarStyle === Platform.MACOS ? 95 : 65;
|
||||||
|
}, [windowBarStyle, shouldShowStickyHeader, stickyHeaderTop, headerHeight]);
|
||||||
|
|
||||||
|
// Calculate group row indexes
|
||||||
|
const groupRowIndexes = useMemo(() => {
|
||||||
|
if (!groups || groups.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const indexes: GroupRowInfo[] = [];
|
||||||
|
let cumulativeDataIndex = 0;
|
||||||
|
const headerOffset = 1; // Assuming header is enabled
|
||||||
|
|
||||||
|
groups.forEach((group, groupIndex) => {
|
||||||
|
const groupHeaderIndex = headerOffset + cumulativeDataIndex + groupIndex;
|
||||||
|
indexes.push({
|
||||||
|
groupIndex,
|
||||||
|
rowIndex: groupHeaderIndex,
|
||||||
|
});
|
||||||
|
cumulativeDataIndex += group.itemCount;
|
||||||
|
});
|
||||||
|
|
||||||
|
return indexes;
|
||||||
|
}, [groups]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
!enabled ||
|
||||||
|
!groups ||
|
||||||
|
groups.length === 0 ||
|
||||||
|
!mainGridRef.current ||
|
||||||
|
!containerRef.current
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the actual scrollable grid element (first child of the container)
|
||||||
|
const mainGridContainer = mainGridRef.current;
|
||||||
|
const mainGrid = mainGridContainer.childNodes[0] as HTMLDivElement | null;
|
||||||
|
|
||||||
|
if (!mainGrid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateStickyGroup = () => {
|
||||||
|
const scrollTop = mainGrid.scrollTop || 0;
|
||||||
|
const containerRect = containerRef.current?.getBoundingClientRect();
|
||||||
|
|
||||||
|
if (!containerRect) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the sticky threshold position
|
||||||
|
// The sticky group row should appear when a group row scrolls past this position
|
||||||
|
// stickyTop already accounts for window bar style and sticky header offset
|
||||||
|
const containerTop = containerRect.top;
|
||||||
|
const baseStickyPosition = stickyTop; // Base position (window bar + sticky header if showing)
|
||||||
|
|
||||||
|
// Find which group row should be sticky
|
||||||
|
// We want to show the current group as soon as its row reaches the sticky position
|
||||||
|
// This way it updates "on scroll" when scrolling into a new group section
|
||||||
|
let targetGroupIndex: null | number = null;
|
||||||
|
|
||||||
|
// Iterate forward through groups to find which one is at or about to reach the sticky position
|
||||||
|
for (let i = 0; i < groupRowIndexes.length; i++) {
|
||||||
|
const { groupIndex, rowIndex } = groupRowIndexes[i];
|
||||||
|
|
||||||
|
// Calculate the top position of this group row relative to the grid scroll
|
||||||
|
let rowTop = headerHeight;
|
||||||
|
for (let r = 0; r < rowIndex; r++) {
|
||||||
|
rowTop += getRowHeight(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate where this row would be in the viewport (absolute position from top of viewport)
|
||||||
|
const rowViewportTop = containerTop + rowTop - scrollTop;
|
||||||
|
|
||||||
|
// Get the height of this group row to account for its own offset
|
||||||
|
const groupRowHeight = getGroupRowHeight ? getGroupRowHeight(groupIndex) : 40; // Default group row height
|
||||||
|
|
||||||
|
// Calculate the sticky position accounting for the sticky group row's own height
|
||||||
|
// Similar to how stickyTop accounts for sticky header height, we add the group row height
|
||||||
|
const stickyPosition = baseStickyPosition + groupRowHeight;
|
||||||
|
|
||||||
|
// Check if this group row has reached or is about to reach the sticky position
|
||||||
|
// The sticky group row appears at baseStickyPosition, but we check when the actual group row
|
||||||
|
// reaches baseStickyPosition + groupRowHeight to account for the sticky group row's own height
|
||||||
|
if (rowViewportTop <= stickyPosition) {
|
||||||
|
// This group has reached the sticky position, so show this group
|
||||||
|
targetGroupIndex = groupIndex;
|
||||||
|
// Don't break here - continue checking to see if a later group should replace it
|
||||||
|
} else {
|
||||||
|
// This group hasn't reached the sticky position yet
|
||||||
|
// If we already found a target group, keep it and stop
|
||||||
|
// Otherwise, no group should be sticky yet
|
||||||
|
if (targetGroupIndex !== null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setStickyGroupIndex((prev) => {
|
||||||
|
if (prev !== targetGroupIndex) {
|
||||||
|
return targetGroupIndex;
|
||||||
|
}
|
||||||
|
return prev;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
updateStickyGroup();
|
||||||
|
|
||||||
|
mainGrid.addEventListener('scroll', updateStickyGroup, { passive: true });
|
||||||
|
window.addEventListener('scroll', updateStickyGroup, true);
|
||||||
|
window.addEventListener('resize', updateStickyGroup);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
mainGrid.removeEventListener('scroll', updateStickyGroup);
|
||||||
|
window.removeEventListener('scroll', updateStickyGroup, true);
|
||||||
|
window.removeEventListener('resize', updateStickyGroup);
|
||||||
|
};
|
||||||
|
}, [
|
||||||
|
enabled,
|
||||||
|
groups,
|
||||||
|
groupRowIndexes,
|
||||||
|
mainGridRef,
|
||||||
|
containerRef,
|
||||||
|
getGroupRowHeight,
|
||||||
|
getRowHeight,
|
||||||
|
headerHeight,
|
||||||
|
stickyTop,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
shouldShowStickyGroupRow: stickyGroupIndex !== null,
|
||||||
|
stickyGroupIndex,
|
||||||
|
stickyTop,
|
||||||
|
};
|
||||||
|
};
|
||||||
+1
-1
@@ -4,7 +4,7 @@ import { RefObject, useMemo } from 'react';
|
|||||||
import { useWindowSettings } from '/@/renderer/store/settings.store';
|
import { useWindowSettings } from '/@/renderer/store/settings.store';
|
||||||
import { Platform } from '/@/shared/types/types';
|
import { Platform } from '/@/shared/types/types';
|
||||||
|
|
||||||
export const useFixedTableHeader = ({
|
export const useStickyTableHeader = ({
|
||||||
containerRef,
|
containerRef,
|
||||||
enabled,
|
enabled,
|
||||||
headerRef,
|
headerRef,
|
||||||
@@ -88,6 +88,33 @@
|
|||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sticky-group-row {
|
||||||
|
position: fixed;
|
||||||
|
z-index: 15;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
padding: 0 2rem;
|
||||||
|
margin: 0 -2rem;
|
||||||
|
pointer-events: none;
|
||||||
|
background-color: var(--theme-colors-background);
|
||||||
|
border-bottom: 1px solid var(--theme-colors-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sticky-group-row-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
width: 100%;
|
||||||
|
background-color: var(--theme-colors-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sticky-group-row-section {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
overflow: hidden;
|
||||||
|
pointer-events: auto;
|
||||||
|
background-color: var(--theme-colors-background);
|
||||||
|
}
|
||||||
|
|
||||||
.item-table-pinned-rows-grid-container.with-header::after {
|
.item-table-pinned-rows-grid-container.with-header::after {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import React, {
|
|||||||
} from 'react';
|
} from 'react';
|
||||||
import { type CellComponentProps, Grid } from 'react-window-v2';
|
import { type CellComponentProps, Grid } from 'react-window-v2';
|
||||||
|
|
||||||
import { useFixedTableHeader } from './hooks/use-fixed-table-header';
|
|
||||||
import styles from './item-table-list.module.css';
|
import styles from './item-table-list.module.css';
|
||||||
|
|
||||||
import { ExpandedListContainer } from '/@/renderer/components/item-list/expanded-list-container';
|
import { ExpandedListContainer } from '/@/renderer/components/item-list/expanded-list-container';
|
||||||
@@ -32,6 +31,8 @@ import {
|
|||||||
useItemListState,
|
useItemListState,
|
||||||
} from '/@/renderer/components/item-list/helpers/item-list-state';
|
} from '/@/renderer/components/item-list/helpers/item-list-state';
|
||||||
import { parseTableColumns } from '/@/renderer/components/item-list/helpers/parse-table-columns';
|
import { parseTableColumns } from '/@/renderer/components/item-list/helpers/parse-table-columns';
|
||||||
|
import { useStickyTableGroupRows } from '/@/renderer/components/item-list/item-table-list/hooks/use-sticky-table-group-rows';
|
||||||
|
import { useStickyTableHeader } from '/@/renderer/components/item-list/item-table-list/hooks/use-sticky-table-header';
|
||||||
import {
|
import {
|
||||||
ItemControls,
|
ItemControls,
|
||||||
ItemListHandle,
|
ItemListHandle,
|
||||||
@@ -620,6 +621,7 @@ interface ItemTableListProps {
|
|||||||
enableHorizontalBorders?: boolean;
|
enableHorizontalBorders?: boolean;
|
||||||
enableRowHoverHighlight?: boolean;
|
enableRowHoverHighlight?: boolean;
|
||||||
enableSelection?: boolean;
|
enableSelection?: boolean;
|
||||||
|
enableStickyGroupRows?: boolean;
|
||||||
enableStickyHeader?: boolean;
|
enableStickyHeader?: boolean;
|
||||||
enableVerticalBorders?: boolean;
|
enableVerticalBorders?: boolean;
|
||||||
getRowId?: ((item: unknown) => string) | string;
|
getRowId?: ((item: unknown) => string) | string;
|
||||||
@@ -658,6 +660,7 @@ export const ItemTableList = ({
|
|||||||
enableHorizontalBorders = false,
|
enableHorizontalBorders = false,
|
||||||
enableRowHoverHighlight = true,
|
enableRowHoverHighlight = true,
|
||||||
enableSelection = true,
|
enableSelection = true,
|
||||||
|
enableStickyGroupRows = false,
|
||||||
enableStickyHeader = false,
|
enableStickyHeader = false,
|
||||||
enableVerticalBorders = false,
|
enableVerticalBorders = false,
|
||||||
getRowId,
|
getRowId,
|
||||||
@@ -784,13 +787,14 @@ export const ItemTableList = ({
|
|||||||
const handleRef = useRef<ItemListHandle | null>(null);
|
const handleRef = useRef<ItemListHandle | null>(null);
|
||||||
const containerFocusRef = useRef<HTMLDivElement | null>(null);
|
const containerFocusRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
const { shouldShowStickyHeader, stickyTop } = useFixedTableHeader({
|
const { shouldShowStickyHeader, stickyTop } = useStickyTableHeader({
|
||||||
containerRef: containerFocusRef,
|
containerRef: containerFocusRef,
|
||||||
enabled: enableHeader && enableStickyHeader,
|
enabled: enableHeader && enableStickyHeader,
|
||||||
headerRef: pinnedRowRef,
|
headerRef: pinnedRowRef,
|
||||||
});
|
});
|
||||||
|
|
||||||
const stickyHeaderRef = useRef<HTMLDivElement | null>(null);
|
const stickyHeaderRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
const stickyGroupRowRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
// Sync scroll position and update position of sticky header
|
// Sync scroll position and update position of sticky header
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -1429,9 +1433,84 @@ export const ItemTableList = ({
|
|||||||
[enableHeader, headerHeight, rowHeight, pinnedRowCount, size, groups],
|
[enableHeader, headerHeight, rowHeight, pinnedRowCount, size, groups],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Create a wrapper for getRowHeight that doesn't require cellProps (for sticky group rows hook)
|
||||||
|
const getRowHeightWrapper = useCallback(
|
||||||
|
(index: number) => {
|
||||||
|
const height = size === 'compact' ? 40 : size === 'large' ? 88 : 64;
|
||||||
|
|
||||||
|
// Check if this row is a group header row and has a custom row height
|
||||||
|
if (groups && groups.length > 0) {
|
||||||
|
let cumulativeDataIndex = 0;
|
||||||
|
const headerOffset = enableHeader ? 1 : 0;
|
||||||
|
|
||||||
|
for (let groupIndex = 0; groupIndex < groups.length; groupIndex++) {
|
||||||
|
const group = groups[groupIndex];
|
||||||
|
const groupHeaderIndex = headerOffset + cumulativeDataIndex + groupIndex;
|
||||||
|
|
||||||
|
if (index === groupHeaderIndex) {
|
||||||
|
if (group.rowHeight !== undefined) {
|
||||||
|
const groupRowHeight =
|
||||||
|
typeof group.rowHeight === 'number'
|
||||||
|
? group.rowHeight
|
||||||
|
: group.rowHeight(index);
|
||||||
|
if (groupRowHeight !== undefined) {
|
||||||
|
return groupRowHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
cumulativeDataIndex += group.itemCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseHeight = typeof rowHeight === 'number' ? rowHeight : height;
|
||||||
|
|
||||||
|
// If enableHeader is true and this is the first sticky row, use fixed header height
|
||||||
|
if (enableHeader && index === 0 && pinnedRowCount > 0) {
|
||||||
|
return headerHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
return baseHeight;
|
||||||
|
},
|
||||||
|
[enableHeader, headerHeight, rowHeight, pinnedRowCount, size, groups],
|
||||||
|
);
|
||||||
|
|
||||||
|
const getGroupRowHeightWrapper = useCallback(
|
||||||
|
(groupIndex: number) => {
|
||||||
|
if (!groups || groupIndex < 0 || groupIndex >= groups.length) {
|
||||||
|
return 40;
|
||||||
|
}
|
||||||
|
|
||||||
|
const group = groups[groupIndex];
|
||||||
|
if (group.rowHeight !== undefined) {
|
||||||
|
return typeof group.rowHeight === 'number' ? group.rowHeight : group.rowHeight(0);
|
||||||
|
}
|
||||||
|
return 40;
|
||||||
|
},
|
||||||
|
[groups],
|
||||||
|
);
|
||||||
|
|
||||||
|
const {
|
||||||
|
shouldShowStickyGroupRow,
|
||||||
|
stickyGroupIndex,
|
||||||
|
stickyTop: stickyGroupTop,
|
||||||
|
} = useStickyTableGroupRows({
|
||||||
|
containerRef: containerFocusRef,
|
||||||
|
enabled: enableStickyGroupRows && !!groups && groups.length > 0,
|
||||||
|
getGroupRowHeight: getGroupRowHeightWrapper,
|
||||||
|
getRowHeight: getRowHeightWrapper,
|
||||||
|
groups,
|
||||||
|
headerHeight,
|
||||||
|
mainGridRef: rowRef,
|
||||||
|
shouldShowStickyHeader,
|
||||||
|
stickyHeaderTop: stickyTop,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Show sticky group row whenever it should be shown
|
||||||
|
const shouldRenderStickyGroupRow = shouldShowStickyGroupRow;
|
||||||
|
|
||||||
const getDataFn = useCallback(() => {
|
const getDataFn = useCallback(() => {
|
||||||
// Reconstruct data array with group headers inserted
|
|
||||||
// Groups are defined by itemCount, so we calculate indexes based on cumulative item counts
|
|
||||||
const result: (null | unknown)[] = enableHeader ? [null] : [];
|
const result: (null | unknown)[] = enableHeader ? [null] : [];
|
||||||
|
|
||||||
if (!groups || groups.length === 0) {
|
if (!groups || groups.length === 0) {
|
||||||
@@ -1446,7 +1525,6 @@ export const ItemTableList = ({
|
|||||||
const headerOffset = enableHeader ? 1 : 0;
|
const headerOffset = enableHeader ? 1 : 0;
|
||||||
|
|
||||||
groups.forEach((group, groupIndex) => {
|
groups.forEach((group, groupIndex) => {
|
||||||
// Group header appears before its items
|
|
||||||
// Index = header offset + cumulative data index + number of previous group headers
|
// Index = header offset + cumulative data index + number of previous group headers
|
||||||
const groupHeaderIndex = headerOffset + cumulativeDataIndex + groupIndex;
|
const groupHeaderIndex = headerOffset + cumulativeDataIndex + groupIndex;
|
||||||
groupIndexes.push(groupHeaderIndex);
|
groupIndexes.push(groupHeaderIndex);
|
||||||
@@ -1457,16 +1535,14 @@ export const ItemTableList = ({
|
|||||||
const startIndex = enableHeader ? 1 : 0;
|
const startIndex = enableHeader ? 1 : 0;
|
||||||
let groupHeaderCount = 0;
|
let groupHeaderCount = 0;
|
||||||
|
|
||||||
// Iterate through the expanded row space (data + group headers)
|
|
||||||
for (
|
for (
|
||||||
let rowIndex = startIndex;
|
let rowIndex = startIndex;
|
||||||
rowIndex < startIndex + data.length + groupIndexes.length;
|
rowIndex < startIndex + data.length + groupIndexes.length;
|
||||||
rowIndex++
|
rowIndex++
|
||||||
) {
|
) {
|
||||||
// Check if this row should have a group header
|
|
||||||
const expectedGroupIndex = groupIndexes[groupHeaderCount];
|
const expectedGroupIndex = groupIndexes[groupHeaderCount];
|
||||||
if (expectedGroupIndex !== undefined && rowIndex === expectedGroupIndex) {
|
if (expectedGroupIndex !== undefined && rowIndex === expectedGroupIndex) {
|
||||||
result.push(null); // Group header row
|
result.push(null);
|
||||||
groupHeaderCount++;
|
groupHeaderCount++;
|
||||||
} else if (dataIndex < data.length) {
|
} else if (dataIndex < data.length) {
|
||||||
result.push(data[dataIndex]);
|
result.push(data[dataIndex]);
|
||||||
@@ -1841,6 +1917,124 @@ export const ItemTableList = ({
|
|||||||
stickyHeaderItemProps,
|
stickyHeaderItemProps,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// Calculate group row height
|
||||||
|
const groupRowHeight = useMemo(() => {
|
||||||
|
if (stickyGroupIndex === null || !groups) {
|
||||||
|
return 40; // Default
|
||||||
|
}
|
||||||
|
|
||||||
|
const group = groups[stickyGroupIndex];
|
||||||
|
if (group.rowHeight !== undefined) {
|
||||||
|
return typeof group.rowHeight === 'number' ? group.rowHeight : group.rowHeight(0);
|
||||||
|
}
|
||||||
|
return 40; // Default group row height
|
||||||
|
}, [stickyGroupIndex, groups]);
|
||||||
|
|
||||||
|
const StickyGroupRow = useMemo(() => {
|
||||||
|
if (!shouldRenderStickyGroupRow || stickyGroupIndex === null || !groups) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const group = groups[stickyGroupIndex];
|
||||||
|
const originalData = data.filter((item) => item !== null);
|
||||||
|
let cumulativeDataIndex = 0;
|
||||||
|
for (let i = 0; i < stickyGroupIndex; i++) {
|
||||||
|
cumulativeDataIndex += groups[i].itemCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
const groupContent = group.render({
|
||||||
|
data: originalData,
|
||||||
|
groupIndex: stickyGroupIndex,
|
||||||
|
index: 0,
|
||||||
|
internalState,
|
||||||
|
startDataIndex: cumulativeDataIndex,
|
||||||
|
});
|
||||||
|
|
||||||
|
const pinnedLeftWidth = calculatedColumnWidths
|
||||||
|
.slice(0, pinnedLeftColumnCount)
|
||||||
|
.reduce((sum, width) => sum + width, 0);
|
||||||
|
const mainWidth = calculatedColumnWidths
|
||||||
|
.slice(pinnedLeftColumnCount, pinnedLeftColumnCount + totalColumnCount)
|
||||||
|
.reduce((sum, width) => sum + width, 0);
|
||||||
|
const pinnedRightWidth = calculatedColumnWidths
|
||||||
|
.slice(pinnedLeftColumnCount + totalColumnCount)
|
||||||
|
.reduce((sum, width) => sum + width, 0);
|
||||||
|
|
||||||
|
const totalTableWidth = calculatedColumnWidths.reduce((sum, width) => sum + width, 0);
|
||||||
|
|
||||||
|
// Calculate the actual sticky position accounting for sticky header
|
||||||
|
const actualStickyTop = stickyGroupTop;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={styles.stickyGroupRow}
|
||||||
|
ref={stickyGroupRowRef}
|
||||||
|
style={{
|
||||||
|
top: `${actualStickyTop}px`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className={styles.stickyGroupRowContent}>
|
||||||
|
{pinnedLeftColumnCount > 0 && (
|
||||||
|
<div
|
||||||
|
className={styles.stickyGroupRowSection}
|
||||||
|
style={{ width: `${pinnedLeftWidth}px` }}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
height: groupRowHeight,
|
||||||
|
width: `${pinnedLeftWidth}px`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{groupContent}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div
|
||||||
|
className={styles.stickyGroupRowSection}
|
||||||
|
style={{ width: `${mainWidth}px` }}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
height: groupRowHeight,
|
||||||
|
marginLeft: pinnedLeftWidth > 0 ? `-${pinnedLeftWidth}px` : 0,
|
||||||
|
width: `${totalTableWidth}px`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{groupContent}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{pinnedRightColumnCount > 0 && (
|
||||||
|
<div
|
||||||
|
className={styles.stickyGroupRowSection}
|
||||||
|
style={{ width: `${pinnedRightWidth}px` }}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
height: groupRowHeight,
|
||||||
|
width: `${pinnedRightWidth}px`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{groupContent}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}, [
|
||||||
|
shouldRenderStickyGroupRow,
|
||||||
|
stickyGroupIndex,
|
||||||
|
groups,
|
||||||
|
data,
|
||||||
|
internalState,
|
||||||
|
calculatedColumnWidths,
|
||||||
|
pinnedLeftColumnCount,
|
||||||
|
pinnedRightColumnCount,
|
||||||
|
totalColumnCount,
|
||||||
|
groupRowHeight,
|
||||||
|
stickyGroupTop,
|
||||||
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={styles.itemTableListContainer}
|
className={styles.itemTableListContainer}
|
||||||
@@ -1856,6 +2050,7 @@ export const ItemTableList = ({
|
|||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
>
|
>
|
||||||
{StickyHeader}
|
{StickyHeader}
|
||||||
|
{StickyGroupRow}
|
||||||
<VirtualizedTableGrid
|
<VirtualizedTableGrid
|
||||||
calculatedColumnWidths={calculatedColumnWidths}
|
calculatedColumnWidths={calculatedColumnWidths}
|
||||||
CellComponent={CellComponent}
|
CellComponent={CellComponent}
|
||||||
|
|||||||
@@ -405,14 +405,16 @@ const AlbumDetailSongsTable = ({ songs }: AlbumDetailSongsTableProps) => {
|
|||||||
<Checkbox
|
<Checkbox
|
||||||
checked={isAllSelected}
|
checked={isAllSelected}
|
||||||
indeterminate={isSomeSelected}
|
indeterminate={isSomeSelected}
|
||||||
|
label={
|
||||||
|
<Text size="sm">
|
||||||
|
{t('common.disc', { postProcess: 'sentenceCase' })}{' '}
|
||||||
|
{discGroup.discNumber}
|
||||||
|
{discGroup.discSubtitle && ` - ${discGroup.discSubtitle}`}
|
||||||
|
</Text>
|
||||||
|
}
|
||||||
onChange={handleCheckboxChange}
|
onChange={handleCheckboxChange}
|
||||||
size="xs"
|
size="xs"
|
||||||
/>
|
/>
|
||||||
<Text size="sm">
|
|
||||||
{t('common.disc', { postProcess: 'sentenceCase' })}{' '}
|
|
||||||
{discGroup.discNumber}
|
|
||||||
{discGroup.discSubtitle && ` - ${discGroup.discSubtitle}`}
|
|
||||||
</Text>
|
|
||||||
</Group>
|
</Group>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -437,6 +439,7 @@ const AlbumDetailSongsTable = ({ songs }: AlbumDetailSongsTableProps) => {
|
|||||||
enableHorizontalBorders={tableConfig.enableHorizontalBorders}
|
enableHorizontalBorders={tableConfig.enableHorizontalBorders}
|
||||||
enableRowHoverHighlight={tableConfig.enableRowHoverHighlight}
|
enableRowHoverHighlight={tableConfig.enableRowHoverHighlight}
|
||||||
enableSelection
|
enableSelection
|
||||||
|
enableStickyGroupRows
|
||||||
enableStickyHeader
|
enableStickyHeader
|
||||||
enableVerticalBorders={tableConfig.enableVerticalBorders}
|
enableVerticalBorders={tableConfig.enableVerticalBorders}
|
||||||
groups={groups}
|
groups={groups}
|
||||||
|
|||||||
Reference in New Issue
Block a user