mirror of
https://github.com/jeffvli/feishin.git
synced 2026-07-22 10:26:33 +02:00
clamp album group title, improve height calulation
This commit is contained in:
@@ -55,9 +55,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.album-title {
|
.album-title {
|
||||||
|
display: -webkit-box;
|
||||||
|
overflow: hidden;
|
||||||
|
-webkit-line-clamp: 3;
|
||||||
|
line-clamp: 3;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
overflow-wrap: anywhere;
|
overflow-wrap: anywhere;
|
||||||
white-space: normal;
|
white-space: normal;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
}
|
}
|
||||||
|
|
||||||
.metadata-row {
|
.metadata-row {
|
||||||
|
|||||||
@@ -123,7 +123,13 @@ export const AlbumGroupHeader = ({
|
|||||||
setResolved({ forInfoHeight: infoHeight, height: resolvedHeight });
|
setResolved({ forInfoHeight: infoHeight, height: resolvedHeight });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (groupKey !== undefined && setAlbumGroupContentHeight) {
|
// Only persist heights that exceed the image/row floor. Equal values
|
||||||
|
// still replaced the Map and re-rendered the virtualizer on mount.
|
||||||
|
if (
|
||||||
|
groupKey !== undefined &&
|
||||||
|
setAlbumGroupContentHeight &&
|
||||||
|
contentHeight > (infoHeight ?? 0)
|
||||||
|
) {
|
||||||
setAlbumGroupContentHeight(groupKey, contentHeight);
|
setAlbumGroupContentHeight(groupKey, contentHeight);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -135,6 +141,7 @@ export const AlbumGroupHeader = ({
|
|||||||
|
|
||||||
return () => resizeObserver.disconnect();
|
return () => resizeObserver.disconnect();
|
||||||
}, [
|
}, [
|
||||||
|
albumImageSize,
|
||||||
groupKey,
|
groupKey,
|
||||||
groupRowCount,
|
groupRowCount,
|
||||||
infoHeight,
|
infoHeight,
|
||||||
|
|||||||
@@ -384,6 +384,35 @@ 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 content-height estimate for album-group info (title + metadata + controls).
|
||||||
|
* Used by the virtualizer before a group header mounts/measures, so scrolling in
|
||||||
|
* new groups does not jump when measured height is written later.
|
||||||
|
* Keep in sync with album-group-header styles (title line-clamp, metadata xs, controls).
|
||||||
|
*/
|
||||||
|
export function estimateAlbumGroupContentHeight({
|
||||||
|
hasEnlargedImage,
|
||||||
|
metadataRowCount,
|
||||||
|
showControls,
|
||||||
|
}: {
|
||||||
|
hasEnlargedImage: boolean;
|
||||||
|
metadataRowCount: number;
|
||||||
|
showControls: boolean;
|
||||||
|
}): number {
|
||||||
|
const TITLE_LINE_HEIGHT = 20;
|
||||||
|
const TITLE_MAX_LINES = 3;
|
||||||
|
const METADATA_LINE_HEIGHT = 18;
|
||||||
|
const CONTROLS_HEIGHT = 26;
|
||||||
|
const PADDING_TOP = hasEnlargedImage ? 8 : 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
PADDING_TOP +
|
||||||
|
TITLE_LINE_HEIGHT * TITLE_MAX_LINES +
|
||||||
|
Math.max(0, metadataRowCount) * METADATA_LINE_HEIGHT +
|
||||||
|
(showControls ? CONTROLS_HEIGHT : 0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/** Stable key for album-group content heights (survives row moves; not row index). */
|
/** Stable key for album-group content heights (survives row moves; not row index). */
|
||||||
export function getAlbumGroupHeightKey(item: unknown, groupRowCount?: number): string | undefined {
|
export function getAlbumGroupHeightKey(item: unknown, groupRowCount?: number): string | undefined {
|
||||||
if (!item || typeof item !== 'object') return undefined;
|
if (!item || typeof item !== 'object') return undefined;
|
||||||
@@ -568,8 +597,10 @@ function getAlbumGroupClampHeight(props: ItemTableListInnerColumn): null | numbe
|
|||||||
);
|
);
|
||||||
const groupStartItem = props.getRowItem?.(groupStartRowIndex);
|
const groupStartItem = props.getRowItem?.(groupStartRowIndex);
|
||||||
const groupHeightKey = getAlbumGroupHeightKey(groupStartItem, groupRowCount);
|
const groupHeightKey = getAlbumGroupHeightKey(groupStartItem, groupRowCount);
|
||||||
const contentHeight =
|
const measuredContentHeight = groupHeightKey
|
||||||
(groupHeightKey ? props.albumGroupContentHeights?.get(groupHeightKey) : undefined) ?? 0;
|
? props.albumGroupContentHeights?.get(groupHeightKey)
|
||||||
|
: undefined;
|
||||||
|
const contentHeight = measuredContentHeight ?? props.estimatedAlbumGroupContentHeight ?? 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 {
|
||||||
|
estimateAlbumGroupContentHeight,
|
||||||
getAlbumGroupHeightKey,
|
getAlbumGroupHeightKey,
|
||||||
getAlbumGroupRowCount,
|
getAlbumGroupRowCount,
|
||||||
getAlbumGroupSpanHeight,
|
getAlbumGroupSpanHeight,
|
||||||
@@ -73,7 +74,12 @@ import {
|
|||||||
ItemTableListColumnConfig,
|
ItemTableListColumnConfig,
|
||||||
} from '/@/renderer/components/item-list/types';
|
} from '/@/renderer/components/item-list/types';
|
||||||
import { PlayerContext, usePlayer } from '/@/renderer/features/player/context/player-context';
|
import { PlayerContext, usePlayer } from '/@/renderer/features/player/context/player-context';
|
||||||
import { useAlbumGroupImageSize, usePlayerStore } from '/@/renderer/store';
|
import {
|
||||||
|
useAlbumGroupImageSize,
|
||||||
|
useAlbumGroupItems,
|
||||||
|
useAlbumGroupShowFavoriteRating,
|
||||||
|
usePlayerStore,
|
||||||
|
} from '/@/renderer/store';
|
||||||
import { animationProps } from '/@/shared/components/animations/animation-props';
|
import { animationProps } from '/@/shared/components/animations/animation-props';
|
||||||
import { useFocusWithin } from '/@/shared/hooks/use-focus-within';
|
import { useFocusWithin } from '/@/shared/hooks/use-focus-within';
|
||||||
import { useMergedRef } from '/@/shared/hooks/use-merged-ref';
|
import { useMergedRef } from '/@/shared/hooks/use-merged-ref';
|
||||||
@@ -183,6 +189,7 @@ interface VirtualizedTableGridProps {
|
|||||||
data: unknown[];
|
data: unknown[];
|
||||||
dataWithGroups: (null | unknown)[];
|
dataWithGroups: (null | unknown)[];
|
||||||
enableScrollShadow: boolean;
|
enableScrollShadow: boolean;
|
||||||
|
estimatedAlbumGroupContentHeight: number;
|
||||||
getItem?: (index: number) => undefined | unknown;
|
getItem?: (index: number) => undefined | unknown;
|
||||||
headerHeight: number;
|
headerHeight: number;
|
||||||
mergedRowRef: React.Ref<HTMLDivElement>;
|
mergedRowRef: React.Ref<HTMLDivElement>;
|
||||||
@@ -208,6 +215,7 @@ const VirtualizedTableGrid = ({
|
|||||||
data,
|
data,
|
||||||
dataWithGroups,
|
dataWithGroups,
|
||||||
enableScrollShadow,
|
enableScrollShadow,
|
||||||
|
estimatedAlbumGroupContentHeight,
|
||||||
getItem,
|
getItem,
|
||||||
headerHeight,
|
headerHeight,
|
||||||
mergedRowRef,
|
mergedRowRef,
|
||||||
@@ -403,6 +411,7 @@ const VirtualizedTableGrid = ({
|
|||||||
enableRowHoverHighlight: tableConfig.enableRowHoverHighlight,
|
enableRowHoverHighlight: tableConfig.enableRowHoverHighlight,
|
||||||
enableSelection: tableConfig.enableSelection,
|
enableSelection: tableConfig.enableSelection,
|
||||||
enableVerticalBorders: tableConfig.enableVerticalBorders,
|
enableVerticalBorders: tableConfig.enableVerticalBorders,
|
||||||
|
estimatedAlbumGroupContentHeight,
|
||||||
getAdjustedRowIndex,
|
getAdjustedRowIndex,
|
||||||
getGroupRenderData,
|
getGroupRenderData,
|
||||||
getRowHeight: tableConfig.getRowHeight,
|
getRowHeight: tableConfig.getRowHeight,
|
||||||
@@ -428,6 +437,7 @@ const VirtualizedTableGrid = ({
|
|||||||
albumGroupImageSize,
|
albumGroupImageSize,
|
||||||
calculatedColumnWidths,
|
calculatedColumnWidths,
|
||||||
dataWithGroups,
|
dataWithGroups,
|
||||||
|
estimatedAlbumGroupContentHeight,
|
||||||
getAdjustedRowIndex,
|
getAdjustedRowIndex,
|
||||||
getGroupRenderData,
|
getGroupRenderData,
|
||||||
getRowItem,
|
getRowItem,
|
||||||
@@ -736,6 +746,8 @@ const MemoizedVirtualizedTableGrid = memo(VirtualizedTableGrid, (prevProps, next
|
|||||||
nextProps.calculatedColumnWidths,
|
nextProps.calculatedColumnWidths,
|
||||||
) &&
|
) &&
|
||||||
prevProps.albumGroupContentHeights === nextProps.albumGroupContentHeights &&
|
prevProps.albumGroupContentHeights === nextProps.albumGroupContentHeights &&
|
||||||
|
prevProps.estimatedAlbumGroupContentHeight ===
|
||||||
|
nextProps.estimatedAlbumGroupContentHeight &&
|
||||||
prevProps.setAlbumGroupContentHeight === nextProps.setAlbumGroupContentHeight &&
|
prevProps.setAlbumGroupContentHeight === nextProps.setAlbumGroupContentHeight &&
|
||||||
prevProps.tableConfig === nextProps.tableConfig &&
|
prevProps.tableConfig === nextProps.tableConfig &&
|
||||||
prevProps.data === nextProps.data &&
|
prevProps.data === nextProps.data &&
|
||||||
@@ -776,6 +788,7 @@ export interface TableItemProps {
|
|||||||
adjustedRowIndexMap?: Map<number, number>;
|
adjustedRowIndexMap?: Map<number, number>;
|
||||||
albumGroupContentHeights?: Map<string, number>;
|
albumGroupContentHeights?: Map<string, number>;
|
||||||
albumGroupImageSize?: number;
|
albumGroupImageSize?: number;
|
||||||
|
estimatedAlbumGroupContentHeight?: number;
|
||||||
calculatedColumnWidths?: number[];
|
calculatedColumnWidths?: number[];
|
||||||
cellPadding?: ItemTableListProps['cellPadding'];
|
cellPadding?: ItemTableListProps['cellPadding'];
|
||||||
columns: ItemTableListColumnConfig[];
|
columns: ItemTableListColumnConfig[];
|
||||||
@@ -1297,6 +1310,21 @@ const BaseItemTableList = ({
|
|||||||
const { playlistId: routePlaylistId } = useParams() as { playlistId?: string };
|
const { playlistId: routePlaylistId } = useParams() as { playlistId?: string };
|
||||||
const tableId = useId();
|
const tableId = useId();
|
||||||
const albumGroupImageSize = useAlbumGroupImageSize();
|
const albumGroupImageSize = useAlbumGroupImageSize();
|
||||||
|
const albumGroupItems = useAlbumGroupItems();
|
||||||
|
const albumGroupShowFavoriteRating = useAlbumGroupShowFavoriteRating();
|
||||||
|
const albumGroupMetadataRowCount = useMemo(
|
||||||
|
() => albumGroupItems.filter((item) => !item.disabled).length,
|
||||||
|
[albumGroupItems],
|
||||||
|
);
|
||||||
|
const estimatedAlbumGroupContentHeight = useMemo(
|
||||||
|
() =>
|
||||||
|
estimateAlbumGroupContentHeight({
|
||||||
|
hasEnlargedImage: (albumGroupImageSize || 96) > 0,
|
||||||
|
metadataRowCount: albumGroupMetadataRowCount,
|
||||||
|
showControls: albumGroupShowFavoriteRating,
|
||||||
|
}),
|
||||||
|
[albumGroupImageSize, albumGroupMetadataRowCount, albumGroupShowFavoriteRating],
|
||||||
|
);
|
||||||
const baseItemCount = itemCount ?? data.length;
|
const baseItemCount = itemCount ?? data.length;
|
||||||
const [albumGroupContentHeights, setAlbumGroupContentHeights] = useState(
|
const [albumGroupContentHeights, setAlbumGroupContentHeights] = useState(
|
||||||
() => new Map<string, number>(),
|
() => new Map<string, number>(),
|
||||||
@@ -1463,10 +1491,12 @@ const BaseItemTableList = ({
|
|||||||
);
|
);
|
||||||
const groupStartItem = cellProps.getRowItem?.(groupStartRowIndex);
|
const groupStartItem = cellProps.getRowItem?.(groupStartRowIndex);
|
||||||
const groupHeightKey = getAlbumGroupHeightKey(groupStartItem, groupRowCount);
|
const groupHeightKey = getAlbumGroupHeightKey(groupStartItem, groupRowCount);
|
||||||
const contentHeight =
|
const measuredContentHeight = groupHeightKey
|
||||||
(groupHeightKey
|
? cellProps.albumGroupContentHeights?.get(groupHeightKey)
|
||||||
? cellProps.albumGroupContentHeights?.get(groupHeightKey)
|
: undefined;
|
||||||
: undefined) ?? 0;
|
// Prefer measured height when present; otherwise reserve with a stable
|
||||||
|
// estimate so newly virtualized groups do not jump after mount measure.
|
||||||
|
const contentHeight = measuredContentHeight ?? estimatedAlbumGroupContentHeight;
|
||||||
const totalGroupHeight = getAlbumGroupSpanHeight(
|
const totalGroupHeight = getAlbumGroupSpanHeight(
|
||||||
groupRowCount,
|
groupRowCount,
|
||||||
baseHeight,
|
baseHeight,
|
||||||
@@ -1482,7 +1512,15 @@ const BaseItemTableList = ({
|
|||||||
|
|
||||||
return baseHeight;
|
return baseHeight;
|
||||||
},
|
},
|
||||||
[albumGroupImageSize, enableHeader, headerHeight, rowHeight, pinnedRowCount, size],
|
[
|
||||||
|
albumGroupImageSize,
|
||||||
|
enableHeader,
|
||||||
|
estimatedAlbumGroupContentHeight,
|
||||||
|
headerHeight,
|
||||||
|
rowHeight,
|
||||||
|
pinnedRowCount,
|
||||||
|
size,
|
||||||
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
const scrollCellProps = useMemo<TableItemProps>(
|
const scrollCellProps = useMemo<TableItemProps>(
|
||||||
@@ -1500,6 +1538,7 @@ const BaseItemTableList = ({
|
|||||||
enableRowHoverHighlight,
|
enableRowHoverHighlight,
|
||||||
enableSelection,
|
enableSelection,
|
||||||
enableVerticalBorders,
|
enableVerticalBorders,
|
||||||
|
estimatedAlbumGroupContentHeight,
|
||||||
getRowHeight,
|
getRowHeight,
|
||||||
getRowItem: (rowIndex: number) => {
|
getRowItem: (rowIndex: number) => {
|
||||||
if (shouldUseAccessor && getItem) {
|
if (shouldUseAccessor && getItem) {
|
||||||
@@ -1532,6 +1571,7 @@ const BaseItemTableList = ({
|
|||||||
enableRowHoverHighlight,
|
enableRowHoverHighlight,
|
||||||
enableSelection,
|
enableSelection,
|
||||||
enableVerticalBorders,
|
enableVerticalBorders,
|
||||||
|
estimatedAlbumGroupContentHeight,
|
||||||
getItem,
|
getItem,
|
||||||
getRowHeight,
|
getRowHeight,
|
||||||
hasAlbumGroupColumn,
|
hasAlbumGroupColumn,
|
||||||
@@ -1887,6 +1927,7 @@ const BaseItemTableList = ({
|
|||||||
data={data}
|
data={data}
|
||||||
dataWithGroups={dataWithGroups}
|
dataWithGroups={dataWithGroups}
|
||||||
enableScrollShadow={enableScrollShadow}
|
enableScrollShadow={enableScrollShadow}
|
||||||
|
estimatedAlbumGroupContentHeight={estimatedAlbumGroupContentHeight}
|
||||||
getItem={getItem}
|
getItem={getItem}
|
||||||
headerHeight={headerHeight}
|
headerHeight={headerHeight}
|
||||||
mergedRowRef={mergedRowRef}
|
mergedRowRef={mergedRowRef}
|
||||||
|
|||||||
Reference in New Issue
Block a user