mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-10 04:30:25 +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 { useSearchParams } from 'react-router';
|
||||||
|
|
||||||
import { parseIntParam, setSearchParam } from '/@/renderer/utils/query-params';
|
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 scrollOffset = useMemo(() => parseIntParam(searchParams, 'scrollOffset'), [searchParams]);
|
||||||
|
|
||||||
const handleOnScrollEnd = (offset: number) => {
|
const handleOnScrollEnd = useCallback(
|
||||||
if (!enabled) return;
|
(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 };
|
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 {
|
import {
|
||||||
ItemTableListInnerColumn,
|
ItemTableListInnerColumn,
|
||||||
TableColumnContainer,
|
TableColumnContainer,
|
||||||
@@ -5,7 +7,7 @@ import {
|
|||||||
import { ItemListItem } from '/@/renderer/components/item-list/types';
|
import { ItemListItem } from '/@/renderer/components/item-list/types';
|
||||||
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
|
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 row: any = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||||
|
|
||||||
const handleActionClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
const handleActionClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
@@ -51,3 +53,16 @@ export const ActionsColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
|
|
||||||
return <TableColumnContainer {...props}> </TableColumnContainer>;
|
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} />;
|
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 };
|
export { AlbumArtistsColumnMemo as AlbumArtistsColumn };
|
||||||
|
|||||||
@@ -75,6 +75,18 @@ const AlbumColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
return <ColumnSkeletonVariable {...props} />;
|
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 };
|
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 };
|
export { ArtistsColumnMemo as ArtistsColumn };
|
||||||
|
|||||||
@@ -45,6 +45,18 @@ const ComposerColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
return <ColumnSkeletonVariable {...props} />;
|
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 };
|
export { ComposerColumnMemo as ComposerColumn };
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { memo, useMemo } from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ColumnNullFallback,
|
ColumnNullFallback,
|
||||||
ColumnSkeletonFixed,
|
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 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];
|
||||||
|
|
||||||
|
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) {
|
if (typeof row === 'string' && row) {
|
||||||
return (
|
return (
|
||||||
<TableColumnTextContainer {...props}>
|
<TableColumnTextContainer {...props}>
|
||||||
<Tooltip label={getDateTooltipLabel(row)} multiline={false}>
|
<Tooltip label={tooltipLabel} multiline={false}>
|
||||||
<span>{formatDateAbsolute(row)}</span>
|
<span>{formattedDate}</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</TableColumnTextContainer>
|
</TableColumnTextContainer>
|
||||||
);
|
);
|
||||||
@@ -50,42 +62,70 @@ export const DateColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
return <ColumnSkeletonFixed {...props} />;
|
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 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];
|
||||||
|
|
||||||
|
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) {
|
if (props.type === TableColumn.RELEASE_DATE) {
|
||||||
const item = rowItem as any;
|
if (releaseDateContent) {
|
||||||
if (item && 'releaseDate' in item && item.releaseDate) {
|
return (
|
||||||
const releaseDate = item.releaseDate;
|
<TableColumnTextContainer {...props}>
|
||||||
const originalDate =
|
<Tooltip label={releaseDateContent.tooltipLabel} multiline={false}>
|
||||||
'originalDate' in item && item.originalDate && item.originalDate !== releaseDate
|
<span>{releaseDateContent.displayText}</span>
|
||||||
? item.originalDate
|
</Tooltip>
|
||||||
: null;
|
</TableColumnTextContainer>
|
||||||
|
);
|
||||||
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 (row === null) {
|
if (row === null) {
|
||||||
@@ -98,8 +138,8 @@ export const AbsoluteDateColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
if (typeof row === 'string' && row) {
|
if (typeof row === 'string' && row) {
|
||||||
return (
|
return (
|
||||||
<TableColumnTextContainer {...props}>
|
<TableColumnTextContainer {...props}>
|
||||||
<Tooltip label={getDateTooltipLabel(row)} multiline={false}>
|
<Tooltip label={tooltipLabel} multiline={false}>
|
||||||
<span>{formatDateAbsoluteUTC(row)}</span>
|
<span>{formattedDate}</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</TableColumnTextContainer>
|
</TableColumnTextContainer>
|
||||||
);
|
);
|
||||||
@@ -112,15 +152,35 @@ export const AbsoluteDateColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
return <ColumnSkeletonFixed {...props} />;
|
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 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];
|
||||||
|
|
||||||
|
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') {
|
if (typeof row === 'string') {
|
||||||
return (
|
return (
|
||||||
<TableColumnTextContainer {...props}>
|
<TableColumnTextContainer {...props}>
|
||||||
<Tooltip label={getDateTooltipLabel(row)} multiline={false}>
|
<Tooltip label={tooltipLabel} multiline={false}>
|
||||||
<span>{formatDateRelative(row)}</span>
|
<span>{formattedDate}</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</TableColumnTextContainer>
|
</TableColumnTextContainer>
|
||||||
);
|
);
|
||||||
@@ -132,3 +192,12 @@ export const RelativeDateColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
|
|
||||||
return <ColumnSkeletonFixed {...props} />;
|
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 {
|
import {
|
||||||
ColumnNullFallback,
|
ColumnNullFallback,
|
||||||
ColumnSkeletonFixed,
|
ColumnSkeletonFixed,
|
||||||
@@ -5,7 +7,7 @@ import {
|
|||||||
TableColumnTextContainer,
|
TableColumnTextContainer,
|
||||||
} from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
} 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 rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||||
const row: any | undefined = (rowItem as any)?.[props.columns[props.columnIndex].id];
|
const row: any | undefined = (rowItem as any)?.[props.columns[props.columnIndex].id];
|
||||||
|
|
||||||
@@ -19,3 +21,12 @@ export const DefaultColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
|
|
||||||
return <ColumnSkeletonFixed {...props} />;
|
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 formatDuration from 'format-duration';
|
||||||
|
import React, { useMemo } from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ColumnNullFallback,
|
ColumnNullFallback,
|
||||||
@@ -7,14 +8,16 @@ import {
|
|||||||
TableColumnTextContainer,
|
TableColumnTextContainer,
|
||||||
} from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
} 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 rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||||
const row: number | undefined = (rowItem as any)?.[props.columns[props.columnIndex].id];
|
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') {
|
if (typeof row === 'number') {
|
||||||
return (
|
return <TableColumnTextContainer {...props}>{formattedDuration}</TableColumnTextContainer>;
|
||||||
<TableColumnTextContainer {...props}>{formatDuration(row)}</TableColumnTextContainer>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (row === null) {
|
if (row === null) {
|
||||||
@@ -23,3 +26,12 @@ export const DurationColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
|
|
||||||
return <ColumnSkeletonFixed {...props} />;
|
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 {
|
import {
|
||||||
ItemTableListInnerColumn,
|
ItemTableListInnerColumn,
|
||||||
TableColumnContainer,
|
TableColumnContainer,
|
||||||
@@ -7,7 +9,7 @@ import { useIsMutatingCreateFavorite } from '/@/renderer/features/shared/mutatio
|
|||||||
import { useIsMutatingDeleteFavorite } from '/@/renderer/features/shared/mutations/delete-favorite-mutation';
|
import { useIsMutatingDeleteFavorite } from '/@/renderer/features/shared/mutations/delete-favorite-mutation';
|
||||||
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
|
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 rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||||
const row: boolean | undefined = rowItem?.[props.columns[props.columnIndex].id];
|
const row: boolean | undefined = rowItem?.[props.columns[props.columnIndex].id];
|
||||||
|
|
||||||
@@ -55,3 +57,19 @@ export const FavoriteColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
|
|
||||||
return <TableColumnContainer {...props}> </TableColumnContainer>;
|
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} />;
|
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 };
|
export { GenreColumnMemo as GenreBadgeColumn };
|
||||||
|
|||||||
@@ -64,6 +64,14 @@ const GenreColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
return <ColumnSkeletonVariable {...props} />;
|
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 };
|
export { GenreColumnMemo as GenreColumn };
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useState } from 'react';
|
import { memo, useState } from 'react';
|
||||||
|
|
||||||
import styles from './image-column.module.css';
|
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 { Folder, LibraryItem } from '/@/shared/types/domain-types';
|
||||||
import { Play } from '/@/shared/types/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 rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||||
const row: string | undefined = rowItem?.id;
|
const row: string | undefined = rowItem?.id;
|
||||||
const item = rowItem as any;
|
const item = rowItem as any;
|
||||||
@@ -136,3 +136,18 @@ export const ImageColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
</TableColumnContainer>
|
</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 {
|
import {
|
||||||
ColumnNullFallback,
|
ColumnNullFallback,
|
||||||
ColumnSkeletonFixed,
|
ColumnSkeletonFixed,
|
||||||
@@ -5,7 +7,7 @@ import {
|
|||||||
TableColumnTextContainer,
|
TableColumnTextContainer,
|
||||||
} from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
} 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 rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||||
const row: number | undefined = (rowItem as any)?.[props.columns[props.columnIndex].id];
|
const row: number | undefined = (rowItem as any)?.[props.columns[props.columnIndex].id];
|
||||||
|
|
||||||
@@ -19,3 +21,12 @@ export const NumericColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
|
|
||||||
return <ColumnSkeletonFixed {...props} />;
|
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 {
|
import {
|
||||||
ColumnNullFallback,
|
ColumnNullFallback,
|
||||||
ColumnSkeletonVariable,
|
ColumnSkeletonVariable,
|
||||||
@@ -5,7 +7,7 @@ import {
|
|||||||
TableColumnTextContainer,
|
TableColumnTextContainer,
|
||||||
} from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
} 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 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];
|
||||||
|
|
||||||
@@ -23,3 +25,12 @@ export const PathColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
|
|
||||||
return <ColumnSkeletonVariable {...props} />;
|
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 { useTranslation } from 'react-i18next';
|
||||||
import { useParams } from 'react-router';
|
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 { LibraryItem } from '/@/shared/types/domain-types';
|
||||||
import { DragOperation, DragTarget, DragTargetMap } from '/@/shared/types/drag-and-drop';
|
import { DragOperation, DragTarget, DragTargetMap } from '/@/shared/types/drag-and-drop';
|
||||||
|
|
||||||
export const PlaylistReorderColumn = (props: ItemTableListInnerColumn) => {
|
const PlaylistReorderColumnBase = (props: ItemTableListInnerColumn) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { playlistId } = useParams() as { playlistId?: string };
|
const { playlistId } = useParams() as { playlistId?: string };
|
||||||
const isHeaderEnabled = !!props.enableHeader;
|
const isHeaderEnabled = !!props.enableHeader;
|
||||||
@@ -363,3 +363,18 @@ export const PlaylistReorderColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
</TableColumnContainer>
|
</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 {
|
import {
|
||||||
ItemTableListInnerColumn,
|
ItemTableListInnerColumn,
|
||||||
TableColumnContainer,
|
TableColumnContainer,
|
||||||
@@ -6,7 +8,7 @@ import { ItemListItem } from '/@/renderer/components/item-list/types';
|
|||||||
import { useIsMutatingRating } from '/@/renderer/features/shared/mutations/set-rating-mutation';
|
import { useIsMutatingRating } from '/@/renderer/features/shared/mutations/set-rating-mutation';
|
||||||
import { Rating } from '/@/shared/components/rating/rating';
|
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 rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||||
const row: null | number | undefined = rowItem?.[props.columns[props.columnIndex].id];
|
const row: null | number | undefined = rowItem?.[props.columns[props.columnIndex].id];
|
||||||
|
|
||||||
@@ -40,3 +42,19 @@ export const RatingColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
|
|
||||||
return <TableColumnContainer {...props}> </TableColumnContainer>;
|
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 { LibraryItem, QueueSong } from '/@/shared/types/domain-types';
|
||||||
import { PlayerStatus } from '/@/shared/types/types';
|
import { PlayerStatus } from '/@/shared/types/types';
|
||||||
|
|
||||||
export const RowIndexColumn = (props: ItemTableListInnerColumn) => {
|
const RowIndexColumnBase = (props: ItemTableListInnerColumn) => {
|
||||||
const { itemType } = props;
|
const { itemType } = props;
|
||||||
|
|
||||||
switch (itemType) {
|
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 DefaultRowIndexColumn = (props: ItemTableListInnerColumn) => {
|
||||||
const {
|
const {
|
||||||
controls,
|
controls,
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { memo, useMemo } from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ColumnNullFallback,
|
ColumnNullFallback,
|
||||||
ColumnSkeletonFixed,
|
ColumnSkeletonFixed,
|
||||||
@@ -6,14 +8,16 @@ import {
|
|||||||
} from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
} from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
||||||
import { formatSizeString } from '/@/renderer/utils/format';
|
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 rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||||
const row: number | undefined = (rowItem as any)?.[props.columns[props.columnIndex].id];
|
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') {
|
if (typeof row === 'number') {
|
||||||
return (
|
return <TableColumnTextContainer {...props}>{formattedSize}</TableColumnTextContainer>;
|
||||||
<TableColumnTextContainer {...props}>{formatSizeString(row)}</TableColumnTextContainer>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (row === null) {
|
if (row === null) {
|
||||||
@@ -22,3 +26,12 @@ export const SizeColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
|
|
||||||
return <ColumnSkeletonFixed {...props} />;
|
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 clsx from 'clsx';
|
||||||
|
import { memo } from 'react';
|
||||||
|
|
||||||
import styles from './text-column.module.css';
|
import styles from './text-column.module.css';
|
||||||
|
|
||||||
@@ -9,7 +10,7 @@ import {
|
|||||||
TableColumnTextContainer,
|
TableColumnTextContainer,
|
||||||
} from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
} 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 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];
|
||||||
|
|
||||||
@@ -33,3 +34,13 @@ export const TextColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
|
|
||||||
return <ColumnSkeletonVariable {...props} />;
|
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 clsx from 'clsx';
|
||||||
import { CSSProperties } from 'react';
|
import { CSSProperties, memo } from 'react';
|
||||||
import { Link } from 'react-router';
|
import { Link } from 'react-router';
|
||||||
|
|
||||||
import styles from './title-artist-column.module.css';
|
import styles from './title-artist-column.module.css';
|
||||||
@@ -194,7 +194,7 @@ export const QueueSongTitleArtistColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
return <ColumnSkeletonVariable {...props} />;
|
return <ColumnSkeletonVariable {...props} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const TitleArtistColumn = (props: ItemTableListInnerColumn) => {
|
const TitleArtistColumnBase = (props: ItemTableListInnerColumn) => {
|
||||||
const { itemType } = props;
|
const { itemType } = props;
|
||||||
|
|
||||||
switch (itemType) {
|
switch (itemType) {
|
||||||
@@ -207,3 +207,18 @@ export const TitleArtistColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
return <DefaultTitleArtistColumn {...props} />;
|
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 clsx from 'clsx';
|
||||||
|
import { memo } 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';
|
||||||
@@ -14,7 +15,7 @@ import { useIsActiveRow } from '/@/renderer/components/item-list/item-table-list
|
|||||||
import { Text } from '/@/shared/components/text/text';
|
import { Text } from '/@/shared/components/text/text';
|
||||||
import { LibraryItem, QueueSong } from '/@/shared/types/domain-types';
|
import { LibraryItem, QueueSong } from '/@/shared/types/domain-types';
|
||||||
|
|
||||||
export const TitleColumn = (props: ItemTableListInnerColumn) => {
|
const TitleColumnBase = (props: ItemTableListInnerColumn) => {
|
||||||
const { itemType } = props;
|
const { itemType } = props;
|
||||||
|
|
||||||
switch (itemType) {
|
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) {
|
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];
|
||||||
|
|||||||
+18
-1
@@ -361,7 +361,9 @@ export const QueueSongTitleCombinedColumn = (props: ItemTableListInnerColumn) =>
|
|||||||
return <ColumnSkeletonVariable {...props} />;
|
return <ColumnSkeletonVariable {...props} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const TitleCombinedColumn = (props: ItemTableListInnerColumn) => {
|
import { memo } from 'react';
|
||||||
|
|
||||||
|
const TitleCombinedColumnBase = (props: ItemTableListInnerColumn) => {
|
||||||
const { itemType } = props;
|
const { itemType } = props;
|
||||||
|
|
||||||
switch (itemType) {
|
switch (itemType) {
|
||||||
@@ -374,3 +376,18 @@ export const TitleCombinedColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
return <DefaultTitleCombinedColumn {...props} />;
|
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 {
|
import {
|
||||||
ColumnNullFallback,
|
ColumnNullFallback,
|
||||||
ColumnSkeletonFixed,
|
ColumnSkeletonFixed,
|
||||||
@@ -6,28 +8,29 @@ import {
|
|||||||
} from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
} from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
||||||
import { SEPARATOR_STRING } from '/@/shared/api/utils';
|
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 rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
|
||||||
const item = rowItem as any;
|
const item = rowItem as any;
|
||||||
|
|
||||||
if (item && 'releaseYear' in item && item.releaseYear !== null) {
|
const yearDisplay = useMemo(() => {
|
||||||
const releaseYear = item.releaseYear;
|
if (item && 'releaseYear' in item && item.releaseYear !== null) {
|
||||||
const originalYear =
|
const releaseYear = item.releaseYear;
|
||||||
'originalYear' in item && item.originalYear !== null ? item.originalYear : null;
|
const originalYear =
|
||||||
|
'originalYear' in item && item.originalYear !== null ? item.originalYear : null;
|
||||||
|
|
||||||
if (originalYear !== null && originalYear !== releaseYear) {
|
if (originalYear !== null && originalYear !== releaseYear) {
|
||||||
return (
|
return `${originalYear}${SEPARATOR_STRING}${releaseYear}`;
|
||||||
<TableColumnTextContainer {...props}>
|
}
|
||||||
{originalYear}
|
|
||||||
{SEPARATOR_STRING}
|
|
||||||
{releaseYear}
|
|
||||||
</TableColumnTextContainer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof releaseYear === 'number') {
|
if (typeof releaseYear === 'number') {
|
||||||
return <TableColumnTextContainer {...props}>{releaseYear}</TableColumnTextContainer>;
|
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];
|
const row: number | undefined = (rowItem as any)?.[props.columns[props.columnIndex].id];
|
||||||
@@ -38,3 +41,16 @@ export const YearColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
|
|
||||||
return <ColumnSkeletonFixed {...props} />;
|
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';
|
} from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
|
||||||
import { disableNativeDragPreview } from '@atlaskit/pragmatic-drag-and-drop/element/disable-native-drag-preview';
|
import { disableNativeDragPreview } from '@atlaskit/pragmatic-drag-and-drop/element/disable-native-drag-preview';
|
||||||
import clsx from 'clsx';
|
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 { 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';
|
||||||
|
|
||||||
import i18n from '/@/i18n/i18n';
|
import i18n from '/@/i18n/i18n';
|
||||||
import { getDraggedItems } from '/@/renderer/components/item-list/helpers/get-dragged-items';
|
import { useItemSelectionState } from '/@/renderer/components/item-list/helpers/item-list-state';
|
||||||
import {
|
|
||||||
useItemDraggingState,
|
|
||||||
useItemSelectionState,
|
|
||||||
} from '/@/renderer/components/item-list/helpers/item-list-state';
|
|
||||||
import { ActionsColumn } from '/@/renderer/components/item-list/item-table-list/columns/actions-column';
|
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 { 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';
|
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 { 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 { 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 { 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 { TableItemProps } from '/@/renderer/components/item-list/item-table-list/item-table-list';
|
||||||
import { ItemControls, ItemListItem } from '/@/renderer/components/item-list/types';
|
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 { Flex } from '/@/shared/components/flex/flex';
|
||||||
import { Icon } from '/@/shared/components/icon/icon';
|
import { Icon } from '/@/shared/components/icon/icon';
|
||||||
import { Skeleton } from '/@/shared/components/skeleton/skeleton';
|
import { Skeleton } from '/@/shared/components/skeleton/skeleton';
|
||||||
import { Text } from '/@/shared/components/text/text';
|
import { Text } from '/@/shared/components/text/text';
|
||||||
import { useDoubleClick } from '/@/shared/hooks/use-double-click';
|
import { useDoubleClick } from '/@/shared/hooks/use-double-click';
|
||||||
import { useMergedRef } from '/@/shared/hooks/use-merged-ref';
|
import { useMergedRef } from '/@/shared/hooks/use-merged-ref';
|
||||||
import { Folder, LibraryItem, QueueSong, Song } from '/@/shared/types/domain-types';
|
import { LibraryItem } from '/@/shared/types/domain-types';
|
||||||
import {
|
import { dndUtils, DragData, DragOperation, DragTarget } from '/@/shared/types/drag-and-drop';
|
||||||
dndUtils,
|
|
||||||
DragData,
|
|
||||||
DragOperation,
|
|
||||||
DragTarget,
|
|
||||||
DragTargetMap,
|
|
||||||
} from '/@/shared/types/drag-and-drop';
|
|
||||||
import { TableColumn } from '/@/shared/types/types';
|
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 {
|
export interface ItemTableListInnerColumn extends ItemTableListColumn {
|
||||||
controls: ItemControls;
|
controls: ItemControls;
|
||||||
@@ -80,9 +79,9 @@ export interface ItemTableListInnerColumn extends ItemTableListColumn {
|
|||||||
type: TableColumn;
|
type: TableColumn;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ItemTableListColumn = (props: ItemTableListColumn) => {
|
const ItemTableListColumnBase = (props: ItemTableListColumn) => {
|
||||||
const { playlistId } = useParams() as { playlistId?: string };
|
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 isHeaderEnabled = !!props.enableHeader;
|
||||||
const isDataRow = isHeaderEnabled ? props.rowIndex > 0 : true;
|
const isDataRow = isHeaderEnabled ? props.rowIndex > 0 : true;
|
||||||
@@ -127,270 +126,16 @@ export const ItemTableListColumn = (props: ItemTableListColumn) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const { dragRef, isDraggedOver, isDragging } = useItemDragDropState({
|
||||||
isDraggedOver,
|
enableDrag: !!props.enableDrag,
|
||||||
isDragging: isDraggingLocal,
|
internalState: props.internalState,
|
||||||
ref: dragRef,
|
isDataRow,
|
||||||
} = useDragDrop<HTMLDivElement>({
|
item,
|
||||||
drag: {
|
itemType: props.itemType,
|
||||||
getId: () => {
|
playerContext: props.playerContext,
|
||||||
if (!item || !isDataRow) {
|
playlistId,
|
||||||
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 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 controls = props.controls;
|
||||||
|
|
||||||
const dragProps = {
|
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];
|
const NonMutedColumns = [TableColumn.TITLE, TableColumn.TITLE_ARTIST, TableColumn.TITLE_COMBINED];
|
||||||
|
|
||||||
export const TableColumnTextContainer = (
|
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 { 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 { useTableRowModel } from '/@/renderer/components/item-list/item-table-list/hooks/use-table-row-model';
|
||||||
import { useTableScrollToIndex } from '/@/renderer/components/item-list/item-table-list/hooks/use-table-scroll-to-index';
|
import { useTableScrollToIndex } from '/@/renderer/components/item-list/item-table-list/hooks/use-table-scroll-to-index';
|
||||||
|
import { ItemTableListColumn } from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
||||||
import {
|
import {
|
||||||
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';
|
||||||
|
import {
|
||||||
|
MemoizedCellRouter,
|
||||||
|
useColumnCellComponents,
|
||||||
|
} from '/@/renderer/components/item-list/item-table-list/memoized-cell-router';
|
||||||
import {
|
import {
|
||||||
ItemControls,
|
ItemControls,
|
||||||
ItemListHandle,
|
ItemListHandle,
|
||||||
@@ -197,6 +202,22 @@ const VirtualizedTableGrid = ({
|
|||||||
[calculatedColumnWidths],
|
[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(() => {
|
const groupHeaderInfoByRowIndex = useMemo(() => {
|
||||||
if (!groups || groups.length === 0) return undefined;
|
if (!groups || groups.length === 0) return undefined;
|
||||||
|
|
||||||
@@ -595,14 +616,10 @@ const VirtualizedTableGrid = ({
|
|||||||
cellProps={itemProps}
|
cellProps={itemProps}
|
||||||
className={styles.height100}
|
className={styles.height100}
|
||||||
columnCount={totalColumnCount}
|
columnCount={totalColumnCount}
|
||||||
columnWidth={(index) => {
|
columnWidth={columnWidthMemoized}
|
||||||
return columnWidth(index + pinnedLeftColumnCount);
|
|
||||||
}}
|
|
||||||
onCellsRendered={handleOnCellsRendered}
|
onCellsRendered={handleOnCellsRendered}
|
||||||
rowCount={totalRowCount}
|
rowCount={totalRowCount}
|
||||||
rowHeight={(index, cellProps) => {
|
rowHeight={rowHeightMemoized}
|
||||||
return getRowHeight(index + pinnedRowCount, cellProps);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
{pinnedLeftColumnCount > 0 && enableScrollShadow && showLeftShadow && (
|
{pinnedLeftColumnCount > 0 && enableScrollShadow && showLeftShadow && (
|
||||||
<div className={styles.itemTableLeftScrollShadow} />
|
<div className={styles.itemTableLeftScrollShadow} />
|
||||||
@@ -669,15 +686,9 @@ const VirtualizedTableGrid = ({
|
|||||||
cellProps={itemProps}
|
cellProps={itemProps}
|
||||||
className={clsx(styles.noScrollbar, styles.height100)}
|
className={clsx(styles.noScrollbar, styles.height100)}
|
||||||
columnCount={pinnedRightColumnCount}
|
columnCount={pinnedRightColumnCount}
|
||||||
columnWidth={(index) => {
|
columnWidth={pinnedRightColumnWidthMemoized}
|
||||||
return columnWidth(
|
|
||||||
index + pinnedLeftColumnCount + totalColumnCount,
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
rowCount={totalRowCount}
|
rowCount={totalRowCount}
|
||||||
rowHeight={(index, cellProps) => {
|
rowHeight={rowHeightMemoized}
|
||||||
return getRowHeight(index + pinnedRowCount, cellProps);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -785,7 +796,7 @@ export interface TableItemProps {
|
|||||||
interface ItemTableListProps {
|
interface ItemTableListProps {
|
||||||
activeRowId?: string;
|
activeRowId?: string;
|
||||||
autoFitColumns?: boolean;
|
autoFitColumns?: boolean;
|
||||||
CellComponent: JSXElementConstructor<CellComponentProps<TableItemProps>>;
|
CellComponent?: JSXElementConstructor<CellComponentProps<TableItemProps>>;
|
||||||
cellPadding?: 'lg' | 'md' | 'sm' | 'xl' | 'xs';
|
cellPadding?: 'lg' | 'md' | 'sm' | 'xl' | 'xs';
|
||||||
columns: ItemTableListColumnConfig[];
|
columns: ItemTableListColumnConfig[];
|
||||||
data: unknown[];
|
data: unknown[];
|
||||||
@@ -832,7 +843,7 @@ interface ItemTableListProps {
|
|||||||
const BaseItemTableList = ({
|
const BaseItemTableList = ({
|
||||||
activeRowId,
|
activeRowId,
|
||||||
autoFitColumns = false,
|
autoFitColumns = false,
|
||||||
CellComponent,
|
CellComponent = ItemTableListColumn,
|
||||||
cellPadding = 'sm',
|
cellPadding = 'sm',
|
||||||
columns,
|
columns,
|
||||||
data,
|
data,
|
||||||
@@ -1074,7 +1085,6 @@ const BaseItemTableList = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const getDataFn = useCallback(() => {
|
const getDataFn = useCallback(() => {
|
||||||
// For infinite lists, callers should pass `data` as the currently loaded items only.
|
|
||||||
return data;
|
return data;
|
||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
@@ -1082,7 +1092,6 @@ const BaseItemTableList = ({
|
|||||||
|
|
||||||
const internalState = useItemListState(getDataFn, extractRowId);
|
const internalState = useItemListState(getDataFn, extractRowId);
|
||||||
|
|
||||||
// Helper function to get ItemListStateItemWithRequiredProperties (rowId is separate, not part of item)
|
|
||||||
const getStateItem = useCallback(
|
const getStateItem = useCallback(
|
||||||
(item: any): ItemListStateItemWithRequiredProperties | null => {
|
(item: any): ItemListStateItemWithRequiredProperties | null => {
|
||||||
if (!hasRequiredItemProperties(item)) {
|
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 (
|
return (
|
||||||
<ItemTableListStoreProvider activeRowId={activeRowId}>
|
<ItemTableListStoreProvider activeRowId={activeRowId}>
|
||||||
<ItemTableListConfigProvider value={tableConfigValue}>
|
<ItemTableListConfigProvider value={tableConfigValue}>
|
||||||
@@ -1554,7 +1582,7 @@ const BaseItemTableList = ({
|
|||||||
{StickyGroupRow}
|
{StickyGroupRow}
|
||||||
<MemoizedVirtualizedTableGrid
|
<MemoizedVirtualizedTableGrid
|
||||||
calculatedColumnWidths={calculatedColumnWidths}
|
calculatedColumnWidths={calculatedColumnWidths}
|
||||||
CellComponent={CellComponent}
|
CellComponent={optimizedCellComponent}
|
||||||
cellPadding={cellPadding}
|
cellPadding={cellPadding}
|
||||||
controls={controls}
|
controls={controls}
|
||||||
data={data}
|
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