mirror of
https://github.com/jeffvli/feishin.git
synced 2026-07-18 16:36:29 +02:00
fix album group height calculation on index reorder
This commit is contained in:
@@ -31,21 +31,21 @@ import { LibraryItem, Song } from '/@/shared/types/domain-types';
|
|||||||
import { Play } from '/@/shared/types/types';
|
import { Play } from '/@/shared/types/types';
|
||||||
|
|
||||||
interface AlbumGroupHeaderProps {
|
interface AlbumGroupHeaderProps {
|
||||||
|
groupKey?: string;
|
||||||
groupRowCount?: number;
|
groupRowCount?: number;
|
||||||
metadata: AlbumGroupMetadata;
|
metadata: AlbumGroupMetadata;
|
||||||
onPlay?: (playType: Play) => void;
|
onPlay?: (playType: Play) => void;
|
||||||
rowIndex?: number;
|
setAlbumGroupContentHeight?: (groupKey: string, height: number) => void;
|
||||||
setAlbumGroupContentHeight?: (rowIndex: number, height: number) => void;
|
|
||||||
size?: AlbumGroupTextSize;
|
size?: AlbumGroupTextSize;
|
||||||
song: Song | undefined;
|
song: Song | undefined;
|
||||||
storedContentHeight?: number;
|
storedContentHeight?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AlbumGroupHeader = ({
|
export const AlbumGroupHeader = ({
|
||||||
|
groupKey,
|
||||||
groupRowCount,
|
groupRowCount,
|
||||||
metadata,
|
metadata,
|
||||||
onPlay,
|
onPlay,
|
||||||
rowIndex,
|
|
||||||
setAlbumGroupContentHeight,
|
setAlbumGroupContentHeight,
|
||||||
size = 'normal',
|
size = 'normal',
|
||||||
song,
|
song,
|
||||||
@@ -55,7 +55,9 @@ export const AlbumGroupHeader = ({
|
|||||||
const albumGroupItems = useAlbumGroupItems();
|
const albumGroupItems = useAlbumGroupItems();
|
||||||
const showFavoriteRating = useAlbumGroupShowFavoriteRating();
|
const showFavoriteRating = useAlbumGroupShowFavoriteRating();
|
||||||
const [isHovered, setIsHovered] = useState(false);
|
const [isHovered, setIsHovered] = useState(false);
|
||||||
const [resolvedInfoHeight, setResolvedInfoHeight] = useState<number | undefined>();
|
const [resolved, setResolved] = useState<null | { forInfoHeight: number; height: number }>(
|
||||||
|
null,
|
||||||
|
);
|
||||||
const playButtonBehavior = usePlayButtonBehavior();
|
const playButtonBehavior = usePlayButtonBehavior();
|
||||||
const albumImageSize = useAlbumGroupImageSize();
|
const albumImageSize = useAlbumGroupImageSize();
|
||||||
const rowHeight = {
|
const rowHeight = {
|
||||||
@@ -87,6 +89,13 @@ export const AlbumGroupHeader = ({
|
|||||||
: groupRowCount * rowHeight
|
: groupRowCount * rowHeight
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
|
// Ignore resolved height from a previous (larger) group span so minHeight
|
||||||
|
// cannot keep scrollHeight measurement stuck after a split/shrink.
|
||||||
|
const resolvedInfoHeight =
|
||||||
|
resolved && infoHeight !== undefined && resolved.forInfoHeight === infoHeight
|
||||||
|
? resolved.height
|
||||||
|
: undefined;
|
||||||
|
|
||||||
const imageContainerStyle =
|
const imageContainerStyle =
|
||||||
albumImageSize > 0
|
albumImageSize > 0
|
||||||
? {
|
? {
|
||||||
@@ -108,12 +117,14 @@ export const AlbumGroupHeader = ({
|
|||||||
|
|
||||||
const measure = () => {
|
const measure = () => {
|
||||||
const contentHeight = infoEl.scrollHeight;
|
const contentHeight = infoEl.scrollHeight;
|
||||||
const resolved = Math.max(infoHeight ?? 0, contentHeight);
|
const resolvedHeight = Math.max(infoHeight ?? 0, contentHeight);
|
||||||
|
|
||||||
setResolvedInfoHeight(resolved);
|
if (infoHeight !== undefined) {
|
||||||
|
setResolved({ forInfoHeight: infoHeight, height: resolvedHeight });
|
||||||
|
}
|
||||||
|
|
||||||
if (rowIndex !== undefined && setAlbumGroupContentHeight) {
|
if (groupKey !== undefined && setAlbumGroupContentHeight) {
|
||||||
setAlbumGroupContentHeight(rowIndex, contentHeight);
|
setAlbumGroupContentHeight(groupKey, contentHeight);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -124,9 +135,10 @@ export const AlbumGroupHeader = ({
|
|||||||
|
|
||||||
return () => resizeObserver.disconnect();
|
return () => resizeObserver.disconnect();
|
||||||
}, [
|
}, [
|
||||||
|
groupKey,
|
||||||
|
groupRowCount,
|
||||||
infoHeight,
|
infoHeight,
|
||||||
metadataRows.length,
|
metadataRows.length,
|
||||||
rowIndex,
|
|
||||||
setAlbumGroupContentHeight,
|
setAlbumGroupContentHeight,
|
||||||
storedContentHeight,
|
storedContentHeight,
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { AlbumGroupHeader } from '/@/renderer/components/item-list/item-table-list/album-group-header';
|
import { AlbumGroupHeader } from '/@/renderer/components/item-list/item-table-list/album-group-header';
|
||||||
import { computeAlbumGroupMetadata } from '/@/renderer/components/item-list/item-table-list/album-group-metadata';
|
import { computeAlbumGroupMetadata } from '/@/renderer/components/item-list/item-table-list/album-group-metadata';
|
||||||
import {
|
import {
|
||||||
|
getAlbumGroupHeightKey,
|
||||||
isLastInAlbumGroup,
|
isLastInAlbumGroup,
|
||||||
ItemTableListInnerColumn,
|
ItemTableListInnerColumn,
|
||||||
TableColumnContainer,
|
TableColumnContainer,
|
||||||
@@ -89,6 +90,10 @@ export const AlbumGroupColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const metadata = computeAlbumGroupMetadata(groupSongs, groupRowCount, t);
|
const metadata = computeAlbumGroupMetadata(groupSongs, groupRowCount, t);
|
||||||
|
const groupHeightKey = getAlbumGroupHeightKey(item, groupRowCount);
|
||||||
|
const storedContentHeight = groupHeightKey
|
||||||
|
? props.albumGroupContentHeights?.get(groupHeightKey)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableColumnContainer
|
<TableColumnContainer
|
||||||
@@ -98,14 +103,14 @@ export const AlbumGroupColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
isDraggedOver={null}
|
isDraggedOver={null}
|
||||||
>
|
>
|
||||||
<AlbumGroupHeader
|
<AlbumGroupHeader
|
||||||
|
groupKey={groupHeightKey}
|
||||||
groupRowCount={groupRowCount}
|
groupRowCount={groupRowCount}
|
||||||
metadata={metadata}
|
metadata={metadata}
|
||||||
onPlay={handlePlay}
|
onPlay={handlePlay}
|
||||||
rowIndex={props.rowIndex}
|
|
||||||
setAlbumGroupContentHeight={props.setAlbumGroupContentHeight}
|
setAlbumGroupContentHeight={props.setAlbumGroupContentHeight}
|
||||||
size={props.size === 'default' ? 'normal' : props.size}
|
size={props.size === 'default' ? 'normal' : props.size}
|
||||||
song={item}
|
song={item}
|
||||||
storedContentHeight={props.albumGroupContentHeights?.get(props.rowIndex)}
|
storedContentHeight={storedContentHeight}
|
||||||
/>
|
/>
|
||||||
</TableColumnContainer>
|
</TableColumnContainer>
|
||||||
);
|
);
|
||||||
|
|||||||
+1
-1
@@ -19,7 +19,7 @@ export const useTableScrollToIndex = ({
|
|||||||
rowRef,
|
rowRef,
|
||||||
scrollCellProps,
|
scrollCellProps,
|
||||||
}: {
|
}: {
|
||||||
albumGroupContentHeights: Map<number, number>;
|
albumGroupContentHeights: Map<string, number>;
|
||||||
autoScrollToActiveRow: boolean;
|
autoScrollToActiveRow: boolean;
|
||||||
enableHeader: boolean;
|
enableHeader: boolean;
|
||||||
getRowHeight: (index: number, cellProps: TableItemProps) => number;
|
getRowHeight: (index: number, cellProps: TableItemProps) => number;
|
||||||
|
|||||||
@@ -384,6 +384,22 @@ export const ItemTableListColumn = memo(ItemTableListColumnBase, (prevProps, nex
|
|||||||
|
|
||||||
const NonMutedColumns = [TableColumn.TITLE, TableColumn.TITLE_ARTIST, TableColumn.TITLE_COMBINED];
|
const NonMutedColumns = [TableColumn.TITLE, TableColumn.TITLE_ARTIST, TableColumn.TITLE_COMBINED];
|
||||||
|
|
||||||
|
/** Stable key for album-group content heights (survives row moves; not row index). */
|
||||||
|
export function getAlbumGroupHeightKey(item: unknown, groupRowCount?: number): string | undefined {
|
||||||
|
if (!item || typeof item !== 'object') return undefined;
|
||||||
|
|
||||||
|
let itemKey: string | undefined;
|
||||||
|
if ('_uniqueId' in item && typeof (item as { _uniqueId?: unknown })._uniqueId === 'string') {
|
||||||
|
itemKey = (item as { _uniqueId: string })._uniqueId;
|
||||||
|
} else if ('id' in item && typeof (item as { id?: unknown }).id === 'string') {
|
||||||
|
itemKey = (item as { id: string }).id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!itemKey) return undefined;
|
||||||
|
if (groupRowCount === undefined) return itemKey;
|
||||||
|
return `${itemKey}:${groupRowCount}`;
|
||||||
|
}
|
||||||
|
|
||||||
// Counts how many consecutive rows belong to the same album group as `rowIndex`.
|
// Counts how many consecutive rows belong to the same album group as `rowIndex`.
|
||||||
export function getAlbumGroupRowCount(
|
export function getAlbumGroupRowCount(
|
||||||
rowIndex: number,
|
rowIndex: number,
|
||||||
@@ -550,7 +566,10 @@ function getAlbumGroupClampHeight(props: ItemTableListInnerColumn): null | numbe
|
|||||||
props.getRowItem,
|
props.getRowItem,
|
||||||
props.enableHeader,
|
props.enableHeader,
|
||||||
);
|
);
|
||||||
const contentHeight = props.albumGroupContentHeights?.get(groupStartRowIndex) ?? 0;
|
const groupStartItem = props.getRowItem?.(groupStartRowIndex);
|
||||||
|
const groupHeightKey = getAlbumGroupHeightKey(groupStartItem, groupRowCount);
|
||||||
|
const contentHeight =
|
||||||
|
(groupHeightKey ? props.albumGroupContentHeights?.get(groupHeightKey) : undefined) ?? 0;
|
||||||
const totalGroupHeight = getAlbumGroupSpanHeight(
|
const totalGroupHeight = getAlbumGroupSpanHeight(
|
||||||
groupRowCount,
|
groupRowCount,
|
||||||
baseHeight,
|
baseHeight,
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ import { useTablePaneSync } from '/@/renderer/components/item-list/item-table-li
|
|||||||
import { useTableRowModel } from '/@/renderer/components/item-list/item-table-list/hooks/use-table-row-model';
|
import { useTableRowModel } from '/@/renderer/components/item-list/item-table-list/hooks/use-table-row-model';
|
||||||
import { useTableScrollToIndex } from '/@/renderer/components/item-list/item-table-list/hooks/use-table-scroll-to-index';
|
import { useTableScrollToIndex } from '/@/renderer/components/item-list/item-table-list/hooks/use-table-scroll-to-index';
|
||||||
import {
|
import {
|
||||||
|
getAlbumGroupHeightKey,
|
||||||
getAlbumGroupRowCount,
|
getAlbumGroupRowCount,
|
||||||
getAlbumGroupSpanHeight,
|
getAlbumGroupSpanHeight,
|
||||||
getAlbumGroupStartRowIndex,
|
getAlbumGroupStartRowIndex,
|
||||||
@@ -176,7 +177,7 @@ const ItemTableScrollShadowRight = memo(function ItemTableScrollShadowRight({
|
|||||||
ItemTableScrollShadowRight.displayName = 'ItemTableScrollShadowRight';
|
ItemTableScrollShadowRight.displayName = 'ItemTableScrollShadowRight';
|
||||||
|
|
||||||
interface VirtualizedTableGridProps {
|
interface VirtualizedTableGridProps {
|
||||||
albumGroupContentHeights: Map<number, number>;
|
albumGroupContentHeights: Map<string, number>;
|
||||||
calculatedColumnWidths: number[];
|
calculatedColumnWidths: number[];
|
||||||
CellComponent: JSXElementConstructor<CellComponentProps<TableItemProps>>;
|
CellComponent: JSXElementConstructor<CellComponentProps<TableItemProps>>;
|
||||||
data: unknown[];
|
data: unknown[];
|
||||||
@@ -194,7 +195,7 @@ interface VirtualizedTableGridProps {
|
|||||||
pinnedRowCount: number;
|
pinnedRowCount: number;
|
||||||
pinnedRowRef: React.RefObject<HTMLDivElement | null>;
|
pinnedRowRef: React.RefObject<HTMLDivElement | null>;
|
||||||
scrollShadowStore: TableScrollShadowStore;
|
scrollShadowStore: TableScrollShadowStore;
|
||||||
setAlbumGroupContentHeight: (rowIndex: number, height: number) => void;
|
setAlbumGroupContentHeight: (groupKey: string, height: number) => void;
|
||||||
tableConfig: ItemTableListConfig;
|
tableConfig: ItemTableListConfig;
|
||||||
totalColumnCount: number;
|
totalColumnCount: number;
|
||||||
totalRowCount: number;
|
totalRowCount: number;
|
||||||
@@ -773,7 +774,7 @@ export interface TableGroupHeader {
|
|||||||
|
|
||||||
export interface TableItemProps {
|
export interface TableItemProps {
|
||||||
adjustedRowIndexMap?: Map<number, number>;
|
adjustedRowIndexMap?: Map<number, number>;
|
||||||
albumGroupContentHeights?: Map<number, number>;
|
albumGroupContentHeights?: Map<string, number>;
|
||||||
albumGroupImageSize?: number;
|
albumGroupImageSize?: number;
|
||||||
calculatedColumnWidths?: number[];
|
calculatedColumnWidths?: number[];
|
||||||
cellPadding?: ItemTableListProps['cellPadding'];
|
cellPadding?: ItemTableListProps['cellPadding'];
|
||||||
@@ -807,7 +808,7 @@ export interface TableItemProps {
|
|||||||
pinnedRightColumnWidths?: number[];
|
pinnedRightColumnWidths?: number[];
|
||||||
playerContext: PlayerContext;
|
playerContext: PlayerContext;
|
||||||
playlistId?: string;
|
playlistId?: string;
|
||||||
setAlbumGroupContentHeight?: (rowIndex: number, height: number) => void;
|
setAlbumGroupContentHeight?: (groupKey: string, height: number) => void;
|
||||||
size?: ItemTableListProps['size'];
|
size?: ItemTableListProps['size'];
|
||||||
startRowIndex?: number;
|
startRowIndex?: number;
|
||||||
tableId: string;
|
tableId: string;
|
||||||
@@ -1298,14 +1299,14 @@ const BaseItemTableList = ({
|
|||||||
const albumGroupImageSize = useAlbumGroupImageSize();
|
const albumGroupImageSize = useAlbumGroupImageSize();
|
||||||
const baseItemCount = itemCount ?? data.length;
|
const baseItemCount = itemCount ?? data.length;
|
||||||
const [albumGroupContentHeights, setAlbumGroupContentHeights] = useState(
|
const [albumGroupContentHeights, setAlbumGroupContentHeights] = useState(
|
||||||
() => new Map<number, number>(),
|
() => new Map<string, number>(),
|
||||||
);
|
);
|
||||||
|
|
||||||
const setAlbumGroupContentHeight = useCallback((rowIndex: number, height: number) => {
|
const setAlbumGroupContentHeight = useCallback((groupKey: string, height: number) => {
|
||||||
setAlbumGroupContentHeights((prev) => {
|
setAlbumGroupContentHeights((prev) => {
|
||||||
if (prev.get(rowIndex) === height) return prev;
|
if (prev.get(groupKey) === height) return prev;
|
||||||
const next = new Map(prev);
|
const next = new Map(prev);
|
||||||
next.set(rowIndex, height);
|
next.set(groupKey, height);
|
||||||
return next;
|
return next;
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
@@ -1460,8 +1461,12 @@ const BaseItemTableList = ({
|
|||||||
cellProps.getRowItem,
|
cellProps.getRowItem,
|
||||||
cellProps.enableHeader,
|
cellProps.enableHeader,
|
||||||
);
|
);
|
||||||
|
const groupStartItem = cellProps.getRowItem?.(groupStartRowIndex);
|
||||||
|
const groupHeightKey = getAlbumGroupHeightKey(groupStartItem, groupRowCount);
|
||||||
const contentHeight =
|
const contentHeight =
|
||||||
cellProps.albumGroupContentHeights?.get(groupStartRowIndex) ?? 0;
|
(groupHeightKey
|
||||||
|
? cellProps.albumGroupContentHeights?.get(groupHeightKey)
|
||||||
|
: undefined) ?? 0;
|
||||||
const totalGroupHeight = getAlbumGroupSpanHeight(
|
const totalGroupHeight = getAlbumGroupSpanHeight(
|
||||||
groupRowCount,
|
groupRowCount,
|
||||||
baseHeight,
|
baseHeight,
|
||||||
|
|||||||
Reference in New Issue
Block a user