mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-07 04:20:12 +02:00
add more memoization to the ItemTableList
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useSearchParams } from 'react-router';
|
||||
|
||||
import { parseIntParam, setSearchParam } from '/@/renderer/utils/query-params';
|
||||
@@ -12,11 +12,16 @@ export const useItemListScrollPersist = ({ enabled }: UseItemListScrollPersistPr
|
||||
|
||||
const scrollOffset = useMemo(() => parseIntParam(searchParams, 'scrollOffset'), [searchParams]);
|
||||
|
||||
const handleOnScrollEnd = (offset: number) => {
|
||||
if (!enabled) return;
|
||||
const handleOnScrollEnd = useCallback(
|
||||
(offset: number) => {
|
||||
if (!enabled) return;
|
||||
|
||||
setSearchParams((prev) => setSearchParam(prev, 'scrollOffset', offset), { replace: true });
|
||||
};
|
||||
setSearchParams((prev) => setSearchParam(prev, 'scrollOffset', offset), {
|
||||
replace: true,
|
||||
});
|
||||
},
|
||||
[enabled, setSearchParams],
|
||||
);
|
||||
|
||||
return { handleOnScrollEnd, scrollOffset };
|
||||
};
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import React from 'react';
|
||||
import { CellComponentProps } from 'react-window-v2';
|
||||
|
||||
import { TableItemProps } from '/@/renderer/components/item-list/item-table-list/item-table-list';
|
||||
import { ItemTableListColumn } from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
||||
import { LibraryItem } from '/@/shared/types/domain-types';
|
||||
import { TableColumn } from '/@/shared/types/types';
|
||||
|
||||
export const createColumnCellComponent = (
|
||||
columnType: TableColumn,
|
||||
itemType: LibraryItem,
|
||||
): React.ComponentType<CellComponentProps<TableItemProps>> => {
|
||||
return React.memo(
|
||||
(props: CellComponentProps<TableItemProps>) => {
|
||||
return <ItemTableListColumn {...props} columnType={columnType} itemType={itemType} />;
|
||||
},
|
||||
(prevProps, nextProps) => {
|
||||
return (
|
||||
prevProps.rowIndex === nextProps.rowIndex &&
|
||||
prevProps.columnIndex === nextProps.columnIndex &&
|
||||
prevProps.data === nextProps.data &&
|
||||
prevProps.style === nextProps.style &&
|
||||
prevProps.columns === nextProps.columns
|
||||
);
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
export const createColumnCellComponents = (
|
||||
columns: TableColumn[],
|
||||
itemType: LibraryItem,
|
||||
): Map<TableColumn, React.ComponentType<CellComponentProps<TableItemProps>>> => {
|
||||
const componentMap = new Map<
|
||||
TableColumn,
|
||||
React.ComponentType<CellComponentProps<TableItemProps>>
|
||||
>();
|
||||
|
||||
columns.forEach((columnType) => {
|
||||
componentMap.set(columnType, createColumnCellComponent(columnType, itemType));
|
||||
});
|
||||
|
||||
return componentMap;
|
||||
};
|
||||
@@ -1,3 +1,5 @@
|
||||
import { memo } from 'react';
|
||||
|
||||
import {
|
||||
ItemTableListInnerColumn,
|
||||
TableColumnContainer,
|
||||
@@ -5,7 +7,7 @@ import {
|
||||
import { ItemListItem } from '/@/renderer/components/item-list/types';
|
||||
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
|
||||
|
||||
export const ActionsColumn = (props: ItemTableListInnerColumn) => {
|
||||
const ActionsColumnBase = (props: ItemTableListInnerColumn) => {
|
||||
const row: any = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||
|
||||
const handleActionClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
@@ -51,3 +53,16 @@ export const ActionsColumn = (props: ItemTableListInnerColumn) => {
|
||||
|
||||
return <TableColumnContainer {...props}> </TableColumnContainer>;
|
||||
};
|
||||
|
||||
export const ActionsColumn = memo(ActionsColumnBase, (prevProps, nextProps) => {
|
||||
const prevItem = prevProps.getRowItem?.(prevProps.rowIndex);
|
||||
const nextItem = nextProps.getRowItem?.(nextProps.rowIndex);
|
||||
|
||||
return (
|
||||
prevProps.rowIndex === nextProps.rowIndex &&
|
||||
prevProps.columnIndex === nextProps.columnIndex &&
|
||||
prevProps.data === nextProps.data &&
|
||||
prevProps.columns === nextProps.columns &&
|
||||
prevItem === nextItem
|
||||
);
|
||||
});
|
||||
|
||||
@@ -54,6 +54,14 @@ const AlbumArtistsColumn = (props: ItemTableListInnerColumn) => {
|
||||
return <ColumnSkeletonVariable {...props} />;
|
||||
};
|
||||
|
||||
export const AlbumArtistsColumnMemo = memo(AlbumArtistsColumn);
|
||||
export const AlbumArtistsColumnMemo = memo(AlbumArtistsColumn, (prevProps, nextProps) => {
|
||||
return (
|
||||
prevProps.rowIndex === nextProps.rowIndex &&
|
||||
prevProps.columnIndex === nextProps.columnIndex &&
|
||||
prevProps.data === nextProps.data &&
|
||||
prevProps.columns === nextProps.columns &&
|
||||
prevProps.size === nextProps.size
|
||||
);
|
||||
});
|
||||
|
||||
export { AlbumArtistsColumnMemo as AlbumArtistsColumn };
|
||||
|
||||
@@ -75,6 +75,18 @@ const AlbumColumn = (props: ItemTableListInnerColumn) => {
|
||||
return <ColumnSkeletonVariable {...props} />;
|
||||
};
|
||||
|
||||
export const AlbumColumnMemo = memo(AlbumColumn);
|
||||
export const AlbumColumnMemo = memo(AlbumColumn, (prevProps, nextProps) => {
|
||||
const prevItem = prevProps.getRowItem?.(prevProps.rowIndex);
|
||||
const nextItem = nextProps.getRowItem?.(nextProps.rowIndex);
|
||||
|
||||
return (
|
||||
prevProps.rowIndex === nextProps.rowIndex &&
|
||||
prevProps.columnIndex === nextProps.columnIndex &&
|
||||
prevProps.data === nextProps.data &&
|
||||
prevProps.columns === nextProps.columns &&
|
||||
prevProps.size === nextProps.size &&
|
||||
prevItem === nextItem
|
||||
);
|
||||
});
|
||||
|
||||
export { AlbumColumnMemo as AlbumColumn };
|
||||
|
||||
@@ -109,6 +109,15 @@ const BaseArtistsColumn = (props: ItemTableListInnerColumn) => {
|
||||
}
|
||||
};
|
||||
|
||||
const ArtistsColumnMemo = memo(BaseArtistsColumn);
|
||||
const ArtistsColumnMemo = memo(BaseArtistsColumn, (prevProps, nextProps) => {
|
||||
return (
|
||||
prevProps.rowIndex === nextProps.rowIndex &&
|
||||
prevProps.columnIndex === nextProps.columnIndex &&
|
||||
prevProps.data === nextProps.data &&
|
||||
prevProps.columns === nextProps.columns &&
|
||||
prevProps.itemType === nextProps.itemType &&
|
||||
prevProps.size === nextProps.size
|
||||
);
|
||||
});
|
||||
|
||||
export { ArtistsColumnMemo as ArtistsColumn };
|
||||
|
||||
@@ -45,6 +45,18 @@ const ComposerColumn = (props: ItemTableListInnerColumn) => {
|
||||
return <ColumnSkeletonVariable {...props} />;
|
||||
};
|
||||
|
||||
export const ComposerColumnMemo = memo(ComposerColumn);
|
||||
export const ComposerColumnMemo = memo(ComposerColumn, (prevProps, nextProps) => {
|
||||
const prevItem = prevProps.getRowItem?.(prevProps.rowIndex);
|
||||
const nextItem = nextProps.getRowItem?.(nextProps.rowIndex);
|
||||
|
||||
return (
|
||||
prevProps.rowIndex === nextProps.rowIndex &&
|
||||
prevProps.columnIndex === nextProps.columnIndex &&
|
||||
prevProps.data === nextProps.data &&
|
||||
prevProps.columns === nextProps.columns &&
|
||||
prevProps.size === nextProps.size &&
|
||||
prevItem === nextItem
|
||||
);
|
||||
});
|
||||
|
||||
export { ComposerColumnMemo as ComposerColumn };
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { memo, useMemo } from 'react';
|
||||
|
||||
import {
|
||||
ColumnNullFallback,
|
||||
ColumnSkeletonFixed,
|
||||
@@ -29,15 +31,25 @@ const getDateTooltipLabel = (utcString: string) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const DateColumn = (props: ItemTableListInnerColumn) => {
|
||||
const DateColumnBase = (props: ItemTableListInnerColumn) => {
|
||||
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 { formattedDate, tooltipLabel } = useMemo(() => {
|
||||
if (typeof row === 'string' && row) {
|
||||
return {
|
||||
formattedDate: formatDateAbsolute(row),
|
||||
tooltipLabel: getDateTooltipLabel(row),
|
||||
};
|
||||
}
|
||||
return { formattedDate: null, tooltipLabel: null };
|
||||
}, [row]);
|
||||
|
||||
if (typeof row === 'string' && row) {
|
||||
return (
|
||||
<TableColumnTextContainer {...props}>
|
||||
<Tooltip label={getDateTooltipLabel(row)} multiline={false}>
|
||||
<span>{formatDateAbsolute(row)}</span>
|
||||
<Tooltip label={tooltipLabel} multiline={false}>
|
||||
<span>{formattedDate}</span>
|
||||
</Tooltip>
|
||||
</TableColumnTextContainer>
|
||||
);
|
||||
@@ -50,42 +62,70 @@ export const DateColumn = (props: ItemTableListInnerColumn) => {
|
||||
return <ColumnSkeletonFixed {...props} />;
|
||||
};
|
||||
|
||||
export const AbsoluteDateColumn = (props: ItemTableListInnerColumn) => {
|
||||
export const DateColumn = memo(DateColumnBase, (prevProps, nextProps) => {
|
||||
return (
|
||||
prevProps.rowIndex === nextProps.rowIndex &&
|
||||
prevProps.columnIndex === nextProps.columnIndex &&
|
||||
prevProps.data === nextProps.data &&
|
||||
prevProps.columns === nextProps.columns
|
||||
);
|
||||
});
|
||||
|
||||
const AbsoluteDateColumnBase = (props: ItemTableListInnerColumn) => {
|
||||
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 releaseDateContent = useMemo(() => {
|
||||
if (props.type === TableColumn.RELEASE_DATE) {
|
||||
const item = rowItem as any;
|
||||
if (item && 'releaseDate' in item && item.releaseDate) {
|
||||
const releaseDate = item.releaseDate;
|
||||
const originalDate =
|
||||
'originalDate' in item && item.originalDate && item.originalDate !== releaseDate
|
||||
? item.originalDate
|
||||
: null;
|
||||
|
||||
if (originalDate) {
|
||||
const formattedOriginalDate = formatDateAbsoluteUTC(originalDate);
|
||||
const formattedReleaseDate = formatDateAbsoluteUTC(releaseDate);
|
||||
const displayText = `${formattedOriginalDate}${SEPARATOR_STRING}${formattedReleaseDate}`;
|
||||
|
||||
return {
|
||||
displayText,
|
||||
tooltipLabel: getDateTooltipLabel(releaseDate),
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof releaseDate === 'string' && releaseDate) {
|
||||
return {
|
||||
displayText: formatDateAbsoluteUTC(releaseDate),
|
||||
tooltipLabel: getDateTooltipLabel(releaseDate),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}, [props.type, rowItem]);
|
||||
|
||||
const { formattedDate, tooltipLabel } = useMemo(() => {
|
||||
if (typeof row === 'string' && row) {
|
||||
return {
|
||||
formattedDate: formatDateAbsoluteUTC(row),
|
||||
tooltipLabel: getDateTooltipLabel(row),
|
||||
};
|
||||
}
|
||||
return { formattedDate: null, tooltipLabel: null };
|
||||
}, [row]);
|
||||
|
||||
if (props.type === TableColumn.RELEASE_DATE) {
|
||||
const item = rowItem as any;
|
||||
if (item && 'releaseDate' in item && item.releaseDate) {
|
||||
const releaseDate = item.releaseDate;
|
||||
const originalDate =
|
||||
'originalDate' in item && item.originalDate && item.originalDate !== releaseDate
|
||||
? item.originalDate
|
||||
: null;
|
||||
|
||||
if (originalDate) {
|
||||
const formattedOriginalDate = formatDateAbsoluteUTC(originalDate);
|
||||
const formattedReleaseDate = formatDateAbsoluteUTC(releaseDate);
|
||||
const displayText = `${formattedOriginalDate}${SEPARATOR_STRING}${formattedReleaseDate}`;
|
||||
|
||||
return (
|
||||
<TableColumnTextContainer {...props}>
|
||||
<Tooltip label={getDateTooltipLabel(releaseDate)} multiline={false}>
|
||||
<span>{displayText}</span>
|
||||
</Tooltip>
|
||||
</TableColumnTextContainer>
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof releaseDate === 'string' && releaseDate) {
|
||||
return (
|
||||
<TableColumnTextContainer {...props}>
|
||||
<Tooltip label={getDateTooltipLabel(releaseDate)} multiline={false}>
|
||||
<span>{formatDateAbsoluteUTC(releaseDate)}</span>
|
||||
</Tooltip>
|
||||
</TableColumnTextContainer>
|
||||
);
|
||||
}
|
||||
if (releaseDateContent) {
|
||||
return (
|
||||
<TableColumnTextContainer {...props}>
|
||||
<Tooltip label={releaseDateContent.tooltipLabel} multiline={false}>
|
||||
<span>{releaseDateContent.displayText}</span>
|
||||
</Tooltip>
|
||||
</TableColumnTextContainer>
|
||||
);
|
||||
}
|
||||
|
||||
if (row === null) {
|
||||
@@ -98,8 +138,8 @@ export const AbsoluteDateColumn = (props: ItemTableListInnerColumn) => {
|
||||
if (typeof row === 'string' && row) {
|
||||
return (
|
||||
<TableColumnTextContainer {...props}>
|
||||
<Tooltip label={getDateTooltipLabel(row)} multiline={false}>
|
||||
<span>{formatDateAbsoluteUTC(row)}</span>
|
||||
<Tooltip label={tooltipLabel} multiline={false}>
|
||||
<span>{formattedDate}</span>
|
||||
</Tooltip>
|
||||
</TableColumnTextContainer>
|
||||
);
|
||||
@@ -112,15 +152,35 @@ export const AbsoluteDateColumn = (props: ItemTableListInnerColumn) => {
|
||||
return <ColumnSkeletonFixed {...props} />;
|
||||
};
|
||||
|
||||
export const RelativeDateColumn = (props: ItemTableListInnerColumn) => {
|
||||
export const AbsoluteDateColumn = memo(AbsoluteDateColumnBase, (prevProps, nextProps) => {
|
||||
return (
|
||||
prevProps.rowIndex === nextProps.rowIndex &&
|
||||
prevProps.columnIndex === nextProps.columnIndex &&
|
||||
prevProps.data === nextProps.data &&
|
||||
prevProps.columns === nextProps.columns &&
|
||||
prevProps.type === nextProps.type
|
||||
);
|
||||
});
|
||||
|
||||
const RelativeDateColumnBase = (props: ItemTableListInnerColumn) => {
|
||||
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 { formattedDate, tooltipLabel } = useMemo(() => {
|
||||
if (typeof row === 'string') {
|
||||
return {
|
||||
formattedDate: formatDateRelative(row),
|
||||
tooltipLabel: getDateTooltipLabel(row),
|
||||
};
|
||||
}
|
||||
return { formattedDate: null, tooltipLabel: null };
|
||||
}, [row]);
|
||||
|
||||
if (typeof row === 'string') {
|
||||
return (
|
||||
<TableColumnTextContainer {...props}>
|
||||
<Tooltip label={getDateTooltipLabel(row)} multiline={false}>
|
||||
<span>{formatDateRelative(row)}</span>
|
||||
<Tooltip label={tooltipLabel} multiline={false}>
|
||||
<span>{formattedDate}</span>
|
||||
</Tooltip>
|
||||
</TableColumnTextContainer>
|
||||
);
|
||||
@@ -132,3 +192,12 @@ export const RelativeDateColumn = (props: ItemTableListInnerColumn) => {
|
||||
|
||||
return <ColumnSkeletonFixed {...props} />;
|
||||
};
|
||||
|
||||
export const RelativeDateColumn = memo(RelativeDateColumnBase, (prevProps, nextProps) => {
|
||||
return (
|
||||
prevProps.rowIndex === nextProps.rowIndex &&
|
||||
prevProps.columnIndex === nextProps.columnIndex &&
|
||||
prevProps.data === nextProps.data &&
|
||||
prevProps.columns === nextProps.columns
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { memo } from 'react';
|
||||
|
||||
import {
|
||||
ColumnNullFallback,
|
||||
ColumnSkeletonFixed,
|
||||
@@ -5,7 +7,7 @@ import {
|
||||
TableColumnTextContainer,
|
||||
} from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
||||
|
||||
export const DefaultColumn = (props: ItemTableListInnerColumn) => {
|
||||
const DefaultColumnBase = (props: ItemTableListInnerColumn) => {
|
||||
const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||
const row: any | undefined = (rowItem as any)?.[props.columns[props.columnIndex].id];
|
||||
|
||||
@@ -19,3 +21,12 @@ export const DefaultColumn = (props: ItemTableListInnerColumn) => {
|
||||
|
||||
return <ColumnSkeletonFixed {...props} />;
|
||||
};
|
||||
|
||||
export const DefaultColumn = memo(DefaultColumnBase, (prevProps, nextProps) => {
|
||||
return (
|
||||
prevProps.rowIndex === nextProps.rowIndex &&
|
||||
prevProps.columnIndex === nextProps.columnIndex &&
|
||||
prevProps.data === nextProps.data &&
|
||||
prevProps.columns === nextProps.columns
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import formatDuration from 'format-duration';
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
import {
|
||||
ColumnNullFallback,
|
||||
@@ -7,14 +8,16 @@ import {
|
||||
TableColumnTextContainer,
|
||||
} from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
||||
|
||||
export const DurationColumn = (props: ItemTableListInnerColumn) => {
|
||||
const DurationColumnBase = (props: ItemTableListInnerColumn) => {
|
||||
const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||
const row: number | undefined = (rowItem as any)?.[props.columns[props.columnIndex].id];
|
||||
|
||||
const formattedDuration = useMemo(() => {
|
||||
return typeof row === 'number' ? formatDuration(row) : null;
|
||||
}, [row]);
|
||||
|
||||
if (typeof row === 'number') {
|
||||
return (
|
||||
<TableColumnTextContainer {...props}>{formatDuration(row)}</TableColumnTextContainer>
|
||||
);
|
||||
return <TableColumnTextContainer {...props}>{formattedDuration}</TableColumnTextContainer>;
|
||||
}
|
||||
|
||||
if (row === null) {
|
||||
@@ -23,3 +26,12 @@ export const DurationColumn = (props: ItemTableListInnerColumn) => {
|
||||
|
||||
return <ColumnSkeletonFixed {...props} />;
|
||||
};
|
||||
|
||||
export const DurationColumn = React.memo(DurationColumnBase, (prevProps, nextProps) => {
|
||||
return (
|
||||
prevProps.rowIndex === nextProps.rowIndex &&
|
||||
prevProps.columnIndex === nextProps.columnIndex &&
|
||||
prevProps.data === nextProps.data &&
|
||||
prevProps.columns === nextProps.columns
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { memo } from 'react';
|
||||
|
||||
import {
|
||||
ItemTableListInnerColumn,
|
||||
TableColumnContainer,
|
||||
@@ -7,7 +9,7 @@ import { useIsMutatingCreateFavorite } from '/@/renderer/features/shared/mutatio
|
||||
import { useIsMutatingDeleteFavorite } from '/@/renderer/features/shared/mutations/delete-favorite-mutation';
|
||||
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
|
||||
|
||||
export const FavoriteColumn = (props: ItemTableListInnerColumn) => {
|
||||
const FavoriteColumnBase = (props: ItemTableListInnerColumn) => {
|
||||
const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||
const row: boolean | undefined = rowItem?.[props.columns[props.columnIndex].id];
|
||||
|
||||
@@ -55,3 +57,19 @@ export const FavoriteColumn = (props: ItemTableListInnerColumn) => {
|
||||
|
||||
return <TableColumnContainer {...props}> </TableColumnContainer>;
|
||||
};
|
||||
|
||||
export const FavoriteColumn = memo(FavoriteColumnBase, (prevProps, nextProps) => {
|
||||
const prevItem = prevProps.getRowItem?.(prevProps.rowIndex);
|
||||
const nextItem = nextProps.getRowItem?.(nextProps.rowIndex);
|
||||
const prevFavorite = prevItem?.[prevProps.columns[prevProps.columnIndex].id];
|
||||
const nextFavorite = nextItem?.[nextProps.columns[nextProps.columnIndex].id];
|
||||
|
||||
return (
|
||||
prevProps.rowIndex === nextProps.rowIndex &&
|
||||
prevProps.columnIndex === nextProps.columnIndex &&
|
||||
prevProps.data === nextProps.data &&
|
||||
prevProps.columns === nextProps.columns &&
|
||||
prevItem === nextItem &&
|
||||
prevFavorite === nextFavorite
|
||||
);
|
||||
});
|
||||
|
||||
@@ -60,6 +60,13 @@ const GenreBadgeColumn = (props: ItemTableListInnerColumn) => {
|
||||
return <ColumnSkeletonVariable {...props} />;
|
||||
};
|
||||
|
||||
export const GenreColumnMemo = memo(GenreBadgeColumn);
|
||||
export const GenreColumnMemo = memo(GenreBadgeColumn, (prevProps, nextProps) => {
|
||||
return (
|
||||
prevProps.rowIndex === nextProps.rowIndex &&
|
||||
prevProps.columnIndex === nextProps.columnIndex &&
|
||||
prevProps.data === nextProps.data &&
|
||||
prevProps.columns === nextProps.columns
|
||||
);
|
||||
});
|
||||
|
||||
export { GenreColumnMemo as GenreBadgeColumn };
|
||||
|
||||
@@ -64,6 +64,14 @@ const GenreColumn = (props: ItemTableListInnerColumn) => {
|
||||
return <ColumnSkeletonVariable {...props} />;
|
||||
};
|
||||
|
||||
export const GenreColumnMemo = memo(GenreColumn);
|
||||
export const GenreColumnMemo = memo(GenreColumn, (prevProps, nextProps) => {
|
||||
return (
|
||||
prevProps.rowIndex === nextProps.rowIndex &&
|
||||
prevProps.columnIndex === nextProps.columnIndex &&
|
||||
prevProps.data === nextProps.data &&
|
||||
prevProps.columns === nextProps.columns &&
|
||||
prevProps.size === nextProps.size
|
||||
);
|
||||
});
|
||||
|
||||
export { GenreColumnMemo as GenreColumn };
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import clsx from 'clsx';
|
||||
import { useState } from 'react';
|
||||
import { memo, useState } from 'react';
|
||||
|
||||
import styles from './image-column.module.css';
|
||||
|
||||
@@ -19,7 +19,7 @@ import { Skeleton } from '/@/shared/components/skeleton/skeleton';
|
||||
import { Folder, LibraryItem } from '/@/shared/types/domain-types';
|
||||
import { Play } from '/@/shared/types/types';
|
||||
|
||||
export const ImageColumn = (props: ItemTableListInnerColumn) => {
|
||||
const ImageColumnBase = (props: ItemTableListInnerColumn) => {
|
||||
const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||
const row: string | undefined = rowItem?.id;
|
||||
const item = rowItem as any;
|
||||
@@ -136,3 +136,18 @@ export const ImageColumn = (props: ItemTableListInnerColumn) => {
|
||||
</TableColumnContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export const ImageColumn = memo(ImageColumnBase, (prevProps, nextProps) => {
|
||||
const prevItem = prevProps.getRowItem?.(prevProps.rowIndex);
|
||||
const nextItem = nextProps.getRowItem?.(nextProps.rowIndex);
|
||||
|
||||
return (
|
||||
prevProps.rowIndex === nextProps.rowIndex &&
|
||||
prevProps.columnIndex === nextProps.columnIndex &&
|
||||
prevProps.data === nextProps.data &&
|
||||
prevProps.columns === nextProps.columns &&
|
||||
prevProps.itemType === nextProps.itemType &&
|
||||
prevProps.size === nextProps.size &&
|
||||
prevItem === nextItem
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { memo } from 'react';
|
||||
|
||||
import {
|
||||
ColumnNullFallback,
|
||||
ColumnSkeletonFixed,
|
||||
@@ -5,7 +7,7 @@ import {
|
||||
TableColumnTextContainer,
|
||||
} from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
||||
|
||||
export const NumericColumn = (props: ItemTableListInnerColumn) => {
|
||||
const NumericColumnBase = (props: ItemTableListInnerColumn) => {
|
||||
const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||
const row: number | undefined = (rowItem as any)?.[props.columns[props.columnIndex].id];
|
||||
|
||||
@@ -19,3 +21,12 @@ export const NumericColumn = (props: ItemTableListInnerColumn) => {
|
||||
|
||||
return <ColumnSkeletonFixed {...props} />;
|
||||
};
|
||||
|
||||
export const NumericColumn = memo(NumericColumnBase, (prevProps, nextProps) => {
|
||||
return (
|
||||
prevProps.rowIndex === nextProps.rowIndex &&
|
||||
prevProps.columnIndex === nextProps.columnIndex &&
|
||||
prevProps.data === nextProps.data &&
|
||||
prevProps.columns === nextProps.columns
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { memo } from 'react';
|
||||
|
||||
import {
|
||||
ColumnNullFallback,
|
||||
ColumnSkeletonVariable,
|
||||
@@ -5,7 +7,7 @@ import {
|
||||
TableColumnTextContainer,
|
||||
} from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
||||
|
||||
export const PathColumn = (props: ItemTableListInnerColumn) => {
|
||||
const PathColumnBase = (props: ItemTableListInnerColumn) => {
|
||||
const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||
const row: string | undefined = (rowItem as any)?.[props.columns[props.columnIndex].id];
|
||||
|
||||
@@ -23,3 +25,12 @@ export const PathColumn = (props: ItemTableListInnerColumn) => {
|
||||
|
||||
return <ColumnSkeletonVariable {...props} />;
|
||||
};
|
||||
|
||||
export const PathColumn = memo(PathColumnBase, (prevProps, nextProps) => {
|
||||
return (
|
||||
prevProps.rowIndex === nextProps.rowIndex &&
|
||||
prevProps.columnIndex === nextProps.columnIndex &&
|
||||
prevProps.data === nextProps.data &&
|
||||
prevProps.columns === nextProps.columns
|
||||
);
|
||||
});
|
||||
|
||||
+17
-2
@@ -1,4 +1,4 @@
|
||||
import { useCallback } from 'react';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useParams } from 'react-router';
|
||||
|
||||
@@ -19,7 +19,7 @@ import { useLongPress } from '/@/shared/hooks/use-long-press';
|
||||
import { LibraryItem } from '/@/shared/types/domain-types';
|
||||
import { DragOperation, DragTarget, DragTargetMap } from '/@/shared/types/drag-and-drop';
|
||||
|
||||
export const PlaylistReorderColumn = (props: ItemTableListInnerColumn) => {
|
||||
const PlaylistReorderColumnBase = (props: ItemTableListInnerColumn) => {
|
||||
const { t } = useTranslation();
|
||||
const { playlistId } = useParams() as { playlistId?: string };
|
||||
const isHeaderEnabled = !!props.enableHeader;
|
||||
@@ -363,3 +363,18 @@ export const PlaylistReorderColumn = (props: ItemTableListInnerColumn) => {
|
||||
</TableColumnContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export const PlaylistReorderColumn = memo(PlaylistReorderColumnBase, (prevProps, nextProps) => {
|
||||
const prevItem = prevProps.getRowItem?.(prevProps.rowIndex);
|
||||
const nextItem = nextProps.getRowItem?.(nextProps.rowIndex);
|
||||
|
||||
return (
|
||||
prevProps.rowIndex === nextProps.rowIndex &&
|
||||
prevProps.columnIndex === nextProps.columnIndex &&
|
||||
prevProps.data === nextProps.data &&
|
||||
prevProps.columns === nextProps.columns &&
|
||||
prevProps.itemType === nextProps.itemType &&
|
||||
prevProps.enableHeader === nextProps.enableHeader &&
|
||||
prevItem === nextItem
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { memo } from 'react';
|
||||
|
||||
import {
|
||||
ItemTableListInnerColumn,
|
||||
TableColumnContainer,
|
||||
@@ -6,7 +8,7 @@ import { ItemListItem } from '/@/renderer/components/item-list/types';
|
||||
import { useIsMutatingRating } from '/@/renderer/features/shared/mutations/set-rating-mutation';
|
||||
import { Rating } from '/@/shared/components/rating/rating';
|
||||
|
||||
export const RatingColumn = (props: ItemTableListInnerColumn) => {
|
||||
const RatingColumnBase = (props: ItemTableListInnerColumn) => {
|
||||
const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||
const row: null | number | undefined = rowItem?.[props.columns[props.columnIndex].id];
|
||||
|
||||
@@ -40,3 +42,19 @@ export const RatingColumn = (props: ItemTableListInnerColumn) => {
|
||||
|
||||
return <TableColumnContainer {...props}> </TableColumnContainer>;
|
||||
};
|
||||
|
||||
export const RatingColumn = memo(RatingColumnBase, (prevProps, nextProps) => {
|
||||
const prevItem = prevProps.getRowItem?.(prevProps.rowIndex);
|
||||
const nextItem = nextProps.getRowItem?.(nextProps.rowIndex);
|
||||
const prevRating = prevItem?.[prevProps.columns[prevProps.columnIndex].id];
|
||||
const nextRating = nextItem?.[nextProps.columns[nextProps.columnIndex].id];
|
||||
|
||||
return (
|
||||
prevProps.rowIndex === nextProps.rowIndex &&
|
||||
prevProps.columnIndex === nextProps.columnIndex &&
|
||||
prevProps.data === nextProps.data &&
|
||||
prevProps.columns === nextProps.columns &&
|
||||
prevItem === nextItem &&
|
||||
prevRating === nextRating
|
||||
);
|
||||
});
|
||||
|
||||
@@ -18,7 +18,7 @@ import { Text } from '/@/shared/components/text/text';
|
||||
import { LibraryItem, QueueSong } from '/@/shared/types/domain-types';
|
||||
import { PlayerStatus } from '/@/shared/types/types';
|
||||
|
||||
export const RowIndexColumn = (props: ItemTableListInnerColumn) => {
|
||||
const RowIndexColumnBase = (props: ItemTableListInnerColumn) => {
|
||||
const { itemType } = props;
|
||||
|
||||
switch (itemType) {
|
||||
@@ -32,6 +32,19 @@ export const RowIndexColumn = (props: ItemTableListInnerColumn) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const RowIndexColumn = memo(RowIndexColumnBase, (prevProps, nextProps) => {
|
||||
return (
|
||||
prevProps.rowIndex === nextProps.rowIndex &&
|
||||
prevProps.columnIndex === nextProps.columnIndex &&
|
||||
prevProps.data === nextProps.data &&
|
||||
prevProps.columns === nextProps.columns &&
|
||||
prevProps.itemType === nextProps.itemType &&
|
||||
prevProps.enableExpansion === nextProps.enableExpansion &&
|
||||
prevProps.enableHeader === nextProps.enableHeader &&
|
||||
prevProps.startRowIndex === nextProps.startRowIndex
|
||||
);
|
||||
});
|
||||
|
||||
const DefaultRowIndexColumn = (props: ItemTableListInnerColumn) => {
|
||||
const {
|
||||
controls,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { memo, useMemo } from 'react';
|
||||
|
||||
import {
|
||||
ColumnNullFallback,
|
||||
ColumnSkeletonFixed,
|
||||
@@ -6,14 +8,16 @@ import {
|
||||
} from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
||||
import { formatSizeString } from '/@/renderer/utils/format';
|
||||
|
||||
export const SizeColumn = (props: ItemTableListInnerColumn) => {
|
||||
const SizeColumnBase = (props: ItemTableListInnerColumn) => {
|
||||
const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||
const row: number | undefined = (rowItem as any)?.[props.columns[props.columnIndex].id];
|
||||
|
||||
const formattedSize = useMemo(() => {
|
||||
return typeof row === 'number' ? formatSizeString(row) : null;
|
||||
}, [row]);
|
||||
|
||||
if (typeof row === 'number') {
|
||||
return (
|
||||
<TableColumnTextContainer {...props}>{formatSizeString(row)}</TableColumnTextContainer>
|
||||
);
|
||||
return <TableColumnTextContainer {...props}>{formattedSize}</TableColumnTextContainer>;
|
||||
}
|
||||
|
||||
if (row === null) {
|
||||
@@ -22,3 +26,12 @@ export const SizeColumn = (props: ItemTableListInnerColumn) => {
|
||||
|
||||
return <ColumnSkeletonFixed {...props} />;
|
||||
};
|
||||
|
||||
export const SizeColumn = memo(SizeColumnBase, (prevProps, nextProps) => {
|
||||
return (
|
||||
prevProps.rowIndex === nextProps.rowIndex &&
|
||||
prevProps.columnIndex === nextProps.columnIndex &&
|
||||
prevProps.data === nextProps.data &&
|
||||
prevProps.columns === nextProps.columns
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import clsx from 'clsx';
|
||||
import { memo } from 'react';
|
||||
|
||||
import styles from './text-column.module.css';
|
||||
|
||||
@@ -9,7 +10,7 @@ import {
|
||||
TableColumnTextContainer,
|
||||
} from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
||||
|
||||
export const TextColumn = (props: ItemTableListInnerColumn) => {
|
||||
const TextColumnBase = (props: ItemTableListInnerColumn) => {
|
||||
const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||
const row: string | undefined = (rowItem as any)?.[props.columns[props.columnIndex].id];
|
||||
|
||||
@@ -33,3 +34,13 @@ export const TextColumn = (props: ItemTableListInnerColumn) => {
|
||||
|
||||
return <ColumnSkeletonVariable {...props} />;
|
||||
};
|
||||
|
||||
export const TextColumn = memo(TextColumnBase, (prevProps, nextProps) => {
|
||||
return (
|
||||
prevProps.rowIndex === nextProps.rowIndex &&
|
||||
prevProps.columnIndex === nextProps.columnIndex &&
|
||||
prevProps.data === nextProps.data &&
|
||||
prevProps.columns === nextProps.columns &&
|
||||
prevProps.size === nextProps.size
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import clsx from 'clsx';
|
||||
import { CSSProperties } from 'react';
|
||||
import { CSSProperties, memo } from 'react';
|
||||
import { Link } from 'react-router';
|
||||
|
||||
import styles from './title-artist-column.module.css';
|
||||
@@ -194,7 +194,7 @@ export const QueueSongTitleArtistColumn = (props: ItemTableListInnerColumn) => {
|
||||
return <ColumnSkeletonVariable {...props} />;
|
||||
};
|
||||
|
||||
export const TitleArtistColumn = (props: ItemTableListInnerColumn) => {
|
||||
const TitleArtistColumnBase = (props: ItemTableListInnerColumn) => {
|
||||
const { itemType } = props;
|
||||
|
||||
switch (itemType) {
|
||||
@@ -207,3 +207,18 @@ export const TitleArtistColumn = (props: ItemTableListInnerColumn) => {
|
||||
return <DefaultTitleArtistColumn {...props} />;
|
||||
}
|
||||
};
|
||||
|
||||
export const TitleArtistColumn = memo(TitleArtistColumnBase, (prevProps, nextProps) => {
|
||||
const prevItem = prevProps.getRowItem?.(prevProps.rowIndex);
|
||||
const nextItem = nextProps.getRowItem?.(nextProps.rowIndex);
|
||||
|
||||
return (
|
||||
prevProps.rowIndex === nextProps.rowIndex &&
|
||||
prevProps.columnIndex === nextProps.columnIndex &&
|
||||
prevProps.data === nextProps.data &&
|
||||
prevProps.columns === nextProps.columns &&
|
||||
prevProps.itemType === nextProps.itemType &&
|
||||
prevProps.size === nextProps.size &&
|
||||
prevItem === nextItem
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import clsx from 'clsx';
|
||||
import { memo } from 'react';
|
||||
import { Link } from 'react-router';
|
||||
|
||||
import styles from './title-column.module.css';
|
||||
@@ -14,7 +15,7 @@ import { useIsActiveRow } from '/@/renderer/components/item-list/item-table-list
|
||||
import { Text } from '/@/shared/components/text/text';
|
||||
import { LibraryItem, QueueSong } from '/@/shared/types/domain-types';
|
||||
|
||||
export const TitleColumn = (props: ItemTableListInnerColumn) => {
|
||||
const TitleColumnBase = (props: ItemTableListInnerColumn) => {
|
||||
const { itemType } = props;
|
||||
|
||||
switch (itemType) {
|
||||
@@ -28,6 +29,21 @@ export const TitleColumn = (props: ItemTableListInnerColumn) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const TitleColumn = memo(TitleColumnBase, (prevProps, nextProps) => {
|
||||
const prevItem = prevProps.getRowItem?.(prevProps.rowIndex);
|
||||
const nextItem = nextProps.getRowItem?.(nextProps.rowIndex);
|
||||
|
||||
return (
|
||||
prevProps.rowIndex === nextProps.rowIndex &&
|
||||
prevProps.columnIndex === nextProps.columnIndex &&
|
||||
prevProps.data === nextProps.data &&
|
||||
prevProps.columns === nextProps.columns &&
|
||||
prevProps.itemType === nextProps.itemType &&
|
||||
prevProps.size === nextProps.size &&
|
||||
prevItem === nextItem
|
||||
);
|
||||
});
|
||||
|
||||
function DefaultTitleColumn(props: ItemTableListInnerColumn) {
|
||||
const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||
const row: string | undefined = rowItem?.[props.columns[props.columnIndex].id];
|
||||
|
||||
+18
-1
@@ -361,7 +361,9 @@ export const QueueSongTitleCombinedColumn = (props: ItemTableListInnerColumn) =>
|
||||
return <ColumnSkeletonVariable {...props} />;
|
||||
};
|
||||
|
||||
export const TitleCombinedColumn = (props: ItemTableListInnerColumn) => {
|
||||
import { memo } from 'react';
|
||||
|
||||
const TitleCombinedColumnBase = (props: ItemTableListInnerColumn) => {
|
||||
const { itemType } = props;
|
||||
|
||||
switch (itemType) {
|
||||
@@ -374,3 +376,18 @@ export const TitleCombinedColumn = (props: ItemTableListInnerColumn) => {
|
||||
return <DefaultTitleCombinedColumn {...props} />;
|
||||
}
|
||||
};
|
||||
|
||||
export const TitleCombinedColumn = memo(TitleCombinedColumnBase, (prevProps, nextProps) => {
|
||||
const prevItem = prevProps.getRowItem?.(prevProps.rowIndex);
|
||||
const nextItem = nextProps.getRowItem?.(nextProps.rowIndex);
|
||||
|
||||
return (
|
||||
prevProps.rowIndex === nextProps.rowIndex &&
|
||||
prevProps.columnIndex === nextProps.columnIndex &&
|
||||
prevProps.data === nextProps.data &&
|
||||
prevProps.columns === nextProps.columns &&
|
||||
prevProps.itemType === nextProps.itemType &&
|
||||
prevProps.size === nextProps.size &&
|
||||
prevItem === nextItem
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { memo, useMemo } from 'react';
|
||||
|
||||
import {
|
||||
ColumnNullFallback,
|
||||
ColumnSkeletonFixed,
|
||||
@@ -6,28 +8,29 @@ import {
|
||||
} from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
||||
import { SEPARATOR_STRING } from '/@/shared/api/utils';
|
||||
|
||||
export const YearColumn = (props: ItemTableListInnerColumn) => {
|
||||
const YearColumnBase = (props: ItemTableListInnerColumn) => {
|
||||
const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||
const item = rowItem as any;
|
||||
|
||||
if (item && 'releaseYear' in item && item.releaseYear !== null) {
|
||||
const releaseYear = item.releaseYear;
|
||||
const originalYear =
|
||||
'originalYear' in item && item.originalYear !== null ? item.originalYear : null;
|
||||
const yearDisplay = useMemo(() => {
|
||||
if (item && 'releaseYear' in item && item.releaseYear !== null) {
|
||||
const releaseYear = item.releaseYear;
|
||||
const originalYear =
|
||||
'originalYear' in item && item.originalYear !== null ? item.originalYear : null;
|
||||
|
||||
if (originalYear !== null && originalYear !== releaseYear) {
|
||||
return (
|
||||
<TableColumnTextContainer {...props}>
|
||||
{originalYear}
|
||||
{SEPARATOR_STRING}
|
||||
{releaseYear}
|
||||
</TableColumnTextContainer>
|
||||
);
|
||||
}
|
||||
if (originalYear !== null && originalYear !== releaseYear) {
|
||||
return `${originalYear}${SEPARATOR_STRING}${releaseYear}`;
|
||||
}
|
||||
|
||||
if (typeof releaseYear === 'number') {
|
||||
return <TableColumnTextContainer {...props}>{releaseYear}</TableColumnTextContainer>;
|
||||
if (typeof releaseYear === 'number') {
|
||||
return releaseYear;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}, [item]);
|
||||
|
||||
if (yearDisplay !== null) {
|
||||
return <TableColumnTextContainer {...props}>{yearDisplay}</TableColumnTextContainer>;
|
||||
}
|
||||
|
||||
const row: number | undefined = (rowItem as any)?.[props.columns[props.columnIndex].id];
|
||||
@@ -38,3 +41,16 @@ export const YearColumn = (props: ItemTableListInnerColumn) => {
|
||||
|
||||
return <ColumnSkeletonFixed {...props} />;
|
||||
};
|
||||
|
||||
export const YearColumn = memo(YearColumnBase, (prevProps, nextProps) => {
|
||||
const prevItem = prevProps.getRowItem?.(prevProps.rowIndex);
|
||||
const nextItem = nextProps.getRowItem?.(nextProps.rowIndex);
|
||||
|
||||
return (
|
||||
prevProps.rowIndex === nextProps.rowIndex &&
|
||||
prevProps.columnIndex === nextProps.columnIndex &&
|
||||
prevProps.data === nextProps.data &&
|
||||
prevProps.columns === nextProps.columns &&
|
||||
prevItem === nextItem
|
||||
);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,306 @@
|
||||
import { getDraggedItems } from '/@/renderer/components/item-list/helpers/get-dragged-items';
|
||||
import { useItemDraggingState } from '/@/renderer/components/item-list/helpers/item-list-state';
|
||||
import { ItemListStateActions } from '/@/renderer/components/item-list/helpers/item-list-state';
|
||||
import { eventEmitter } from '/@/renderer/events/event-emitter';
|
||||
import { PlayerContext } from '/@/renderer/features/player/context/player-context';
|
||||
import { useDragDrop } from '/@/renderer/hooks/use-drag-drop';
|
||||
import { Folder, LibraryItem, QueueSong, Song } from '/@/shared/types/domain-types';
|
||||
import { DragOperation, DragTarget, DragTargetMap } from '/@/shared/types/drag-and-drop';
|
||||
|
||||
interface DragDropState {
|
||||
dragRef: null | React.Ref<HTMLDivElement>;
|
||||
isDraggedOver: 'bottom' | 'top' | null;
|
||||
isDragging: boolean;
|
||||
}
|
||||
|
||||
interface UseItemDragDropStateProps {
|
||||
enableDrag: boolean;
|
||||
internalState: ItemListStateActions;
|
||||
isDataRow: boolean;
|
||||
item: unknown;
|
||||
itemType: LibraryItem;
|
||||
playerContext: PlayerContext;
|
||||
playlistId?: string;
|
||||
}
|
||||
|
||||
export const useItemDragDropState = ({
|
||||
enableDrag,
|
||||
internalState,
|
||||
isDataRow,
|
||||
item,
|
||||
itemType,
|
||||
playerContext,
|
||||
playlistId,
|
||||
}: UseItemDragDropStateProps): DragDropState => {
|
||||
const shouldEnableDrag = enableDrag && isDataRow && !!item;
|
||||
|
||||
const {
|
||||
isDraggedOver,
|
||||
isDragging: isDraggingLocal,
|
||||
ref: dragRef,
|
||||
} = useDragDrop<HTMLDivElement>({
|
||||
drag: {
|
||||
getId: () => {
|
||||
if (!item || !isDataRow) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const draggedItems = getDraggedItems(item as any, internalState);
|
||||
|
||||
return draggedItems.map((draggedItem) => draggedItem.id);
|
||||
},
|
||||
getItem: () => {
|
||||
if (!item || !isDataRow) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const draggedItems = getDraggedItems(item as any, internalState);
|
||||
|
||||
return draggedItems;
|
||||
},
|
||||
itemType,
|
||||
onDragStart: () => {
|
||||
if (!item || !isDataRow) {
|
||||
return;
|
||||
}
|
||||
|
||||
const draggedItems = getDraggedItems(item as any, internalState);
|
||||
if (internalState) {
|
||||
internalState.setDragging(draggedItems);
|
||||
}
|
||||
},
|
||||
onDrop: () => {
|
||||
if (internalState) {
|
||||
internalState.setDragging([]);
|
||||
}
|
||||
},
|
||||
operation:
|
||||
itemType === LibraryItem.QUEUE_SONG
|
||||
? [DragOperation.REORDER, DragOperation.ADD]
|
||||
: itemType === LibraryItem.PLAYLIST_SONG
|
||||
? [DragOperation.REORDER, DragOperation.ADD]
|
||||
: [DragOperation.ADD],
|
||||
target: DragTargetMap[itemType] || DragTarget.GENERIC,
|
||||
},
|
||||
drop: {
|
||||
canDrop: (args) => {
|
||||
if (args.source.type === DragTarget.TABLE_COLUMN) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allow drops for QUEUE_SONG (queue reordering)
|
||||
if (itemType === LibraryItem.QUEUE_SONG) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Allow drops for PLAYLIST_SONG (playlist reordering)
|
||||
// Only allow drops when drag is started from the reorder handle
|
||||
if (
|
||||
itemType === LibraryItem.PLAYLIST_SONG &&
|
||||
args.source.itemType === LibraryItem.PLAYLIST_SONG &&
|
||||
args.source.metadata?.fromReorderHandle === true
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
getData: () => {
|
||||
return {
|
||||
id: [(item as unknown as { id: string }).id],
|
||||
item: [item as unknown as unknown[]],
|
||||
itemType,
|
||||
type: DragTargetMap[itemType] || DragTarget.GENERIC,
|
||||
};
|
||||
},
|
||||
onDrag: () => {
|
||||
return;
|
||||
},
|
||||
onDragLeave: () => {
|
||||
return;
|
||||
},
|
||||
onDrop: (args) => {
|
||||
if (args.self.type === DragTarget.QUEUE_SONG) {
|
||||
const sourceServerId = (
|
||||
args.source.item?.[0] as unknown as { _serverId: string }
|
||||
)._serverId;
|
||||
|
||||
const sourceItemType = args.source.itemType as LibraryItem;
|
||||
|
||||
const droppedOnUniqueId = (
|
||||
args.self.item?.[0] as unknown as { _uniqueId: string }
|
||||
)._uniqueId;
|
||||
|
||||
switch (args.source.type) {
|
||||
case DragTarget.ALBUM: {
|
||||
playerContext.addToQueueByFetch(
|
||||
sourceServerId,
|
||||
args.source.id,
|
||||
sourceItemType,
|
||||
{ edge: args.edge, uniqueId: droppedOnUniqueId },
|
||||
);
|
||||
break;
|
||||
}
|
||||
case DragTarget.ALBUM_ARTIST: {
|
||||
playerContext.addToQueueByFetch(
|
||||
sourceServerId,
|
||||
args.source.id,
|
||||
sourceItemType,
|
||||
{ edge: args.edge, uniqueId: droppedOnUniqueId },
|
||||
);
|
||||
break;
|
||||
}
|
||||
case DragTarget.ARTIST: {
|
||||
playerContext.addToQueueByFetch(
|
||||
sourceServerId,
|
||||
args.source.id,
|
||||
sourceItemType,
|
||||
{ edge: args.edge, uniqueId: droppedOnUniqueId },
|
||||
);
|
||||
break;
|
||||
}
|
||||
case DragTarget.FOLDER: {
|
||||
const items = args.source.item;
|
||||
|
||||
const { folders, songs } = (items || []).reduce<{
|
||||
folders: Folder[];
|
||||
songs: Song[];
|
||||
}>(
|
||||
(acc, item) => {
|
||||
if ((item as unknown as Song)._itemType === LibraryItem.SONG) {
|
||||
acc.songs.push(item as unknown as Song);
|
||||
} else if (
|
||||
(item as unknown as Folder)._itemType === LibraryItem.FOLDER
|
||||
) {
|
||||
acc.folders.push(item as unknown as Folder);
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{ folders: [], songs: [] },
|
||||
);
|
||||
|
||||
const folderIds = folders.map((folder) => folder.id);
|
||||
|
||||
// Handle folders: fetch and add to queue
|
||||
if (folderIds.length > 0) {
|
||||
playerContext.addToQueueByFetch(
|
||||
sourceServerId,
|
||||
folderIds,
|
||||
LibraryItem.FOLDER,
|
||||
{ edge: args.edge, uniqueId: droppedOnUniqueId },
|
||||
);
|
||||
}
|
||||
|
||||
// Handle songs: add directly to queue
|
||||
if (songs.length > 0) {
|
||||
playerContext.addToQueueByData(songs, {
|
||||
edge: args.edge,
|
||||
uniqueId: droppedOnUniqueId,
|
||||
});
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case DragTarget.GENRE: {
|
||||
playerContext.addToQueueByFetch(
|
||||
sourceServerId,
|
||||
args.source.id,
|
||||
sourceItemType,
|
||||
{ edge: args.edge, uniqueId: droppedOnUniqueId },
|
||||
);
|
||||
break;
|
||||
}
|
||||
case DragTarget.PLAYLIST: {
|
||||
playerContext.addToQueueByFetch(
|
||||
sourceServerId,
|
||||
args.source.id,
|
||||
sourceItemType,
|
||||
{ edge: args.edge, uniqueId: droppedOnUniqueId },
|
||||
);
|
||||
break;
|
||||
}
|
||||
case DragTarget.QUEUE_SONG: {
|
||||
const sourceItems = (args.source.item || []) as QueueSong[];
|
||||
if (
|
||||
sourceItems.length > 0 &&
|
||||
args.edge &&
|
||||
(args.edge === 'top' || args.edge === 'bottom')
|
||||
) {
|
||||
playerContext.moveSelectedTo(
|
||||
sourceItems,
|
||||
args.edge,
|
||||
droppedOnUniqueId,
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DragTarget.SONG: {
|
||||
const sourceItems = (args.source.item || []) as Song[];
|
||||
if (sourceItems.length > 0) {
|
||||
playerContext.addToQueueByData(sourceItems, {
|
||||
edge: args.edge,
|
||||
uniqueId: droppedOnUniqueId,
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle PLAYLIST_SONG reordering
|
||||
// Only allow drops when drag is started from the reorder handle
|
||||
if (
|
||||
args.self.itemType === LibraryItem.PLAYLIST_SONG &&
|
||||
args.source.itemType === LibraryItem.PLAYLIST_SONG &&
|
||||
args.source.metadata?.fromReorderHandle === true &&
|
||||
playlistId
|
||||
) {
|
||||
const sourceItems = (args.source.item || []) as any[];
|
||||
const targetItem = item as any;
|
||||
|
||||
if (
|
||||
sourceItems.length > 0 &&
|
||||
args.edge &&
|
||||
(args.edge === 'top' || args.edge === 'bottom') &&
|
||||
targetItem
|
||||
) {
|
||||
// Emit event to reorder playlist songs
|
||||
eventEmitter.emit('PLAYLIST_REORDER', {
|
||||
edge: args.edge,
|
||||
playlistId,
|
||||
sourceIds: args.source.id,
|
||||
targetId: targetItem.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (internalState) {
|
||||
internalState.setDragging([]);
|
||||
}
|
||||
|
||||
return;
|
||||
},
|
||||
},
|
||||
isEnabled: shouldEnableDrag,
|
||||
});
|
||||
|
||||
const itemRowId =
|
||||
item && typeof item === 'object' && 'id' in item && internalState
|
||||
? internalState.extractRowId(item)
|
||||
: undefined;
|
||||
const isDraggingState = useItemDraggingState(
|
||||
internalState,
|
||||
itemRowId ||
|
||||
(item && typeof item === 'object' && 'id' in item ? (item as any).id : undefined),
|
||||
);
|
||||
const isDragging = internalState ? isDraggingState : isDraggingLocal;
|
||||
|
||||
return {
|
||||
dragRef: shouldEnableDrag ? dragRef : null,
|
||||
isDraggedOver: isDraggedOver === 'top' || isDraggedOver === 'bottom' ? isDraggedOver : null,
|
||||
isDragging,
|
||||
};
|
||||
};
|
||||
@@ -10,18 +10,22 @@ import {
|
||||
} from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
|
||||
import { disableNativeDragPreview } from '@atlaskit/pragmatic-drag-and-drop/element/disable-native-drag-preview';
|
||||
import clsx from 'clsx';
|
||||
import React, { CSSProperties, ReactElement, ReactNode, useEffect, useRef, useState } from 'react';
|
||||
import React, {
|
||||
CSSProperties,
|
||||
memo,
|
||||
ReactElement,
|
||||
ReactNode,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { useParams } from 'react-router';
|
||||
import { CellComponentProps } from 'react-window-v2';
|
||||
|
||||
import styles from './item-table-list-column.module.css';
|
||||
|
||||
import i18n from '/@/i18n/i18n';
|
||||
import { getDraggedItems } from '/@/renderer/components/item-list/helpers/get-dragged-items';
|
||||
import {
|
||||
useItemDraggingState,
|
||||
useItemSelectionState,
|
||||
} from '/@/renderer/components/item-list/helpers/item-list-state';
|
||||
import { useItemSelectionState } from '/@/renderer/components/item-list/helpers/item-list-state';
|
||||
import { ActionsColumn } from '/@/renderer/components/item-list/item-table-list/columns/actions-column';
|
||||
import { AlbumArtistsColumn } from '/@/renderer/components/item-list/item-table-list/columns/album-artists-column';
|
||||
import { AlbumColumn } from '/@/renderer/components/item-list/item-table-list/columns/album-column';
|
||||
@@ -50,27 +54,22 @@ import { TitleArtistColumn } from '/@/renderer/components/item-list/item-table-l
|
||||
import { TitleColumn } from '/@/renderer/components/item-list/item-table-list/columns/title-column';
|
||||
import { TitleCombinedColumn } from '/@/renderer/components/item-list/item-table-list/columns/title-combined-column';
|
||||
import { YearColumn } from '/@/renderer/components/item-list/item-table-list/columns/year-column';
|
||||
import { useItemDragDropState } from '/@/renderer/components/item-list/item-table-list/hooks/use-item-drag-drop-state';
|
||||
import { TableItemProps } from '/@/renderer/components/item-list/item-table-list/item-table-list';
|
||||
import { ItemControls, ItemListItem } from '/@/renderer/components/item-list/types';
|
||||
import { eventEmitter } from '/@/renderer/events/event-emitter';
|
||||
import { useDragDrop } from '/@/renderer/hooks/use-drag-drop';
|
||||
import { Flex } from '/@/shared/components/flex/flex';
|
||||
import { Icon } from '/@/shared/components/icon/icon';
|
||||
import { Skeleton } from '/@/shared/components/skeleton/skeleton';
|
||||
import { Text } from '/@/shared/components/text/text';
|
||||
import { useDoubleClick } from '/@/shared/hooks/use-double-click';
|
||||
import { useMergedRef } from '/@/shared/hooks/use-merged-ref';
|
||||
import { Folder, LibraryItem, QueueSong, Song } from '/@/shared/types/domain-types';
|
||||
import {
|
||||
dndUtils,
|
||||
DragData,
|
||||
DragOperation,
|
||||
DragTarget,
|
||||
DragTargetMap,
|
||||
} from '/@/shared/types/drag-and-drop';
|
||||
import { LibraryItem } from '/@/shared/types/domain-types';
|
||||
import { dndUtils, DragData, DragOperation, DragTarget } from '/@/shared/types/drag-and-drop';
|
||||
import { TableColumn } from '/@/shared/types/types';
|
||||
|
||||
export interface ItemTableListColumn extends CellComponentProps<TableItemProps> {}
|
||||
export interface ItemTableListColumn extends CellComponentProps<TableItemProps> {
|
||||
columnType?: TableColumn;
|
||||
}
|
||||
|
||||
export interface ItemTableListInnerColumn extends ItemTableListColumn {
|
||||
controls: ItemControls;
|
||||
@@ -80,9 +79,9 @@ export interface ItemTableListInnerColumn extends ItemTableListColumn {
|
||||
type: TableColumn;
|
||||
}
|
||||
|
||||
export const ItemTableListColumn = (props: ItemTableListColumn) => {
|
||||
const ItemTableListColumnBase = (props: ItemTableListColumn) => {
|
||||
const { playlistId } = useParams() as { playlistId?: string };
|
||||
const type = props.columns[props.columnIndex].id as TableColumn;
|
||||
const type = props.columnType ?? (props.columns[props.columnIndex].id as TableColumn);
|
||||
|
||||
const isHeaderEnabled = !!props.enableHeader;
|
||||
const isDataRow = isHeaderEnabled ? props.rowIndex > 0 : true;
|
||||
@@ -127,270 +126,16 @@ export const ItemTableListColumn = (props: ItemTableListColumn) => {
|
||||
}
|
||||
}
|
||||
|
||||
const {
|
||||
isDraggedOver,
|
||||
isDragging: isDraggingLocal,
|
||||
ref: dragRef,
|
||||
} = useDragDrop<HTMLDivElement>({
|
||||
drag: {
|
||||
getId: () => {
|
||||
if (!item || !isDataRow) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const draggedItems = getDraggedItems(item as any, props.internalState);
|
||||
|
||||
return draggedItems.map((draggedItem) => draggedItem.id);
|
||||
},
|
||||
getItem: () => {
|
||||
if (!item || !isDataRow) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const draggedItems = getDraggedItems(item as any, props.internalState);
|
||||
|
||||
return draggedItems;
|
||||
},
|
||||
itemType: props.itemType,
|
||||
onDragStart: () => {
|
||||
if (!item || !isDataRow) {
|
||||
return;
|
||||
}
|
||||
|
||||
const draggedItems = getDraggedItems(item as any, props.internalState);
|
||||
if (props.internalState) {
|
||||
props.internalState.setDragging(draggedItems);
|
||||
}
|
||||
},
|
||||
onDrop: () => {
|
||||
if (props.internalState) {
|
||||
props.internalState.setDragging([]);
|
||||
}
|
||||
},
|
||||
operation:
|
||||
props.itemType === LibraryItem.QUEUE_SONG
|
||||
? [DragOperation.REORDER, DragOperation.ADD]
|
||||
: props.itemType === LibraryItem.PLAYLIST_SONG
|
||||
? [DragOperation.REORDER, DragOperation.ADD]
|
||||
: [DragOperation.ADD],
|
||||
target: DragTargetMap[props.itemType] || DragTarget.GENERIC,
|
||||
},
|
||||
drop: {
|
||||
canDrop: (args) => {
|
||||
if (args.source.type === DragTarget.TABLE_COLUMN) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allow drops for QUEUE_SONG (queue reordering)
|
||||
if (props.itemType === LibraryItem.QUEUE_SONG) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Allow drops for PLAYLIST_SONG (playlist reordering)
|
||||
// Only allow drops when drag is started from the reorder handle
|
||||
if (
|
||||
props.itemType === LibraryItem.PLAYLIST_SONG &&
|
||||
args.source.itemType === LibraryItem.PLAYLIST_SONG &&
|
||||
args.source.metadata?.fromReorderHandle === true
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
getData: () => {
|
||||
return {
|
||||
id: [(item as unknown as { id: string }).id],
|
||||
item: [item as unknown as unknown[]],
|
||||
itemType: props.itemType,
|
||||
type: DragTargetMap[props.itemType] || DragTarget.GENERIC,
|
||||
};
|
||||
},
|
||||
onDrag: () => {
|
||||
return;
|
||||
},
|
||||
onDragLeave: () => {
|
||||
return;
|
||||
},
|
||||
onDrop: (args) => {
|
||||
if (args.self.type === DragTarget.QUEUE_SONG) {
|
||||
const sourceServerId = (
|
||||
args.source.item?.[0] as unknown as { _serverId: string }
|
||||
)._serverId;
|
||||
|
||||
const sourceItemType = args.source.itemType as LibraryItem;
|
||||
|
||||
const droppedOnUniqueId = (
|
||||
args.self.item?.[0] as unknown as { _uniqueId: string }
|
||||
)._uniqueId;
|
||||
|
||||
switch (args.source.type) {
|
||||
case DragTarget.ALBUM: {
|
||||
props.playerContext.addToQueueByFetch(
|
||||
sourceServerId,
|
||||
args.source.id,
|
||||
sourceItemType,
|
||||
{ edge: args.edge, uniqueId: droppedOnUniqueId },
|
||||
);
|
||||
break;
|
||||
}
|
||||
case DragTarget.ALBUM_ARTIST: {
|
||||
props.playerContext.addToQueueByFetch(
|
||||
sourceServerId,
|
||||
args.source.id,
|
||||
sourceItemType,
|
||||
{ edge: args.edge, uniqueId: droppedOnUniqueId },
|
||||
);
|
||||
break;
|
||||
}
|
||||
case DragTarget.ARTIST: {
|
||||
props.playerContext.addToQueueByFetch(
|
||||
sourceServerId,
|
||||
args.source.id,
|
||||
sourceItemType,
|
||||
{ edge: args.edge, uniqueId: droppedOnUniqueId },
|
||||
);
|
||||
break;
|
||||
}
|
||||
case DragTarget.FOLDER: {
|
||||
const items = args.source.item;
|
||||
|
||||
const { folders, songs } = (items || []).reduce<{
|
||||
folders: Folder[];
|
||||
songs: Song[];
|
||||
}>(
|
||||
(acc, item) => {
|
||||
if ((item as unknown as Song)._itemType === LibraryItem.SONG) {
|
||||
acc.songs.push(item as unknown as Song);
|
||||
} else if (
|
||||
(item as unknown as Folder)._itemType === LibraryItem.FOLDER
|
||||
) {
|
||||
acc.folders.push(item as unknown as Folder);
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{ folders: [], songs: [] },
|
||||
);
|
||||
|
||||
const folderIds = folders.map((folder) => folder.id);
|
||||
|
||||
// Handle folders: fetch and add to queue
|
||||
if (folderIds.length > 0) {
|
||||
props.playerContext.addToQueueByFetch(
|
||||
sourceServerId,
|
||||
folderIds,
|
||||
LibraryItem.FOLDER,
|
||||
{ edge: args.edge, uniqueId: droppedOnUniqueId },
|
||||
);
|
||||
}
|
||||
|
||||
// Handle songs: add directly to queue
|
||||
if (songs.length > 0) {
|
||||
props.playerContext.addToQueueByData(songs, {
|
||||
edge: args.edge,
|
||||
uniqueId: droppedOnUniqueId,
|
||||
});
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case DragTarget.GENRE: {
|
||||
props.playerContext.addToQueueByFetch(
|
||||
sourceServerId,
|
||||
args.source.id,
|
||||
sourceItemType,
|
||||
{ edge: args.edge, uniqueId: droppedOnUniqueId },
|
||||
);
|
||||
break;
|
||||
}
|
||||
case DragTarget.PLAYLIST: {
|
||||
props.playerContext.addToQueueByFetch(
|
||||
sourceServerId,
|
||||
args.source.id,
|
||||
sourceItemType,
|
||||
{ edge: args.edge, uniqueId: droppedOnUniqueId },
|
||||
);
|
||||
break;
|
||||
}
|
||||
case DragTarget.QUEUE_SONG: {
|
||||
const sourceItems = (args.source.item || []) as QueueSong[];
|
||||
if (
|
||||
sourceItems.length > 0 &&
|
||||
args.edge &&
|
||||
(args.edge === 'top' || args.edge === 'bottom')
|
||||
) {
|
||||
props.playerContext.moveSelectedTo(
|
||||
sourceItems,
|
||||
args.edge,
|
||||
droppedOnUniqueId,
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DragTarget.SONG: {
|
||||
const sourceItems = (args.source.item || []) as Song[];
|
||||
if (sourceItems.length > 0) {
|
||||
props.playerContext.addToQueueByData(sourceItems, {
|
||||
edge: args.edge,
|
||||
uniqueId: droppedOnUniqueId,
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle PLAYLIST_SONG reordering
|
||||
// Only allow drops when drag is started from the reorder handle
|
||||
if (
|
||||
args.self.itemType === LibraryItem.PLAYLIST_SONG &&
|
||||
args.source.itemType === LibraryItem.PLAYLIST_SONG &&
|
||||
args.source.metadata?.fromReorderHandle === true &&
|
||||
playlistId
|
||||
) {
|
||||
const sourceItems = (args.source.item || []) as any[];
|
||||
const targetItem = item as any;
|
||||
|
||||
if (
|
||||
sourceItems.length > 0 &&
|
||||
args.edge &&
|
||||
(args.edge === 'top' || args.edge === 'bottom') &&
|
||||
targetItem
|
||||
) {
|
||||
// Emit event to reorder playlist songs
|
||||
eventEmitter.emit('PLAYLIST_REORDER', {
|
||||
edge: args.edge,
|
||||
playlistId,
|
||||
sourceIds: args.source.id,
|
||||
targetId: targetItem.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (props.internalState) {
|
||||
props.internalState.setDragging([]);
|
||||
}
|
||||
|
||||
return;
|
||||
},
|
||||
},
|
||||
isEnabled: shouldEnableDrag,
|
||||
const { dragRef, isDraggedOver, isDragging } = useItemDragDropState({
|
||||
enableDrag: !!props.enableDrag,
|
||||
internalState: props.internalState,
|
||||
isDataRow,
|
||||
item,
|
||||
itemType: props.itemType,
|
||||
playerContext: props.playerContext,
|
||||
playlistId,
|
||||
});
|
||||
|
||||
const itemRowId =
|
||||
item && typeof item === 'object' && 'id' in item && props.internalState
|
||||
? props.internalState.extractRowId(item)
|
||||
: undefined;
|
||||
const isDraggingState = useItemDraggingState(
|
||||
props.internalState,
|
||||
itemRowId ||
|
||||
(item && typeof item === 'object' && 'id' in item ? (item as any).id : undefined),
|
||||
);
|
||||
const isDragging = props.internalState ? isDraggingState : isDraggingLocal;
|
||||
|
||||
const controls = props.controls;
|
||||
|
||||
const dragProps = {
|
||||
@@ -583,6 +328,37 @@ export const ItemTableListColumn = (props: ItemTableListColumn) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const ItemTableListColumn = memo(ItemTableListColumnBase, (prevProps, nextProps) => {
|
||||
const prevItem = prevProps.getRowItem?.(prevProps.rowIndex);
|
||||
const nextItem = nextProps.getRowItem?.(nextProps.rowIndex);
|
||||
|
||||
return (
|
||||
prevProps.rowIndex === nextProps.rowIndex &&
|
||||
prevProps.columnIndex === nextProps.columnIndex &&
|
||||
prevProps.data === nextProps.data &&
|
||||
prevProps.columns === nextProps.columns &&
|
||||
prevProps.style === nextProps.style &&
|
||||
prevProps.columnType === nextProps.columnType &&
|
||||
prevProps.itemType === nextProps.itemType &&
|
||||
prevProps.enableHeader === nextProps.enableHeader &&
|
||||
prevProps.enableDrag === nextProps.enableDrag &&
|
||||
prevProps.groups === nextProps.groups &&
|
||||
prevProps.groupHeaderInfoByRowIndex === nextProps.groupHeaderInfoByRowIndex &&
|
||||
prevProps.pinnedLeftColumnCount === nextProps.pinnedLeftColumnCount &&
|
||||
prevProps.pinnedLeftColumnWidths === nextProps.pinnedLeftColumnWidths &&
|
||||
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 &&
|
||||
prevItem === nextItem
|
||||
);
|
||||
});
|
||||
|
||||
const NonMutedColumns = [TableColumn.TITLE, TableColumn.TITLE_ARTIST, TableColumn.TITLE_COMBINED];
|
||||
|
||||
export const TableColumnTextContainer = (
|
||||
|
||||
@@ -43,10 +43,15 @@ import { useTableKeyboardNavigation } from '/@/renderer/components/item-list/ite
|
||||
import { useTablePaneSync } from '/@/renderer/components/item-list/item-table-list/hooks/use-table-pane-sync';
|
||||
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 { ItemTableListColumn } from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
||||
import {
|
||||
ItemTableListConfigProvider,
|
||||
ItemTableListStoreProvider,
|
||||
} from '/@/renderer/components/item-list/item-table-list/item-table-list-context';
|
||||
import {
|
||||
MemoizedCellRouter,
|
||||
useColumnCellComponents,
|
||||
} from '/@/renderer/components/item-list/item-table-list/memoized-cell-router';
|
||||
import {
|
||||
ItemControls,
|
||||
ItemListHandle,
|
||||
@@ -197,6 +202,22 @@ const VirtualizedTableGrid = ({
|
||||
[calculatedColumnWidths],
|
||||
);
|
||||
|
||||
const columnWidthMemoized = useCallback(
|
||||
(index: number) => columnWidth(index + pinnedLeftColumnCount),
|
||||
[columnWidth, pinnedLeftColumnCount],
|
||||
);
|
||||
|
||||
const rowHeightMemoized = useCallback(
|
||||
(index: number, cellProps: TableItemProps) =>
|
||||
getRowHeight(index + pinnedRowCount, cellProps),
|
||||
[getRowHeight, pinnedRowCount],
|
||||
);
|
||||
|
||||
const pinnedRightColumnWidthMemoized = useCallback(
|
||||
(index: number) => columnWidth(index + pinnedLeftColumnCount + totalColumnCount),
|
||||
[columnWidth, pinnedLeftColumnCount, totalColumnCount],
|
||||
);
|
||||
|
||||
const groupHeaderInfoByRowIndex = useMemo(() => {
|
||||
if (!groups || groups.length === 0) return undefined;
|
||||
|
||||
@@ -595,14 +616,10 @@ const VirtualizedTableGrid = ({
|
||||
cellProps={itemProps}
|
||||
className={styles.height100}
|
||||
columnCount={totalColumnCount}
|
||||
columnWidth={(index) => {
|
||||
return columnWidth(index + pinnedLeftColumnCount);
|
||||
}}
|
||||
columnWidth={columnWidthMemoized}
|
||||
onCellsRendered={handleOnCellsRendered}
|
||||
rowCount={totalRowCount}
|
||||
rowHeight={(index, cellProps) => {
|
||||
return getRowHeight(index + pinnedRowCount, cellProps);
|
||||
}}
|
||||
rowHeight={rowHeightMemoized}
|
||||
/>
|
||||
{pinnedLeftColumnCount > 0 && enableScrollShadow && showLeftShadow && (
|
||||
<div className={styles.itemTableLeftScrollShadow} />
|
||||
@@ -669,15 +686,9 @@ const VirtualizedTableGrid = ({
|
||||
cellProps={itemProps}
|
||||
className={clsx(styles.noScrollbar, styles.height100)}
|
||||
columnCount={pinnedRightColumnCount}
|
||||
columnWidth={(index) => {
|
||||
return columnWidth(
|
||||
index + pinnedLeftColumnCount + totalColumnCount,
|
||||
);
|
||||
}}
|
||||
columnWidth={pinnedRightColumnWidthMemoized}
|
||||
rowCount={totalRowCount}
|
||||
rowHeight={(index, cellProps) => {
|
||||
return getRowHeight(index + pinnedRowCount, cellProps);
|
||||
}}
|
||||
rowHeight={rowHeightMemoized}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -785,7 +796,7 @@ export interface TableItemProps {
|
||||
interface ItemTableListProps {
|
||||
activeRowId?: string;
|
||||
autoFitColumns?: boolean;
|
||||
CellComponent: JSXElementConstructor<CellComponentProps<TableItemProps>>;
|
||||
CellComponent?: JSXElementConstructor<CellComponentProps<TableItemProps>>;
|
||||
cellPadding?: 'lg' | 'md' | 'sm' | 'xl' | 'xs';
|
||||
columns: ItemTableListColumnConfig[];
|
||||
data: unknown[];
|
||||
@@ -832,7 +843,7 @@ interface ItemTableListProps {
|
||||
const BaseItemTableList = ({
|
||||
activeRowId,
|
||||
autoFitColumns = false,
|
||||
CellComponent,
|
||||
CellComponent = ItemTableListColumn,
|
||||
cellPadding = 'sm',
|
||||
columns,
|
||||
data,
|
||||
@@ -1074,7 +1085,6 @@ const BaseItemTableList = ({
|
||||
});
|
||||
|
||||
const getDataFn = useCallback(() => {
|
||||
// For infinite lists, callers should pass `data` as the currently loaded items only.
|
||||
return data;
|
||||
}, [data]);
|
||||
|
||||
@@ -1082,7 +1092,6 @@ const BaseItemTableList = ({
|
||||
|
||||
const internalState = useItemListState(getDataFn, extractRowId);
|
||||
|
||||
// Helper function to get ItemListStateItemWithRequiredProperties (rowId is separate, not part of item)
|
||||
const getStateItem = useCallback(
|
||||
(item: any): ItemListStateItemWithRequiredProperties | null => {
|
||||
if (!hasRequiredItemProperties(item)) {
|
||||
@@ -1532,6 +1541,25 @@ const BaseItemTableList = ({
|
||||
],
|
||||
);
|
||||
|
||||
const columnCellComponents = useColumnCellComponents(
|
||||
parsedColumns.map((c) => c.id as TableColumn),
|
||||
itemType,
|
||||
);
|
||||
|
||||
const optimizedCellComponent = useMemo<
|
||||
JSXElementConstructor<CellComponentProps<TableItemProps>>
|
||||
>(() => {
|
||||
if (CellComponent && CellComponent !== ItemTableListColumn) {
|
||||
return CellComponent;
|
||||
}
|
||||
|
||||
return (cellProps: CellComponentProps<TableItemProps>) => {
|
||||
return (
|
||||
<MemoizedCellRouter {...cellProps} columnCellComponents={columnCellComponents} />
|
||||
);
|
||||
};
|
||||
}, [CellComponent, columnCellComponents]);
|
||||
|
||||
return (
|
||||
<ItemTableListStoreProvider activeRowId={activeRowId}>
|
||||
<ItemTableListConfigProvider value={tableConfigValue}>
|
||||
@@ -1554,7 +1582,7 @@ const BaseItemTableList = ({
|
||||
{StickyGroupRow}
|
||||
<MemoizedVirtualizedTableGrid
|
||||
calculatedColumnWidths={calculatedColumnWidths}
|
||||
CellComponent={CellComponent}
|
||||
CellComponent={optimizedCellComponent}
|
||||
cellPadding={cellPadding}
|
||||
controls={controls}
|
||||
data={data}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import React, { memo, useMemo } from 'react';
|
||||
import { CellComponentProps } from 'react-window-v2';
|
||||
|
||||
import { createColumnCellComponents } from './cell-component-factory';
|
||||
import { TableItemProps } from './item-table-list';
|
||||
import { ItemTableListColumn } from './item-table-list-column';
|
||||
|
||||
import { LibraryItem } from '/@/shared/types/domain-types';
|
||||
import { TableColumn } from '/@/shared/types/types';
|
||||
|
||||
interface MemoizedCellRouterProps extends CellComponentProps<TableItemProps> {
|
||||
columnCellComponents: Map<TableColumn, React.ComponentType<CellComponentProps<TableItemProps>>>;
|
||||
}
|
||||
|
||||
const MemoizedCellRouterBase = (props: MemoizedCellRouterProps) => {
|
||||
const columnType = props.columns[props.columnIndex]?.id as TableColumn;
|
||||
const ColumnComponent = props.columnCellComponents.get(columnType);
|
||||
|
||||
if (ColumnComponent) {
|
||||
// eslint-disable-next-line react-hooks/static-components
|
||||
return <ColumnComponent {...props} />;
|
||||
}
|
||||
|
||||
return <ItemTableListColumn {...props} />;
|
||||
};
|
||||
|
||||
export const MemoizedCellRouter = memo(MemoizedCellRouterBase, (prevProps, nextProps) => {
|
||||
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 = (
|
||||
columns: TableColumn[],
|
||||
itemType: LibraryItem,
|
||||
): Map<TableColumn, React.ComponentType<CellComponentProps<TableItemProps>>> => {
|
||||
const columnsKey = useMemo(() => columns.join(','), [columns]);
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
return useMemo(() => createColumnCellComponents(columns, itemType), [columnsKey, itemType]);
|
||||
};
|
||||
Reference in New Issue
Block a user