mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-09 20:29:36 +02:00
refactor item table props
This commit is contained in:
@@ -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
|
||||||
|
|||||||
+237
-225
@@ -34,256 +34,268 @@ 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: () => {
|
? {
|
||||||
if (!item || !isDataRow) {
|
getId: () => {
|
||||||
return [];
|
if (!item || !isDataRow) {
|
||||||
}
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
const draggedItems = getDraggedItems(item as any, internalState);
|
const draggedItems = getDraggedItems(item as any, internalState);
|
||||||
|
|
||||||
return draggedItems.map((draggedItem) => draggedItem.id);
|
return draggedItems.map((draggedItem) => draggedItem.id);
|
||||||
},
|
},
|
||||||
getItem: () => {
|
getItem: () => {
|
||||||
if (!item || !isDataRow) {
|
if (!item || !isDataRow) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const draggedItems = getDraggedItems(item as any, internalState);
|
const draggedItems = getDraggedItems(item as any, internalState);
|
||||||
|
|
||||||
return draggedItems;
|
return draggedItems;
|
||||||
},
|
},
|
||||||
itemType,
|
itemType,
|
||||||
onDragStart: () => {
|
onDragStart: () => {
|
||||||
if (!item || !isDataRow) {
|
if (!item || !isDataRow) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const draggedItems = getDraggedItems(item as any, internalState);
|
const draggedItems = getDraggedItems(item as any, internalState);
|
||||||
if (internalState) {
|
if (internalState) {
|
||||||
internalState.setDragging(draggedItems);
|
internalState.setDragging(draggedItems);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onDrop: () => {
|
onDrop: () => {
|
||||||
if (internalState) {
|
if (internalState) {
|
||||||
internalState.setDragging([]);
|
internalState.setDragging([]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
operation:
|
operation:
|
||||||
itemType === LibraryItem.QUEUE_SONG
|
itemType === LibraryItem.QUEUE_SONG
|
||||||
? [DragOperation.REORDER, DragOperation.ADD]
|
? [DragOperation.REORDER, DragOperation.ADD]
|
||||||
: itemType === LibraryItem.PLAYLIST_SONG
|
: itemType === LibraryItem.PLAYLIST_SONG
|
||||||
? [DragOperation.REORDER, DragOperation.ADD]
|
? [DragOperation.REORDER, DragOperation.ADD]
|
||||||
: [DragOperation.ADD],
|
: [DragOperation.ADD],
|
||||||
target: DragTargetMap[itemType] || DragTarget.GENERIC,
|
target: DragTargetMap[itemType] || DragTarget.GENERIC,
|
||||||
},
|
}
|
||||||
drop: {
|
: undefined,
|
||||||
canDrop: (args) => {
|
drop: needsDropRegistration
|
||||||
if (args.source.type === DragTarget.TABLE_COLUMN) {
|
? {
|
||||||
return false;
|
canDrop: (args) => {
|
||||||
}
|
if (args.source.type === DragTarget.TABLE_COLUMN) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Allow drops for QUEUE_SONG (queue reordering)
|
// Allow drops for QUEUE_SONG (queue reordering)
|
||||||
if (itemType === LibraryItem.QUEUE_SONG) {
|
if (itemType === LibraryItem.QUEUE_SONG) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow drops for PLAYLIST_SONG (playlist reordering)
|
// Allow drops for PLAYLIST_SONG (playlist reordering)
|
||||||
// Only allow drops when drag is started from the reorder handle
|
// Only allow drops when drag is started from the reorder handle
|
||||||
if (
|
if (
|
||||||
itemType === LibraryItem.PLAYLIST_SONG &&
|
itemType === LibraryItem.PLAYLIST_SONG &&
|
||||||
args.source.itemType === LibraryItem.PLAYLIST_SONG &&
|
args.source.itemType === LibraryItem.PLAYLIST_SONG &&
|
||||||
args.source.metadata?.fromReorderHandle === true
|
args.source.metadata?.fromReorderHandle === true
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
getData: () => {
|
getData: () => {
|
||||||
return {
|
return {
|
||||||
id: [(item as unknown as { id: string }).id],
|
id: [(item as unknown as { id: string }).id],
|
||||||
item: [item as unknown as unknown[]],
|
item: [item as unknown as unknown[]],
|
||||||
itemType,
|
itemType,
|
||||||
type: DragTargetMap[itemType] || DragTarget.GENERIC,
|
type: DragTargetMap[itemType] || DragTarget.GENERIC,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
onDrag: () => {
|
onDrag: () => {
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
onDragLeave: () => {
|
onDragLeave: () => {
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
onDrop: (args) => {
|
onDrop: (args) => {
|
||||||
if (args.self.type === DragTarget.QUEUE_SONG) {
|
if (args.self.type === DragTarget.QUEUE_SONG) {
|
||||||
const sourceServerId = (
|
const sourceServerId = (
|
||||||
args.source.item?.[0] as unknown as { _serverId: string }
|
args.source.item?.[0] as unknown as { _serverId: string }
|
||||||
)._serverId;
|
)._serverId;
|
||||||
|
|
||||||
const sourceItemType = args.source.itemType as LibraryItem;
|
const sourceItemType = args.source.itemType as LibraryItem;
|
||||||
|
|
||||||
const droppedOnUniqueId = (
|
const droppedOnUniqueId = (
|
||||||
args.self.item?.[0] as unknown as { _uniqueId: string }
|
args.self.item?.[0] as unknown as { _uniqueId: string }
|
||||||
)._uniqueId;
|
)._uniqueId;
|
||||||
|
|
||||||
switch (args.source.type) {
|
switch (args.source.type) {
|
||||||
case DragTarget.ALBUM: {
|
case DragTarget.ALBUM: {
|
||||||
playerContext.addToQueueByFetch(
|
playerContext.addToQueueByFetch(
|
||||||
sourceServerId,
|
sourceServerId,
|
||||||
args.source.id,
|
args.source.id,
|
||||||
sourceItemType,
|
sourceItemType,
|
||||||
{ edge: args.edge, uniqueId: droppedOnUniqueId },
|
{ edge: args.edge, uniqueId: droppedOnUniqueId },
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DragTarget.ALBUM_ARTIST: {
|
case DragTarget.ALBUM_ARTIST: {
|
||||||
playerContext.addToQueueByFetch(
|
playerContext.addToQueueByFetch(
|
||||||
sourceServerId,
|
sourceServerId,
|
||||||
args.source.id,
|
args.source.id,
|
||||||
sourceItemType,
|
sourceItemType,
|
||||||
{ edge: args.edge, uniqueId: droppedOnUniqueId },
|
{ edge: args.edge, uniqueId: droppedOnUniqueId },
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DragTarget.ARTIST: {
|
case DragTarget.ARTIST: {
|
||||||
playerContext.addToQueueByFetch(
|
playerContext.addToQueueByFetch(
|
||||||
sourceServerId,
|
sourceServerId,
|
||||||
args.source.id,
|
args.source.id,
|
||||||
sourceItemType,
|
sourceItemType,
|
||||||
{ edge: args.edge, uniqueId: droppedOnUniqueId },
|
{ edge: args.edge, uniqueId: droppedOnUniqueId },
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DragTarget.FOLDER: {
|
case DragTarget.FOLDER: {
|
||||||
const items = args.source.item;
|
const items = args.source.item;
|
||||||
|
|
||||||
const { folders, songs } = (items || []).reduce<{
|
const { folders, songs } = (items || []).reduce<{
|
||||||
folders: Folder[];
|
folders: Folder[];
|
||||||
songs: Song[];
|
songs: Song[];
|
||||||
}>(
|
}>(
|
||||||
(acc, item) => {
|
(acc, item) => {
|
||||||
if ((item as unknown as Song)._itemType === LibraryItem.SONG) {
|
if (
|
||||||
acc.songs.push(item as unknown as Song);
|
(item as unknown as Song)._itemType ===
|
||||||
} else if (
|
LibraryItem.SONG
|
||||||
(item as unknown as Folder)._itemType === LibraryItem.FOLDER
|
) {
|
||||||
) {
|
acc.songs.push(item as unknown as Song);
|
||||||
acc.folders.push(item as unknown as Folder);
|
} else if (
|
||||||
}
|
(item as unknown as Folder)._itemType ===
|
||||||
return acc;
|
LibraryItem.FOLDER
|
||||||
},
|
) {
|
||||||
{ folders: [], songs: [] },
|
acc.folders.push(item as unknown as Folder);
|
||||||
);
|
}
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{ folders: [], songs: [] },
|
||||||
|
);
|
||||||
|
|
||||||
const folderIds = folders.map((folder) => folder.id);
|
const folderIds = folders.map((folder) => folder.id);
|
||||||
|
|
||||||
// Handle folders: fetch and add to queue
|
// Handle folders: fetch and add to queue
|
||||||
if (folderIds.length > 0) {
|
if (folderIds.length > 0) {
|
||||||
playerContext.addToQueueByFetch(
|
playerContext.addToQueueByFetch(
|
||||||
sourceServerId,
|
sourceServerId,
|
||||||
folderIds,
|
folderIds,
|
||||||
LibraryItem.FOLDER,
|
LibraryItem.FOLDER,
|
||||||
{ edge: args.edge, uniqueId: droppedOnUniqueId },
|
{ edge: args.edge, uniqueId: droppedOnUniqueId },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle songs: add directly to queue
|
// Handle songs: add directly to queue
|
||||||
if (songs.length > 0) {
|
if (songs.length > 0) {
|
||||||
playerContext.addToQueueByData(songs, {
|
playerContext.addToQueueByData(songs, {
|
||||||
edge: args.edge,
|
edge: args.edge,
|
||||||
uniqueId: droppedOnUniqueId,
|
uniqueId: droppedOnUniqueId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DragTarget.GENRE: {
|
case DragTarget.GENRE: {
|
||||||
playerContext.addToQueueByFetch(
|
playerContext.addToQueueByFetch(
|
||||||
sourceServerId,
|
sourceServerId,
|
||||||
args.source.id,
|
args.source.id,
|
||||||
sourceItemType,
|
sourceItemType,
|
||||||
{ edge: args.edge, uniqueId: droppedOnUniqueId },
|
{ edge: args.edge, uniqueId: droppedOnUniqueId },
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DragTarget.PLAYLIST: {
|
case DragTarget.PLAYLIST: {
|
||||||
playerContext.addToQueueByFetch(
|
playerContext.addToQueueByFetch(
|
||||||
sourceServerId,
|
sourceServerId,
|
||||||
args.source.id,
|
args.source.id,
|
||||||
sourceItemType,
|
sourceItemType,
|
||||||
{ edge: args.edge, uniqueId: droppedOnUniqueId },
|
{ edge: args.edge, uniqueId: droppedOnUniqueId },
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DragTarget.QUEUE_SONG: {
|
case DragTarget.QUEUE_SONG: {
|
||||||
const sourceItems = (args.source.item || []) as QueueSong[];
|
const sourceItems = (args.source.item || []) as QueueSong[];
|
||||||
if (
|
if (
|
||||||
sourceItems.length > 0 &&
|
sourceItems.length > 0 &&
|
||||||
args.edge &&
|
args.edge &&
|
||||||
(args.edge === 'top' || args.edge === 'bottom')
|
(args.edge === 'top' || args.edge === 'bottom')
|
||||||
) {
|
) {
|
||||||
playerContext.moveSelectedTo(
|
playerContext.moveSelectedTo(
|
||||||
sourceItems,
|
sourceItems,
|
||||||
args.edge,
|
args.edge,
|
||||||
droppedOnUniqueId,
|
droppedOnUniqueId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DragTarget.SONG: {
|
case DragTarget.SONG: {
|
||||||
const sourceItems = (args.source.item || []) as Song[];
|
const sourceItems = (args.source.item || []) as Song[];
|
||||||
if (sourceItems.length > 0) {
|
if (sourceItems.length > 0) {
|
||||||
playerContext.addToQueueByData(sourceItems, {
|
playerContext.addToQueueByData(sourceItems, {
|
||||||
edge: args.edge,
|
edge: args.edge,
|
||||||
uniqueId: droppedOnUniqueId,
|
uniqueId: droppedOnUniqueId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle PLAYLIST_SONG reordering
|
// Handle PLAYLIST_SONG reordering
|
||||||
// Only allow drops when drag is started from the reorder handle
|
// Only allow drops when drag is started from the reorder handle
|
||||||
if (
|
if (
|
||||||
args.self.itemType === LibraryItem.PLAYLIST_SONG &&
|
args.self.itemType === LibraryItem.PLAYLIST_SONG &&
|
||||||
args.source.itemType === LibraryItem.PLAYLIST_SONG &&
|
args.source.itemType === LibraryItem.PLAYLIST_SONG &&
|
||||||
args.source.metadata?.fromReorderHandle === true &&
|
args.source.metadata?.fromReorderHandle === true &&
|
||||||
playlistId
|
playlistId
|
||||||
) {
|
) {
|
||||||
const sourceItems = (args.source.item || []) as any[];
|
const sourceItems = (args.source.item || []) as any[];
|
||||||
const targetItem = item as any;
|
const targetItem = item as any;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
sourceItems.length > 0 &&
|
sourceItems.length > 0 &&
|
||||||
args.edge &&
|
args.edge &&
|
||||||
(args.edge === 'top' || args.edge === 'bottom') &&
|
(args.edge === 'top' || args.edge === 'bottom') &&
|
||||||
targetItem
|
targetItem
|
||||||
) {
|
) {
|
||||||
// Emit event to reorder playlist songs
|
// Emit event to reorder playlist songs
|
||||||
eventEmitter.emit('PLAYLIST_REORDER', {
|
eventEmitter.emit('PLAYLIST_REORDER', {
|
||||||
edge: args.edge,
|
edge: args.edge,
|
||||||
playlistId,
|
playlistId,
|
||||||
sourceIds: args.source.id,
|
sourceIds: args.source.id,
|
||||||
targetId: targetItem.id,
|
targetId: targetItem.id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (internalState) {
|
if (internalState) {
|
||||||
internalState.setDragging([]);
|
internalState.setDragging([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
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[],
|
||||||
|
|||||||
Reference in New Issue
Block a user