mirror of
https://github.com/jeffvli/feishin.git
synced 2026-06-19 18:04:22 +02:00
add detail columns
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
import { ItemDetailListCellProps } from './types';
|
||||||
|
|
||||||
|
export const ActionsColumn = (_props: ItemDetailListCellProps) => null;
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { ItemDetailListCellProps } from './types';
|
||||||
|
|
||||||
|
export const AlbumArtistColumn = ({ song }: ItemDetailListCellProps) =>
|
||||||
|
song.albumArtistName ?? '—';
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import { ItemDetailListCellProps } from './types';
|
||||||
|
|
||||||
|
export const AlbumColumn = ({ song }: ItemDetailListCellProps) => song.album ?? '—';
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import { ItemDetailListCellProps } from './types';
|
||||||
|
|
||||||
|
export const ArtistColumn = ({ song }: ItemDetailListCellProps) => song.artistName ?? '—';
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { ItemDetailListCellProps } from './types';
|
||||||
|
|
||||||
|
export const BitDepthColumn = ({ song }: ItemDetailListCellProps) =>
|
||||||
|
song.bitDepth != null ? String(song.bitDepth) : '—';
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { ItemDetailListCellProps } from './types';
|
||||||
|
|
||||||
|
export const BitRateColumn = ({ song }: ItemDetailListCellProps) =>
|
||||||
|
song.bitRate != null ? `${song.bitRate} kbps` : '—';
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { ItemDetailListCellProps } from './types';
|
||||||
|
|
||||||
|
export const BpmColumn = ({ song }: ItemDetailListCellProps) =>
|
||||||
|
song.bpm != null ? String(song.bpm) : '—';
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { ItemDetailListCellProps } from './types';
|
||||||
|
|
||||||
|
export const ChannelsColumn = ({ song }: ItemDetailListCellProps) =>
|
||||||
|
song.channels != null ? String(song.channels) : '—';
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import { ItemDetailListCellProps } from './types';
|
||||||
|
|
||||||
|
export const CodecColumn = ({ song }: ItemDetailListCellProps) => song.container ?? '—';
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import { ItemDetailListCellProps } from './types';
|
||||||
|
|
||||||
|
export const CommentColumn = ({ song }: ItemDetailListCellProps) => song.comment ?? '—';
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { ItemDetailListCellProps } from './types';
|
||||||
|
|
||||||
|
export const ComposerColumn = ({ song }: ItemDetailListCellProps) => {
|
||||||
|
const composers = song.participants?.composer;
|
||||||
|
if (!composers?.length) return '—';
|
||||||
|
return composers.map((a) => a.name).join(', ');
|
||||||
|
};
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { ItemDetailListCellProps } from './types';
|
||||||
|
import { formatDateAbsolute } from '/@/renderer/utils/format';
|
||||||
|
|
||||||
|
export const DateAddedColumn = ({ song }: ItemDetailListCellProps) =>
|
||||||
|
song.createdAt ? formatDateAbsolute(song.createdAt) : '—';
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { ItemDetailListCellProps } from './types';
|
||||||
|
import { TableColumn } from '/@/shared/types/types';
|
||||||
|
|
||||||
|
interface DefaultColumnProps extends ItemDetailListCellProps {
|
||||||
|
columnId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DefaultColumn = ({ columnId, song }: DefaultColumnProps) => {
|
||||||
|
const raw = (song as Record<string, unknown>)[columnId];
|
||||||
|
if (raw === undefined || raw === null || typeof raw === 'object') return '—';
|
||||||
|
return String(raw);
|
||||||
|
};
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { ItemDetailListCellProps } from './types';
|
||||||
|
|
||||||
|
export const DiscNumberColumn = ({ song }: ItemDetailListCellProps) =>
|
||||||
|
String(song.discNumber ?? 1);
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import formatDuration from 'format-duration';
|
||||||
|
|
||||||
|
import { ItemDetailListCellProps } from './types';
|
||||||
|
|
||||||
|
export const DurationColumn = ({ song }: ItemDetailListCellProps) => formatDuration(song.duration);
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { ItemDetailListCellProps } from './types';
|
||||||
|
import { Icon } from '/@/shared/components/icon/icon';
|
||||||
|
|
||||||
|
export const FavoriteColumn = ({
|
||||||
|
isMutatingFavorite,
|
||||||
|
onFavoriteClick,
|
||||||
|
song,
|
||||||
|
}: ItemDetailListCellProps) => (
|
||||||
|
<div
|
||||||
|
aria-disabled={isMutatingFavorite}
|
||||||
|
onClick={(event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
onFavoriteClick?.(song);
|
||||||
|
}}
|
||||||
|
onDoubleClick={(event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
}}
|
||||||
|
role="button"
|
||||||
|
>
|
||||||
|
<Icon icon="favorite" size="xs" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { ItemDetailListCellProps } from './types';
|
||||||
|
|
||||||
|
export const GenreBadgeColumn = ({ song }: ItemDetailListCellProps) =>
|
||||||
|
song.genres?.length ? song.genres.map((g) => g.name).join(', ') : '—';
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { ItemDetailListCellProps } from './types';
|
||||||
|
|
||||||
|
export const GenreColumn = ({ song }: ItemDetailListCellProps) =>
|
||||||
|
song.genres?.length ? song.genres.map((g) => g.name).join(', ') : '—';
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { ItemDetailListCellProps } from './types';
|
||||||
|
import { ItemImage } from '/@/renderer/components/item-image/item-image';
|
||||||
|
import { LibraryItem } from '/@/shared/types/domain-types';
|
||||||
|
|
||||||
|
export const ImageColumn = ({ song }: ItemDetailListCellProps) =>
|
||||||
|
song.imageId ? (
|
||||||
|
<ItemImage id={song.imageId} itemType={LibraryItem.SONG} type="itemCard" />
|
||||||
|
) : (
|
||||||
|
'—'
|
||||||
|
);
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
import React, { type ReactNode } from 'react';
|
||||||
|
|
||||||
|
import type { ItemDetailListCellProps } from './types';
|
||||||
|
|
||||||
|
import { ActionsColumn } from './actions-column';
|
||||||
|
import { AlbumArtistColumn } from './album-artist-column';
|
||||||
|
import { AlbumColumn } from './album-column';
|
||||||
|
import { ArtistColumn } from './artist-column';
|
||||||
|
import { BitDepthColumn } from './bit-depth-column';
|
||||||
|
import { BitRateColumn } from './bit-rate-column';
|
||||||
|
import { BpmColumn } from './bpm-column';
|
||||||
|
import { ChannelsColumn } from './channels-column';
|
||||||
|
import { CodecColumn } from './codec-column';
|
||||||
|
import { CommentColumn } from './comment-column';
|
||||||
|
import { ComposerColumn } from './composer-column';
|
||||||
|
import { DateAddedColumn } from './date-added-column';
|
||||||
|
import { DefaultColumn } from './default-column';
|
||||||
|
import { DiscNumberColumn } from './disc-number-column';
|
||||||
|
import { DurationColumn } from './duration-column';
|
||||||
|
import { FavoriteColumn } from './favorite-column';
|
||||||
|
import { GenreBadgeColumn } from './genre-badge-column';
|
||||||
|
import { GenreColumn } from './genre-column';
|
||||||
|
import { ImageColumn } from './image-column';
|
||||||
|
import { LastPlayedColumn } from './last-played-column';
|
||||||
|
import { PathColumn } from './path-column';
|
||||||
|
import { PlayCountColumn } from './play-count-column';
|
||||||
|
import { RatingColumn } from './rating-column';
|
||||||
|
import { ReleaseDateColumn } from './release-date-column';
|
||||||
|
import { RowIndexColumn } from './row-index-column';
|
||||||
|
import { SampleRateColumn } from './sample-rate-column';
|
||||||
|
import { SizeColumn } from './size-column';
|
||||||
|
import { TitleArtistColumn } from './title-artist-column';
|
||||||
|
import { TitleColumn } from './title-column';
|
||||||
|
import { TitleCombinedColumn } from './title-combined-column';
|
||||||
|
import { TrackNumberColumn } from './track-number-column';
|
||||||
|
import { YearColumn } from './year-column';
|
||||||
|
|
||||||
|
import { TableColumn } from '/@/shared/types/types';
|
||||||
|
|
||||||
|
type CellComponent = (props: ItemDetailListCellProps) => ReactNode;
|
||||||
|
|
||||||
|
const COLUMN_MAP: Partial<Record<TableColumn, CellComponent>> = {
|
||||||
|
[TableColumn.ACTIONS]: ActionsColumn,
|
||||||
|
[TableColumn.ALBUM]: AlbumColumn,
|
||||||
|
[TableColumn.ALBUM_ARTIST]: AlbumArtistColumn,
|
||||||
|
[TableColumn.ARTIST]: ArtistColumn,
|
||||||
|
[TableColumn.BIT_DEPTH]: BitDepthColumn,
|
||||||
|
[TableColumn.BIT_RATE]: BitRateColumn,
|
||||||
|
[TableColumn.BPM]: BpmColumn,
|
||||||
|
[TableColumn.CHANNELS]: ChannelsColumn,
|
||||||
|
[TableColumn.CODEC]: CodecColumn,
|
||||||
|
[TableColumn.COMMENT]: CommentColumn,
|
||||||
|
[TableColumn.COMPOSER]: ComposerColumn,
|
||||||
|
[TableColumn.DATE_ADDED]: DateAddedColumn,
|
||||||
|
[TableColumn.DISC_NUMBER]: DiscNumberColumn,
|
||||||
|
[TableColumn.DURATION]: DurationColumn,
|
||||||
|
[TableColumn.GENRE]: GenreColumn,
|
||||||
|
[TableColumn.GENRE_BADGE]: GenreBadgeColumn,
|
||||||
|
[TableColumn.IMAGE]: ImageColumn,
|
||||||
|
[TableColumn.LAST_PLAYED]: LastPlayedColumn,
|
||||||
|
[TableColumn.PATH]: PathColumn,
|
||||||
|
[TableColumn.PLAY_COUNT]: PlayCountColumn,
|
||||||
|
[TableColumn.RELEASE_DATE]: ReleaseDateColumn,
|
||||||
|
[TableColumn.ROW_INDEX]: RowIndexColumn,
|
||||||
|
[TableColumn.SAMPLE_RATE]: SampleRateColumn,
|
||||||
|
[TableColumn.SIZE]: SizeColumn,
|
||||||
|
[TableColumn.TITLE]: TitleColumn,
|
||||||
|
[TableColumn.TITLE_ARTIST]: TitleArtistColumn,
|
||||||
|
[TableColumn.TITLE_COMBINED]: TitleCombinedColumn,
|
||||||
|
[TableColumn.TRACK_NUMBER]: TrackNumberColumn,
|
||||||
|
[TableColumn.USER_FAVORITE]: FavoriteColumn,
|
||||||
|
[TableColumn.USER_RATING]: RatingColumn,
|
||||||
|
[TableColumn.YEAR]: YearColumn,
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DetailListCellComponentProps = ItemDetailListCellProps & { columnId?: string };
|
||||||
|
|
||||||
|
export function getDetailListCellComponent(
|
||||||
|
columnId: string | TableColumn,
|
||||||
|
): (props: DetailListCellComponentProps) => ReactNode {
|
||||||
|
const Component = COLUMN_MAP[columnId as TableColumn];
|
||||||
|
if (Component) {
|
||||||
|
return Component as (props: DetailListCellComponentProps) => ReactNode;
|
||||||
|
}
|
||||||
|
return (props: DetailListCellComponentProps) =>
|
||||||
|
React.createElement(DefaultColumn, {
|
||||||
|
columnId: props.columnId ?? (columnId as string),
|
||||||
|
song: props.song,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export type { ItemDetailListCellProps } from './types';
|
||||||
|
|
||||||
|
export {
|
||||||
|
ActionsColumn,
|
||||||
|
AlbumArtistColumn,
|
||||||
|
AlbumColumn,
|
||||||
|
ArtistColumn,
|
||||||
|
BitDepthColumn,
|
||||||
|
BitRateColumn,
|
||||||
|
BpmColumn,
|
||||||
|
ChannelsColumn,
|
||||||
|
CodecColumn,
|
||||||
|
CommentColumn,
|
||||||
|
ComposerColumn,
|
||||||
|
DateAddedColumn,
|
||||||
|
DefaultColumn,
|
||||||
|
DiscNumberColumn,
|
||||||
|
DurationColumn,
|
||||||
|
FavoriteColumn,
|
||||||
|
GenreBadgeColumn,
|
||||||
|
GenreColumn,
|
||||||
|
ImageColumn,
|
||||||
|
LastPlayedColumn,
|
||||||
|
PathColumn,
|
||||||
|
PlayCountColumn,
|
||||||
|
RatingColumn,
|
||||||
|
ReleaseDateColumn,
|
||||||
|
RowIndexColumn,
|
||||||
|
SampleRateColumn,
|
||||||
|
SizeColumn,
|
||||||
|
TitleArtistColumn,
|
||||||
|
TitleColumn,
|
||||||
|
TitleCombinedColumn,
|
||||||
|
TrackNumberColumn,
|
||||||
|
YearColumn,
|
||||||
|
};
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { ItemDetailListCellProps } from './types';
|
||||||
|
import { formatDateRelative } from '/@/renderer/utils/format';
|
||||||
|
|
||||||
|
export const LastPlayedColumn = ({ song }: ItemDetailListCellProps) =>
|
||||||
|
song.lastPlayedAt ? formatDateRelative(song.lastPlayedAt) : '—';
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import { ItemDetailListCellProps } from './types';
|
||||||
|
|
||||||
|
export const PathColumn = ({ song }: ItemDetailListCellProps) => song.path ?? '—';
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { ItemDetailListCellProps } from './types';
|
||||||
|
|
||||||
|
export const PlayCountColumn = ({ song }: ItemDetailListCellProps) =>
|
||||||
|
String(song.playCount ?? 0);
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import { ItemDetailListCellProps } from './types';
|
||||||
|
import { ReadOnlyRating } from '/@/shared/components/read-only-rating/read-only-rating';
|
||||||
|
|
||||||
|
export const RatingColumn = ({ song }: ItemDetailListCellProps) => (
|
||||||
|
<ReadOnlyRating size="md" value={song.userRating ?? undefined} />
|
||||||
|
);
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { ItemDetailListCellProps } from './types';
|
||||||
|
import { formatDateAbsoluteUTC } from '/@/renderer/utils/format';
|
||||||
|
|
||||||
|
export const ReleaseDateColumn = ({ song }: ItemDetailListCellProps) =>
|
||||||
|
song.releaseDate ? formatDateAbsoluteUTC(song.releaseDate) : '—';
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { ItemDetailListCellProps } from './types';
|
||||||
|
|
||||||
|
export const RowIndexColumn = ({ rowIndex }: ItemDetailListCellProps) =>
|
||||||
|
String((rowIndex ?? 0) + 1);
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { ItemDetailListCellProps } from './types';
|
||||||
|
|
||||||
|
export const SampleRateColumn = ({ song }: ItemDetailListCellProps) =>
|
||||||
|
song.sampleRate != null ? `${song.sampleRate} Hz` : '—';
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { ItemDetailListCellProps } from './types';
|
||||||
|
import { formatSizeString } from '/@/renderer/utils/format';
|
||||||
|
|
||||||
|
export const SizeColumn = ({ song }: ItemDetailListCellProps) =>
|
||||||
|
song.size != null ? formatSizeString(song.size) : '—';
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { ItemDetailListCellProps } from './types';
|
||||||
|
|
||||||
|
export const TitleArtistColumn = ({ song }: ItemDetailListCellProps) =>
|
||||||
|
[song.name, song.artistName].filter(Boolean).join(' — ') || '—';
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import { ItemDetailListCellProps } from './types';
|
||||||
|
|
||||||
|
export const TitleColumn = ({ song }: ItemDetailListCellProps) => song.name ?? '—';
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { ItemDetailListCellProps } from './types';
|
||||||
|
|
||||||
|
export const TitleCombinedColumn = ({ song }: ItemDetailListCellProps) =>
|
||||||
|
[song.name, song.artistName].filter(Boolean).join(' — ') || '—';
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { ItemDetailListCellProps } from './types';
|
||||||
|
|
||||||
|
export const TrackNumberColumn = ({ song }: ItemDetailListCellProps) => {
|
||||||
|
const disc = song.discNumber ?? 1;
|
||||||
|
const track = song.trackNumber.toString().padStart(2, '0');
|
||||||
|
return `${disc} - ${track}`;
|
||||||
|
};
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { Song } from '/@/shared/types/domain-types';
|
||||||
|
|
||||||
|
export interface ItemDetailListCellProps {
|
||||||
|
isMutatingFavorite?: boolean;
|
||||||
|
onFavoriteClick?: (song: Song) => void;
|
||||||
|
rowIndex?: number;
|
||||||
|
song: Song;
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { ItemDetailListCellProps } from './types';
|
||||||
|
|
||||||
|
export const YearColumn = ({ song }: ItemDetailListCellProps) =>
|
||||||
|
song.releaseYear != null ? String(song.releaseYear) : '—';
|
||||||
+22
-53
@@ -1,5 +1,4 @@
|
|||||||
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
||||||
import formatDuration from 'format-duration';
|
|
||||||
import throttle from 'lodash/throttle';
|
import throttle from 'lodash/throttle';
|
||||||
import { AnimatePresence } from 'motion/react';
|
import { AnimatePresence } from 'motion/react';
|
||||||
import { useOverlayScrollbars } from 'overlayscrollbars-react';
|
import { useOverlayScrollbars } from 'overlayscrollbars-react';
|
||||||
@@ -19,6 +18,7 @@ import {
|
|||||||
useItemSelectionState,
|
useItemSelectionState,
|
||||||
} from '/@/renderer/components/item-list/helpers/item-list-state';
|
} from '/@/renderer/components/item-list/helpers/item-list-state';
|
||||||
import { parseTableColumns } from '/@/renderer/components/item-list/helpers/parse-table-columns';
|
import { parseTableColumns } from '/@/renderer/components/item-list/helpers/parse-table-columns';
|
||||||
|
import { getDetailListCellComponent } from '/@/renderer/components/item-list/item-detail-list/columns';
|
||||||
import {
|
import {
|
||||||
pickTableColumns,
|
pickTableColumns,
|
||||||
SONG_TABLE_COLUMNS,
|
SONG_TABLE_COLUMNS,
|
||||||
@@ -31,8 +31,6 @@ 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 { AppRoute } from '/@/renderer/router/routes';
|
import { AppRoute } from '/@/renderer/router/routes';
|
||||||
import { useSettingsStore } from '/@/renderer/store';
|
import { useSettingsStore } from '/@/renderer/store';
|
||||||
import { Icon } from '/@/shared/components/icon/icon';
|
|
||||||
import { ReadOnlyRating } from '/@/shared/components/read-only-rating/read-only-rating';
|
|
||||||
import { Skeleton } from '/@/shared/components/skeleton/skeleton';
|
import { Skeleton } from '/@/shared/components/skeleton/skeleton';
|
||||||
import { Album, LibraryItem, Song } from '/@/shared/types/domain-types';
|
import { Album, LibraryItem, Song } from '/@/shared/types/domain-types';
|
||||||
import { ItemListKey, TableColumn } from '/@/shared/types/types';
|
import { ItemListKey, TableColumn } from '/@/shared/types/types';
|
||||||
@@ -66,6 +64,7 @@ interface TrackRowProps {
|
|||||||
internalState: ItemListStateActions;
|
internalState: ItemListStateActions;
|
||||||
isMutatingFavorite: boolean;
|
isMutatingFavorite: boolean;
|
||||||
onFavoriteClick: (song: Song) => void;
|
onFavoriteClick: (song: Song) => void;
|
||||||
|
rowIndex: number;
|
||||||
song: Song;
|
song: Song;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,7 +72,14 @@ const textAlignFromAlign = (align: ItemTableListColumnConfig['align']) =>
|
|||||||
align === 'start' ? 'left' : align === 'end' ? 'right' : 'center';
|
align === 'start' ? 'left' : align === 'end' ? 'right' : 'center';
|
||||||
|
|
||||||
const TrackRow = memo(
|
const TrackRow = memo(
|
||||||
({ columns, internalState, isMutatingFavorite, onFavoriteClick, song }: TrackRowProps) => {
|
({
|
||||||
|
columns,
|
||||||
|
internalState,
|
||||||
|
isMutatingFavorite,
|
||||||
|
onFavoriteClick,
|
||||||
|
rowIndex,
|
||||||
|
song,
|
||||||
|
}: TrackRowProps) => {
|
||||||
const playerContext = usePlayer();
|
const playerContext = usePlayer();
|
||||||
const { dragRef, isDragging } = useItemDragDropState<HTMLTableRowElement>({
|
const { dragRef, isDragging } = useItemDragDropState<HTMLTableRowElement>({
|
||||||
enableDrag: true,
|
enableDrag: true,
|
||||||
@@ -83,8 +89,6 @@ const TrackRow = memo(
|
|||||||
itemType: LibraryItem.SONG,
|
itemType: LibraryItem.SONG,
|
||||||
playerContext,
|
playerContext,
|
||||||
});
|
});
|
||||||
const discAndCol =
|
|
||||||
`${song.discNumber ?? 1}` + ' - ' + song.trackNumber.toString().padStart(2, '0');
|
|
||||||
const isSelected = useItemSelectionState(internalState, song.id);
|
const isSelected = useItemSelectionState(internalState, song.id);
|
||||||
|
|
||||||
const handleRowClick = useCallback(
|
const handleRowClick = useCallback(
|
||||||
@@ -195,52 +199,16 @@ const TrackRow = memo(
|
|||||||
textAlign: textAlignFromAlign(col.align),
|
textAlign: textAlignFromAlign(col.align),
|
||||||
...widthStyle,
|
...widthStyle,
|
||||||
};
|
};
|
||||||
|
const CellComponent = getDetailListCellComponent(col.id);
|
||||||
let content: React.ReactNode;
|
const content = (
|
||||||
switch (col.id) {
|
<CellComponent
|
||||||
case TableColumn.DISC_NUMBER:
|
columnId={col.id}
|
||||||
case TableColumn.TRACK_NUMBER:
|
isMutatingFavorite={isMutatingFavorite}
|
||||||
content = discAndCol;
|
onFavoriteClick={onFavoriteClick}
|
||||||
break;
|
rowIndex={rowIndex}
|
||||||
case TableColumn.DURATION:
|
song={song}
|
||||||
content = formatDuration(song.duration);
|
/>
|
||||||
break;
|
);
|
||||||
case TableColumn.TITLE:
|
|
||||||
content = song.name;
|
|
||||||
break;
|
|
||||||
case TableColumn.USER_FAVORITE:
|
|
||||||
content = (
|
|
||||||
<div
|
|
||||||
aria-disabled={isMutatingFavorite}
|
|
||||||
onClick={(event) => {
|
|
||||||
event.stopPropagation();
|
|
||||||
event.preventDefault();
|
|
||||||
onFavoriteClick(song);
|
|
||||||
}}
|
|
||||||
onDoubleClick={(event) => {
|
|
||||||
event.stopPropagation();
|
|
||||||
event.preventDefault();
|
|
||||||
}}
|
|
||||||
role="button"
|
|
||||||
>
|
|
||||||
<Icon icon="favorite" size="xs" />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case TableColumn.USER_RATING:
|
|
||||||
content = (
|
|
||||||
<ReadOnlyRating size="md" value={song.userRating ?? undefined} />
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
default: {
|
|
||||||
const raw = (song as Record<string, unknown>)[col.id];
|
|
||||||
content =
|
|
||||||
raw !== undefined && raw !== null && typeof raw !== 'object'
|
|
||||||
? String(raw)
|
|
||||||
: '—';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<td className={styles.trackCell} key={col.id} style={style}>
|
<td className={styles.trackCell} key={col.id} style={style}>
|
||||||
@@ -378,13 +346,14 @@ const RowContent = memo(
|
|||||||
<div className={styles.right}>
|
<div className={styles.right}>
|
||||||
<table className={styles.tracksTable}>
|
<table className={styles.tracksTable}>
|
||||||
<tbody>
|
<tbody>
|
||||||
{songs.map((song) => (
|
{songs.map((song, rowIndex) => (
|
||||||
<TrackRow
|
<TrackRow
|
||||||
columns={trackColumns}
|
columns={trackColumns}
|
||||||
internalState={internalState}
|
internalState={internalState}
|
||||||
isMutatingFavorite={isMutatingFavorite}
|
isMutatingFavorite={isMutatingFavorite}
|
||||||
key={song.id}
|
key={song.id}
|
||||||
onFavoriteClick={onFavoriteClick}
|
onFavoriteClick={onFavoriteClick}
|
||||||
|
rowIndex={rowIndex}
|
||||||
song={song as Song}
|
song={song as Song}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import { UseSuspenseQueryOptions } from '@tanstack/react-query';
|
import { UseSuspenseQueryOptions } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { api } from '/@/renderer/api';
|
import { api } from '/@/renderer/api';
|
||||||
import { ItemDetailList } from '/@/renderer/components/item-detail/item-detail';
|
|
||||||
import { useItemListInfiniteLoader } from '/@/renderer/components/item-list/helpers/item-list-infinite-loader';
|
import { useItemListInfiniteLoader } from '/@/renderer/components/item-list/helpers/item-list-infinite-loader';
|
||||||
import { useItemListScrollPersist } from '/@/renderer/components/item-list/helpers/use-item-list-scroll-persist';
|
import { useItemListScrollPersist } from '/@/renderer/components/item-list/helpers/use-item-list-scroll-persist';
|
||||||
|
import { ItemDetailList } from '/@/renderer/components/item-list/item-detail-list/item-detail';
|
||||||
import { ItemListComponentProps } from '/@/renderer/components/item-list/types';
|
import { ItemListComponentProps } from '/@/renderer/components/item-list/types';
|
||||||
import { albumQueries } from '/@/renderer/features/albums/api/album-api';
|
import { albumQueries } from '/@/renderer/features/albums/api/album-api';
|
||||||
import {
|
import {
|
||||||
|
|||||||
Reference in New Issue
Block a user