refactor item table props

This commit is contained in:
jeffvli
2026-04-04 12:34:27 -07:00
parent 1592204515
commit 141a20f042
8 changed files with 395 additions and 458 deletions
@@ -20,7 +20,8 @@ export const createColumnCellComponent = (
prevProps.columnIndex === nextProps.columnIndex && prevProps.columnIndex === nextProps.columnIndex &&
prevProps.data === nextProps.data && prevProps.data === nextProps.data &&
prevProps.style === nextProps.style && prevProps.style === nextProps.style &&
prevProps.columns === nextProps.columns prevProps.columns === nextProps.columns &&
prevProps.playlistId === nextProps.playlistId
); );
}, },
); );
@@ -18,10 +18,15 @@ const DateColumnBase = (props: ItemTableListInnerColumn) => {
const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex]; const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
const row: string | undefined = (rowItem as any)?.[props.columns[props.columnIndex].id]; const row: string | undefined = (rowItem as any)?.[props.columns[props.columnIndex].id];
if (typeof row === 'string' && row) { const formattedAbsolute = useMemo(
() => (typeof row === 'string' && row ? formatDateAbsolute(row) : null),
[row],
);
if (formattedAbsolute) {
return ( return (
<TableColumnTextContainer {...props}> <TableColumnTextContainer {...props}>
<span>{formatDateAbsolute(row)}</span> <span>{formattedAbsolute}</span>
</TableColumnTextContainer> </TableColumnTextContainer>
); );
} }
@@ -63,6 +68,11 @@ const AbsoluteDateColumnBase = (props: ItemTableListInnerColumn) => {
return null; return null;
}, [props.type, rowItem]); }, [props.type, rowItem]);
const formattedIsoFallback = useMemo(
() => (typeof row === 'string' && row ? formatPartialIsoDateUTC(row) : null),
[row],
);
if (props.type === TableColumn.RELEASE_DATE) { if (props.type === TableColumn.RELEASE_DATE) {
if (releaseDateContent) { if (releaseDateContent) {
return ( return (
@@ -72,10 +82,10 @@ const AbsoluteDateColumnBase = (props: ItemTableListInnerColumn) => {
); );
} }
if (typeof row === 'string' && row) { if (formattedIsoFallback) {
return ( return (
<TableColumnTextContainer {...props}> <TableColumnTextContainer {...props}>
<span>{formatPartialIsoDateUTC(row)}</span> <span>{formattedIsoFallback}</span>
</TableColumnTextContainer> </TableColumnTextContainer>
); );
} }
@@ -96,10 +106,15 @@ const RelativeDateColumnBase = (props: ItemTableListInnerColumn) => {
const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex]; const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
const row: string | undefined = (rowItem as any)?.[props.columns[props.columnIndex].id]; const row: string | undefined = (rowItem as any)?.[props.columns[props.columnIndex].id];
if (typeof row === 'string') { const formattedRelative = useMemo(() => {
if (typeof row !== 'string') return null;
return formatDateRelative(row);
}, [row]);
if (formattedRelative !== null) {
return ( return (
<TableColumnTextContainer {...props}> <TableColumnTextContainer {...props}>
<span>{formatDateRelative(row)}</span> <span>{formattedRelative}</span>
</TableColumnTextContainer> </TableColumnTextContainer>
); );
} }
@@ -1,4 +1,5 @@
import clsx from 'clsx'; import clsx from 'clsx';
import { useMemo } from 'react';
import { Link } from 'react-router'; import { Link } from 'react-router';
import styles from './title-column.module.css'; import styles from './title-column.module.css';
@@ -35,8 +36,12 @@ function DefaultTitleColumn(props: ItemTableListInnerColumn) {
const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex]; const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
const row: string | undefined = rowItem?.[props.columns[props.columnIndex].id]; const row: string | undefined = rowItem?.[props.columns[props.columnIndex].id];
const path = useMemo(() => {
if (typeof row !== 'string' || !rowItem || !(rowItem as any).id) return undefined;
return getTitlePath(props.itemType, (rowItem as any).id as string);
}, [props.itemType, row, rowItem]);
if (typeof row === 'string') { if (typeof row === 'string') {
const path = getTitlePath(props.itemType, (rowItem as any).id as string);
const item = rowItem as any; const item = rowItem as any;
const titleLinkProps = path const titleLinkProps = path
@@ -80,8 +85,12 @@ function QueueSongTitleColumn(props: ItemTableListInnerColumn) {
const song = rowItem as QueueSong; const song = rowItem as QueueSong;
const isActive = useIsActiveRow(song?.id, song?._uniqueId); const isActive = useIsActiveRow(song?.id, song?._uniqueId);
const path = useMemo(() => {
if (typeof row !== 'string' || !rowItem || !(rowItem as any).id) return undefined;
return getTitlePath(props.itemType, (rowItem as any).id as string);
}, [props.itemType, row, rowItem]);
if (typeof row === 'string') { if (typeof row === 'string') {
const path = getTitlePath(props.itemType, (rowItem as any).id as string);
const item = rowItem as any; const item = rowItem as any;
const titleLinkProps = path const titleLinkProps = path
@@ -34,12 +34,17 @@ export const useItemDragDropState = <TElement extends HTMLElement = HTMLDivEleme
}: UseItemDragDropStateProps): DragDropState<TElement> => { }: UseItemDragDropStateProps): DragDropState<TElement> => {
const shouldEnableDrag = enableDrag && isDataRow && !!item; const shouldEnableDrag = enableDrag && isDataRow && !!item;
const needsDropRegistration =
shouldEnableDrag &&
(itemType === LibraryItem.QUEUE_SONG || itemType === LibraryItem.PLAYLIST_SONG);
const { const {
isDraggedOver, isDraggedOver,
isDragging: isDraggingLocal, isDragging: isDraggingLocal,
ref: dragRef, ref: dragRef,
} = useDragDrop<TElement>({ } = useDragDrop<TElement>({
drag: { drag: shouldEnableDrag
? {
getId: () => { getId: () => {
if (!item || !isDataRow) { if (!item || !isDataRow) {
return []; return [];
@@ -81,8 +86,10 @@ export const useItemDragDropState = <TElement extends HTMLElement = HTMLDivEleme
? [DragOperation.REORDER, DragOperation.ADD] ? [DragOperation.REORDER, DragOperation.ADD]
: [DragOperation.ADD], : [DragOperation.ADD],
target: DragTargetMap[itemType] || DragTarget.GENERIC, target: DragTargetMap[itemType] || DragTarget.GENERIC,
}, }
drop: { : undefined,
drop: needsDropRegistration
? {
canDrop: (args) => { canDrop: (args) => {
if (args.source.type === DragTarget.TABLE_COLUMN) { if (args.source.type === DragTarget.TABLE_COLUMN) {
return false; return false;
@@ -167,10 +174,14 @@ export const useItemDragDropState = <TElement extends HTMLElement = HTMLDivEleme
songs: Song[]; songs: Song[];
}>( }>(
(acc, item) => { (acc, item) => {
if ((item as unknown as Song)._itemType === LibraryItem.SONG) { if (
(item as unknown as Song)._itemType ===
LibraryItem.SONG
) {
acc.songs.push(item as unknown as Song); acc.songs.push(item as unknown as Song);
} else if ( } else if (
(item as unknown as Folder)._itemType === LibraryItem.FOLDER (item as unknown as Folder)._itemType ===
LibraryItem.FOLDER
) { ) {
acc.folders.push(item as unknown as Folder); acc.folders.push(item as unknown as Folder);
} }
@@ -283,7 +294,8 @@ export const useItemDragDropState = <TElement extends HTMLElement = HTMLDivEleme
return; return;
}, },
}, }
: undefined,
isEnabled: shouldEnableDrag, isEnabled: shouldEnableDrag,
}); });
@@ -19,7 +19,6 @@ import React, {
useRef, useRef,
useState, useState,
} from 'react'; } from 'react';
import { useParams } from 'react-router';
import { CellComponentProps } from 'react-window-v2'; import { CellComponentProps } from 'react-window-v2';
import styles from './item-table-list-column.module.css'; import styles from './item-table-list-column.module.css';
@@ -82,7 +81,6 @@ export interface ItemTableListInnerColumn extends ItemTableListColumn {
} }
const ItemTableListColumnBase = (props: ItemTableListColumn) => { const ItemTableListColumnBase = (props: ItemTableListColumn) => {
const { playlistId } = useParams() as { playlistId?: string };
const type = props.columnType ?? (props.columns[props.columnIndex].id as TableColumn); const type = props.columnType ?? (props.columns[props.columnIndex].id as TableColumn);
const isHeaderEnabled = !!props.enableHeader; const isHeaderEnabled = !!props.enableHeader;
@@ -135,7 +133,7 @@ const ItemTableListColumnBase = (props: ItemTableListColumn) => {
item, item,
itemType: props.itemType, itemType: props.itemType,
playerContext: props.playerContext, playerContext: props.playerContext,
playlistId, playlistId: props.playlistId,
}); });
const controls = props.controls; const controls = props.controls;
@@ -362,6 +360,7 @@ export const ItemTableListColumn = memo(ItemTableListColumnBase, (prevProps, nex
prevProps.enableColumnResize === nextProps.enableColumnResize && prevProps.enableColumnResize === nextProps.enableColumnResize &&
prevProps.enableColumnReorder === nextProps.enableColumnReorder && prevProps.enableColumnReorder === nextProps.enableColumnReorder &&
prevProps.cellPadding === nextProps.cellPadding && prevProps.cellPadding === nextProps.cellPadding &&
prevProps.playlistId === nextProps.playlistId &&
prevItem === nextItem prevItem === nextItem
); );
}); });
@@ -1,31 +1,51 @@
import type { ReactElement } from 'react';
import React, { createContext, useContext, useEffect, useMemo, useRef } from 'react'; import React, { createContext, useContext, useEffect, useMemo, useRef } from 'react';
import { useSyncExternalStore } from 'react'; import { useSyncExternalStore } from 'react';
import type { TableItemProps } from './item-table-list';
import { ItemListStateActions } from '/@/renderer/components/item-list/helpers/item-list-state'; import { ItemListStateActions } from '/@/renderer/components/item-list/helpers/item-list-state';
import { ItemControls, ItemTableListColumnConfig } from '/@/renderer/components/item-list/types'; import { ItemControls, ItemTableListColumnConfig } from '/@/renderer/components/item-list/types';
import { PlayerContext } from '/@/renderer/features/player/context/player-context'; import { PlayerContext } from '/@/renderer/features/player/context/player-context';
import { LibraryItem } from '/@/shared/types/domain-types'; import { LibraryItem } from '/@/shared/types/domain-types';
/**
* Stage A/B: Provide table-scoped config + external stores so churny values can update
* without forcing `cellProps` identity changes (and therefore without rerendering every visible cell).
*/
export type ItemTableListConfig = { export type ItemTableListConfig = {
cellPadding: 'lg' | 'md' | 'sm' | 'xl' | 'xs'; cellPadding: 'lg' | 'md' | 'sm' | 'xl' | 'xs';
columns: ItemTableListColumnConfig[]; columns: ItemTableListColumnConfig[];
controls: ItemControls; controls: ItemControls;
enableAlternateRowColors: boolean;
enableColumnReorder: boolean;
enableColumnResize: boolean;
enableDrag: boolean;
enableExpansion: boolean;
enableHeader: boolean; enableHeader: boolean;
enableHorizontalBorders: boolean;
enableRowHoverHighlight: boolean; enableRowHoverHighlight: boolean;
enableSelection: boolean; enableSelection: boolean;
enableVerticalBorders: boolean;
getRowHeight: (index: number, cellProps: TableItemProps) => number;
groups?: ItemTableListGroupHeader[];
internalState: ItemListStateActions; internalState: ItemListStateActions;
itemType: LibraryItem; itemType: LibraryItem;
playerContext: PlayerContext; playerContext: PlayerContext;
playlistId?: string;
size: 'compact' | 'default' | 'large'; size: 'compact' | 'default' | 'large';
startRowIndex?: number; startRowIndex?: number;
tableId: string; tableId: string;
}; };
export type ItemTableListGroupHeader = {
itemCount: number;
render: (props: {
data: unknown[];
groupIndex: number;
index: number;
internalState: ItemListStateActions;
startDataIndex: number;
}) => ReactElement;
};
const ItemTableListConfigContext = createContext<ItemTableListConfig | null>(null); const ItemTableListConfigContext = createContext<ItemTableListConfig | null>(null);
export const ItemTableListConfigProvider = ({ export const ItemTableListConfigProvider = ({
@@ -15,6 +15,7 @@ import React, {
useRef, useRef,
useState, useState,
} from 'react'; } from 'react';
import { useParams } from 'react-router';
import { type CellComponentProps, Grid } from 'react-window-v2'; import { type CellComponentProps, Grid } from 'react-window-v2';
import styles from './item-table-list.module.css'; import styles from './item-table-list.module.css';
@@ -43,6 +44,7 @@ import { useTableRowModel } from '/@/renderer/components/item-list/item-table-li
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 { ItemTableListColumn } from '/@/renderer/components/item-list/item-table-list/item-table-list-column'; import { ItemTableListColumn } from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
import { import {
type ItemTableListConfig,
ItemTableListConfigProvider, ItemTableListConfigProvider,
ItemTableListStoreProvider, ItemTableListStoreProvider,
} from '/@/renderer/components/item-list/item-table-list/item-table-list-context'; } from '/@/renderer/components/item-list/item-table-list/item-table-list-context';
@@ -104,27 +106,11 @@ export enum TableItemSize {
interface VirtualizedTableGridProps { interface VirtualizedTableGridProps {
calculatedColumnWidths: number[]; calculatedColumnWidths: number[];
CellComponent: JSXElementConstructor<CellComponentProps<TableItemProps>>; CellComponent: JSXElementConstructor<CellComponentProps<TableItemProps>>;
cellPadding: 'lg' | 'md' | 'sm' | 'xl' | 'xs';
controls: ItemControls;
data: unknown[]; data: unknown[];
dataWithGroups: (null | unknown)[]; dataWithGroups: (null | unknown)[];
enableAlternateRowColors: boolean;
enableColumnReorder: boolean;
enableColumnResize: boolean;
enableDrag?: boolean;
enableExpansion: boolean;
enableHeader: boolean;
enableHorizontalBorders: boolean;
enableRowHoverHighlight: boolean;
enableScrollShadow: boolean; enableScrollShadow: boolean;
enableSelection: boolean;
enableVerticalBorders: boolean;
getItem?: (index: number) => undefined | unknown; getItem?: (index: number) => undefined | unknown;
getRowHeight: (index: number, cellProps: TableItemProps) => number;
groups?: TableGroupHeader[];
headerHeight: number; headerHeight: number;
internalState: ItemListStateActions;
itemType: LibraryItem;
mergedRowRef: React.Ref<HTMLDivElement>; mergedRowRef: React.Ref<HTMLDivElement>;
onRangeChanged?: ItemTableListProps['onRangeChanged']; onRangeChanged?: ItemTableListProps['onRangeChanged'];
parsedColumns: ReturnType<typeof parseTableColumns>; parsedColumns: ReturnType<typeof parseTableColumns>;
@@ -134,13 +120,10 @@ interface VirtualizedTableGridProps {
pinnedRightColumnRef: React.RefObject<HTMLDivElement | null>; pinnedRightColumnRef: React.RefObject<HTMLDivElement | null>;
pinnedRowCount: number; pinnedRowCount: number;
pinnedRowRef: React.RefObject<HTMLDivElement | null>; pinnedRowRef: React.RefObject<HTMLDivElement | null>;
playerContext: PlayerContext;
showLeftShadow: boolean; showLeftShadow: boolean;
showRightShadow: boolean; showRightShadow: boolean;
showTopShadow: boolean; showTopShadow: boolean;
size: 'compact' | 'default' | 'large'; tableConfig: ItemTableListConfig;
startRowIndex?: number;
tableId: string;
totalColumnCount: number; totalColumnCount: number;
totalRowCount: number; totalRowCount: number;
} }
@@ -148,27 +131,11 @@ interface VirtualizedTableGridProps {
const VirtualizedTableGrid = ({ const VirtualizedTableGrid = ({
calculatedColumnWidths, calculatedColumnWidths,
CellComponent, CellComponent,
cellPadding,
controls,
data, data,
dataWithGroups, dataWithGroups,
enableAlternateRowColors,
enableColumnReorder,
enableColumnResize,
enableDrag,
enableExpansion,
enableHeader,
enableHorizontalBorders,
enableRowHoverHighlight,
enableScrollShadow, enableScrollShadow,
enableSelection,
enableVerticalBorders,
getItem, getItem,
getRowHeight,
groups,
headerHeight, headerHeight,
internalState,
itemType,
mergedRowRef, mergedRowRef,
onRangeChanged, onRangeChanged,
parsedColumns, parsedColumns,
@@ -178,16 +145,14 @@ const VirtualizedTableGrid = ({
pinnedRightColumnRef, pinnedRightColumnRef,
pinnedRowCount, pinnedRowCount,
pinnedRowRef, pinnedRowRef,
playerContext,
showLeftShadow, showLeftShadow,
showRightShadow, showRightShadow,
showTopShadow, showTopShadow,
size, tableConfig,
startRowIndex,
tableId,
totalColumnCount, totalColumnCount,
totalRowCount, totalRowCount,
}: VirtualizedTableGridProps) => { }: VirtualizedTableGridProps) => {
const { enableHeader, enableRowHoverHighlight, getRowHeight, groups } = tableConfig;
const hoverDelegateRef = useRef<HTMLDivElement | null>(null); const hoverDelegateRef = useRef<HTMLDivElement | null>(null);
useRowInteractionDelegate({ useRowInteractionDelegate({
@@ -345,35 +310,7 @@ const VirtualizedTableGrid = ({
], ],
); );
const stableConfigProps = useMemo( const gridOnlyProps = useMemo(
() => ({
cellPadding,
columns: parsedColumns,
controls,
enableHeader,
getRowHeight,
hasAlbumGroupColumn: parsedColumns.some((col) => col.id === TableColumn.ALBUM_GROUP),
internalState,
itemType,
playerContext,
size,
tableId,
}),
[
cellPadding,
parsedColumns,
controls,
enableHeader,
getRowHeight,
internalState,
itemType,
playerContext,
size,
tableId,
],
);
const dynamicDataProps = useMemo(
() => ({ () => ({
calculatedColumnWidths, calculatedColumnWidths,
data: dataWithGroups, data: dataWithGroups,
@@ -381,11 +318,11 @@ const VirtualizedTableGrid = ({
getGroupRenderData, getGroupRenderData,
getRowItem, getRowItem,
groupHeaderInfoByRowIndex, groupHeaderInfoByRowIndex,
hasAlbumGroupColumn: parsedColumns.some((col) => col.id === TableColumn.ALBUM_GROUP),
pinnedLeftColumnCount, pinnedLeftColumnCount,
pinnedLeftColumnWidths, pinnedLeftColumnWidths,
pinnedRightColumnCount, pinnedRightColumnCount,
pinnedRightColumnWidths, pinnedRightColumnWidths,
startRowIndex,
}), }),
[ [
calculatedColumnWidths, calculatedColumnWidths,
@@ -394,50 +331,68 @@ const VirtualizedTableGrid = ({
getAdjustedRowIndex, getAdjustedRowIndex,
getGroupRenderData, getGroupRenderData,
groupHeaderInfoByRowIndex, groupHeaderInfoByRowIndex,
parsedColumns,
pinnedLeftColumnCount, pinnedLeftColumnCount,
pinnedLeftColumnWidths, pinnedLeftColumnWidths,
pinnedRightColumnCount, pinnedRightColumnCount,
pinnedRightColumnWidths, pinnedRightColumnWidths,
startRowIndex,
],
);
const featureFlags = useMemo(
() => ({
enableAlternateRowColors,
enableColumnReorder,
enableColumnResize,
enableDrag,
enableExpansion,
enableHorizontalBorders,
enableRowHoverHighlight,
enableSelection,
enableVerticalBorders,
groups,
}),
[
enableAlternateRowColors,
enableColumnReorder,
enableColumnResize,
enableDrag,
enableExpansion,
enableHorizontalBorders,
enableRowHoverHighlight,
enableSelection,
enableVerticalBorders,
groups,
], ],
); );
const itemProps: TableItemProps = useMemo( const itemProps: TableItemProps = useMemo(
() => ({ () => ({
...stableConfigProps, cellPadding: tableConfig.cellPadding,
...dynamicDataProps, columns: tableConfig.columns,
...featureFlags, controls: tableConfig.controls,
enableAlternateRowColors: tableConfig.enableAlternateRowColors,
enableColumnReorder: tableConfig.enableColumnReorder,
enableColumnResize: tableConfig.enableColumnResize,
enableDrag: tableConfig.enableDrag,
enableExpansion: tableConfig.enableExpansion,
enableHeader: tableConfig.enableHeader,
enableHorizontalBorders: tableConfig.enableHorizontalBorders,
enableRowHoverHighlight: tableConfig.enableRowHoverHighlight,
enableSelection: tableConfig.enableSelection,
enableVerticalBorders: tableConfig.enableVerticalBorders,
getRowHeight: tableConfig.getRowHeight,
groups: tableConfig.groups,
internalState: tableConfig.internalState,
itemType: tableConfig.itemType,
playerContext: tableConfig.playerContext,
playlistId: tableConfig.playlistId,
size: tableConfig.size,
startRowIndex: tableConfig.startRowIndex,
tableId: tableConfig.tableId,
...gridOnlyProps,
}), }),
[stableConfigProps, dynamicDataProps, featureFlags], [gridOnlyProps, tableConfig],
); );
const pinnedLeftGridMinWidthPx = useMemo(() => {
let sum = 0;
for (let i = 0; i < pinnedLeftColumnCount; i++) {
sum += calculatedColumnWidths[i] ?? 0;
}
return sum;
}, [calculatedColumnWidths, pinnedLeftColumnCount]);
const pinnedRightGridMinWidthPx = useMemo(() => {
let sum = 0;
const start = pinnedLeftColumnCount + totalColumnCount;
for (let i = 0; i < pinnedRightColumnCount; i++) {
sum += calculatedColumnWidths[start + i] ?? 0;
}
return sum;
}, [calculatedColumnWidths, pinnedLeftColumnCount, pinnedRightColumnCount, totalColumnCount]);
const pinnedRowsMinHeightPx = useMemo(() => {
let sum = 0;
for (let i = 0; i < pinnedRowCount; i++) {
sum += getRowHeight(i, itemProps);
}
return sum;
}, [getRowHeight, itemProps, pinnedRowCount]);
const PinnedRowCell = useCallback( const PinnedRowCell = useCallback(
(cellProps: CellComponentProps & TableItemProps) => { (cellProps: CellComponentProps & TableItemProps) => {
return ( return (
@@ -447,16 +402,14 @@ const VirtualizedTableGrid = ({
/> />
); );
}, },
// eslint-disable-next-line react-hooks/exhaustive-deps [pinnedLeftColumnCount, CellComponent],
[pinnedLeftColumnCount, CellComponent, featureFlags, calculatedColumnWidths],
); );
const PinnedColumnCell = useCallback( const PinnedColumnCell = useCallback(
(cellProps: CellComponentProps & TableItemProps) => { (cellProps: CellComponentProps & TableItemProps) => {
return <CellComponent {...cellProps} rowIndex={cellProps.rowIndex + pinnedRowCount} />; return <CellComponent {...cellProps} rowIndex={cellProps.rowIndex + pinnedRowCount} />;
}, },
// eslint-disable-next-line react-hooks/exhaustive-deps [pinnedRowCount, CellComponent],
[pinnedRowCount, CellComponent, featureFlags, calculatedColumnWidths],
); );
const PinnedRightColumnCell = useCallback( const PinnedRightColumnCell = useCallback(
@@ -469,15 +422,7 @@ const VirtualizedTableGrid = ({
/> />
); );
}, },
// eslint-disable-next-line react-hooks/exhaustive-deps [pinnedLeftColumnCount, pinnedRowCount, totalColumnCount, CellComponent],
[
pinnedLeftColumnCount,
pinnedRowCount,
totalColumnCount,
CellComponent,
featureFlags,
calculatedColumnWidths,
],
); );
const PinnedRightIntersectionCell = useCallback( const PinnedRightIntersectionCell = useCallback(
@@ -489,14 +434,7 @@ const VirtualizedTableGrid = ({
/> />
); );
}, },
// eslint-disable-next-line react-hooks/exhaustive-deps [pinnedLeftColumnCount, totalColumnCount, CellComponent],
[
pinnedLeftColumnCount,
totalColumnCount,
CellComponent,
featureFlags,
calculatedColumnWidths,
],
); );
const RowCell = useCallback( const RowCell = useCallback(
@@ -509,14 +447,7 @@ const VirtualizedTableGrid = ({
/> />
); );
}, },
// eslint-disable-next-line react-hooks/exhaustive-deps [pinnedLeftColumnCount, pinnedRowCount, CellComponent],
[
pinnedLeftColumnCount,
pinnedRowCount,
CellComponent,
featureFlags,
calculatedColumnWidths,
],
); );
const handleOnCellsRendered = useCallback( const handleOnCellsRendered = useCallback(
@@ -541,10 +472,7 @@ const VirtualizedTableGrid = ({
style={ style={
{ {
'--header-height': `${headerHeight}px`, '--header-height': `${headerHeight}px`,
minWidth: `${Array.from({ length: pinnedLeftColumnCount }, () => 0).reduce( minWidth: `${pinnedLeftGridMinWidthPx}px`,
(a, _, i) => a + columnWidth(i),
0,
)}px`,
} as React.CSSProperties } as React.CSSProperties
} }
> >
@@ -554,10 +482,7 @@ const VirtualizedTableGrid = ({
[styles.withHeader]: enableHeader, [styles.withHeader]: enableHeader,
})} })}
style={{ style={{
minHeight: `${Array.from({ length: pinnedRowCount }, () => 0).reduce( minHeight: `${pinnedRowsMinHeightPx}px`,
(a, _, i) => a + getRowHeight(i, itemProps),
0,
)}px`,
overflow: 'hidden', overflow: 'hidden',
}} }}
> >
@@ -611,10 +536,7 @@ const VirtualizedTableGrid = ({
style={ style={
{ {
'--header-height': `${headerHeight}px`, '--header-height': `${headerHeight}px`,
minHeight: `${Array.from( minHeight: `${pinnedRowsMinHeightPx}px`,
{ length: pinnedRowCount },
() => 0,
).reduce((a, _, i) => a + getRowHeight(i, itemProps), 0)}px`,
overflow: 'hidden', overflow: 'hidden',
} as React.CSSProperties } as React.CSSProperties
} }
@@ -627,7 +549,7 @@ const VirtualizedTableGrid = ({
columnWidth={(index) => { columnWidth={(index) => {
return columnWidth(index + pinnedLeftColumnCount); return columnWidth(index + pinnedLeftColumnCount);
}} }}
rowCount={Array.from({ length: pinnedRowCount }, () => 0).length} rowCount={pinnedRowCount}
rowHeight={getRowHeight} rowHeight={getRowHeight}
/> />
</div> </div>
@@ -660,14 +582,7 @@ const VirtualizedTableGrid = ({
style={ style={
{ {
'--header-height': `${headerHeight}px`, '--header-height': `${headerHeight}px`,
minWidth: `${Array.from( minWidth: `${pinnedRightGridMinWidthPx}px`,
{ length: pinnedRightColumnCount },
() => 0,
).reduce(
(a, _, i) =>
a + columnWidth(i + pinnedLeftColumnCount + totalColumnCount),
0,
)}px`,
} as React.CSSProperties } as React.CSSProperties
} }
> >
@@ -677,10 +592,7 @@ const VirtualizedTableGrid = ({
[styles.withHeader]: enableHeader, [styles.withHeader]: enableHeader,
})} })}
style={{ style={{
minHeight: `${Array.from( minHeight: `${pinnedRowsMinHeightPx}px`,
{ length: pinnedRowCount },
() => 0,
).reduce((a, _, i) => a + getRowHeight(i, itemProps), 0)}px`,
overflow: 'hidden', overflow: 'hidden',
}} }}
> >
@@ -739,27 +651,12 @@ const MemoizedVirtualizedTableGrid = memo(VirtualizedTableGrid, (prevProps, next
prevProps.calculatedColumnWidths, prevProps.calculatedColumnWidths,
nextProps.calculatedColumnWidths, nextProps.calculatedColumnWidths,
) && ) &&
prevProps.cellPadding === nextProps.cellPadding && prevProps.tableConfig === nextProps.tableConfig &&
prevProps.controls === nextProps.controls &&
prevProps.data === nextProps.data && prevProps.data === nextProps.data &&
prevProps.dataWithGroups === nextProps.dataWithGroups && prevProps.dataWithGroups === nextProps.dataWithGroups &&
prevProps.enableAlternateRowColors === nextProps.enableAlternateRowColors &&
prevProps.enableColumnReorder === nextProps.enableColumnReorder &&
prevProps.enableColumnResize === nextProps.enableColumnResize &&
prevProps.enableDrag === nextProps.enableDrag &&
prevProps.enableExpansion === nextProps.enableExpansion &&
prevProps.enableHeader === nextProps.enableHeader &&
prevProps.enableHorizontalBorders === nextProps.enableHorizontalBorders &&
prevProps.enableRowHoverHighlight === nextProps.enableRowHoverHighlight &&
prevProps.enableScrollShadow === nextProps.enableScrollShadow && prevProps.enableScrollShadow === nextProps.enableScrollShadow &&
prevProps.enableSelection === nextProps.enableSelection &&
prevProps.enableVerticalBorders === nextProps.enableVerticalBorders &&
prevProps.getItem === nextProps.getItem && prevProps.getItem === nextProps.getItem &&
prevProps.getRowHeight === nextProps.getRowHeight &&
prevProps.groups === nextProps.groups &&
prevProps.headerHeight === nextProps.headerHeight && prevProps.headerHeight === nextProps.headerHeight &&
prevProps.internalState === nextProps.internalState &&
prevProps.itemType === nextProps.itemType &&
prevProps.mergedRowRef === nextProps.mergedRowRef && prevProps.mergedRowRef === nextProps.mergedRowRef &&
prevProps.onRangeChanged === nextProps.onRangeChanged && prevProps.onRangeChanged === nextProps.onRangeChanged &&
prevProps.parsedColumns === nextProps.parsedColumns && prevProps.parsedColumns === nextProps.parsedColumns &&
@@ -769,13 +666,9 @@ const MemoizedVirtualizedTableGrid = memo(VirtualizedTableGrid, (prevProps, next
prevProps.pinnedRightColumnRef === nextProps.pinnedRightColumnRef && prevProps.pinnedRightColumnRef === nextProps.pinnedRightColumnRef &&
prevProps.pinnedRowCount === nextProps.pinnedRowCount && prevProps.pinnedRowCount === nextProps.pinnedRowCount &&
prevProps.pinnedRowRef === nextProps.pinnedRowRef && prevProps.pinnedRowRef === nextProps.pinnedRowRef &&
prevProps.playerContext === nextProps.playerContext &&
prevProps.showLeftShadow === nextProps.showLeftShadow && prevProps.showLeftShadow === nextProps.showLeftShadow &&
prevProps.showRightShadow === nextProps.showRightShadow && prevProps.showRightShadow === nextProps.showRightShadow &&
prevProps.showTopShadow === nextProps.showTopShadow && prevProps.showTopShadow === nextProps.showTopShadow &&
prevProps.size === nextProps.size &&
prevProps.startRowIndex === nextProps.startRowIndex &&
prevProps.tableId === nextProps.tableId &&
prevProps.totalColumnCount === nextProps.totalColumnCount && prevProps.totalColumnCount === nextProps.totalColumnCount &&
prevProps.totalRowCount === nextProps.totalRowCount && prevProps.totalRowCount === nextProps.totalRowCount &&
prevProps.CellComponent === nextProps.CellComponent prevProps.CellComponent === nextProps.CellComponent
@@ -828,6 +721,7 @@ export interface TableItemProps {
pinnedRightColumnCount?: number; pinnedRightColumnCount?: number;
pinnedRightColumnWidths?: number[]; pinnedRightColumnWidths?: number[];
playerContext: PlayerContext; playerContext: PlayerContext;
playlistId?: string;
size?: ItemTableListProps['size']; size?: ItemTableListProps['size'];
startRowIndex?: number; startRowIndex?: number;
tableId: string; tableId: string;
@@ -1309,6 +1203,7 @@ const BaseItemTableList = ({
size = 'default', size = 'default',
startRowIndex, startRowIndex,
}: ItemTableListProps) => { }: ItemTableListProps) => {
const { playlistId: routePlaylistId } = useParams() as { playlistId?: string };
const tableId = useId(); const tableId = useId();
const baseItemCount = itemCount ?? data.length; const baseItemCount = itemCount ?? data.length;
const totalItemCount = enableHeader ? baseItemCount + 1 : baseItemCount; const totalItemCount = enableHeader ? baseItemCount + 1 : baseItemCount;
@@ -1574,6 +1469,7 @@ const BaseItemTableList = ({
pinnedLeftColumnCount + totalColumnCount, pinnedLeftColumnCount + totalColumnCount,
), ),
playerContext, playerContext,
playlistId: routePlaylistId,
size, size,
tableId, tableId,
}), }),
@@ -1599,6 +1495,7 @@ const BaseItemTableList = ({
pinnedLeftColumnCount, pinnedLeftColumnCount,
pinnedRightColumnCount, pinnedRightColumnCount,
playerContext, playerContext,
routePlaylistId,
size, size,
tableId, tableId,
totalColumnCount, totalColumnCount,
@@ -1612,17 +1509,27 @@ const BaseItemTableList = ({
itemType, itemType,
}); });
const tableConfigValue = useMemo( const tableConfigValue = useMemo<ItemTableListConfig>(
() => ({ () => ({
cellPadding, cellPadding,
columns: parsedColumns, columns: parsedColumns,
controls, controls,
enableAlternateRowColors,
enableColumnReorder: !!onColumnReordered,
enableColumnResize: !!onColumnResized,
enableDrag,
enableExpansion,
enableHeader, enableHeader,
enableHorizontalBorders,
enableRowHoverHighlight, enableRowHoverHighlight,
enableSelection, enableSelection,
enableVerticalBorders,
getRowHeight,
groups,
internalState, internalState,
itemType, itemType,
playerContext, playerContext,
playlistId: routePlaylistId,
size, size,
startRowIndex, startRowIndex,
tableId, tableId,
@@ -1631,12 +1538,22 @@ const BaseItemTableList = ({
cellPadding, cellPadding,
parsedColumns, parsedColumns,
controls, controls,
enableAlternateRowColors,
onColumnReordered,
onColumnResized,
enableDrag,
enableExpansion,
enableHeader, enableHeader,
enableHorizontalBorders,
enableRowHoverHighlight, enableRowHoverHighlight,
enableSelection, enableSelection,
enableVerticalBorders,
getRowHeight,
groups,
internalState, internalState,
itemType, itemType,
playerContext, playerContext,
routePlaylistId,
size, size,
startRowIndex, startRowIndex,
tableId, tableId,
@@ -1707,27 +1624,11 @@ const BaseItemTableList = ({
<MemoizedVirtualizedTableGrid <MemoizedVirtualizedTableGrid
calculatedColumnWidths={calculatedColumnWidths} calculatedColumnWidths={calculatedColumnWidths}
CellComponent={optimizedCellComponent} CellComponent={optimizedCellComponent}
cellPadding={cellPadding}
controls={controls}
data={data} data={data}
dataWithGroups={dataWithGroups} dataWithGroups={dataWithGroups}
enableAlternateRowColors={enableAlternateRowColors}
enableColumnReorder={!!onColumnReordered}
enableColumnResize={!!onColumnResized}
enableDrag={enableDrag}
enableExpansion={enableExpansion}
enableHeader={enableHeader}
enableHorizontalBorders={enableHorizontalBorders}
enableRowHoverHighlight={enableRowHoverHighlight}
enableScrollShadow={enableScrollShadow} enableScrollShadow={enableScrollShadow}
enableSelection={enableSelection}
enableVerticalBorders={enableVerticalBorders}
getItem={getItem} getItem={getItem}
getRowHeight={getRowHeight}
groups={groups}
headerHeight={headerHeight} headerHeight={headerHeight}
internalState={internalState}
itemType={itemType}
mergedRowRef={mergedRowRef} mergedRowRef={mergedRowRef}
onRangeChanged={onRangeChanged} onRangeChanged={onRangeChanged}
parsedColumns={parsedColumns} parsedColumns={parsedColumns}
@@ -1737,13 +1638,10 @@ const BaseItemTableList = ({
pinnedRightColumnRef={pinnedRightColumnRef} pinnedRightColumnRef={pinnedRightColumnRef}
pinnedRowCount={pinnedRowCount} pinnedRowCount={pinnedRowCount}
pinnedRowRef={pinnedRowRef} pinnedRowRef={pinnedRowRef}
playerContext={playerContext}
showLeftShadow={showLeftShadow} showLeftShadow={showLeftShadow}
showRightShadow={showRightShadow} showRightShadow={showRightShadow}
showTopShadow={showTopShadow} showTopShadow={showTopShadow}
size={size} tableConfig={tableConfigValue}
startRowIndex={startRowIndex}
tableId={tableId}
totalColumnCount={totalColumnCount} totalColumnCount={totalColumnCount}
totalRowCount={totalRowCount} totalRowCount={totalRowCount}
/> />
@@ -1,4 +1,4 @@
import React, { memo, useMemo } from 'react'; import React, { useMemo } from 'react';
import { CellComponentProps } from 'react-window-v2'; import { CellComponentProps } from 'react-window-v2';
import { createColumnCellComponents } from './cell-component-factory'; import { createColumnCellComponents } from './cell-component-factory';
@@ -24,24 +24,7 @@ const MemoizedCellRouterBase = (props: MemoizedCellRouterProps) => {
return <ItemTableListColumn {...props} />; return <ItemTableListColumn {...props} />;
}; };
export const MemoizedCellRouter = memo(MemoizedCellRouterBase, (prevProps, nextProps) => { export const MemoizedCellRouter = MemoizedCellRouterBase;
return (
prevProps.rowIndex === nextProps.rowIndex &&
prevProps.columnIndex === nextProps.columnIndex &&
prevProps.data === nextProps.data &&
prevProps.columns === nextProps.columns &&
prevProps.columnCellComponents === nextProps.columnCellComponents &&
prevProps.size === nextProps.size &&
prevProps.enableAlternateRowColors === nextProps.enableAlternateRowColors &&
prevProps.enableHorizontalBorders === nextProps.enableHorizontalBorders &&
prevProps.enableVerticalBorders === nextProps.enableVerticalBorders &&
prevProps.enableRowHoverHighlight === nextProps.enableRowHoverHighlight &&
prevProps.enableSelection === nextProps.enableSelection &&
prevProps.enableColumnResize === nextProps.enableColumnResize &&
prevProps.enableColumnReorder === nextProps.enableColumnReorder &&
prevProps.cellPadding === nextProps.cellPadding
);
});
export const useColumnCellComponents = ( export const useColumnCellComponents = (
columns: TableColumn[], columns: TableColumn[],