From 31ccface1ebb331c5467a9bdeba78b649594bd33 Mon Sep 17 00:00:00 2001 From: jeffvli Date: Thu, 9 Oct 2025 11:06:10 -0700 Subject: [PATCH] add remaining table columns --- .../columns/actions-column.tsx | 3 +- .../columns/artists-column.module.css | 17 +++++ .../columns/artists-column.tsx | 62 +++++++++++++++++ .../columns/favorite-column.tsx | 3 +- .../item-table-list/columns/rating-column.tsx | 6 +- .../columns/row-index-column.tsx | 3 +- .../columns/title-combined-column.module.css | 30 +++++++++ .../columns/title-combined-column.tsx | 66 +++++++++++++++++++ .../item-table-list-column.module.css | 5 ++ .../item-table-list-column.tsx | 22 +++++-- 10 files changed, 207 insertions(+), 10 deletions(-) create mode 100644 src/renderer/components/item-list/item-table-list/columns/artists-column.module.css create mode 100644 src/renderer/components/item-list/item-table-list/columns/artists-column.tsx create mode 100644 src/renderer/components/item-list/item-table-list/columns/title-combined-column.module.css create mode 100644 src/renderer/components/item-list/item-table-list/columns/title-combined-column.tsx diff --git a/src/renderer/components/item-list/item-table-list/columns/actions-column.tsx b/src/renderer/components/item-list/item-table-list/columns/actions-column.tsx index cf5a0dbd1..f593b3395 100644 --- a/src/renderer/components/item-list/item-table-list/columns/actions-column.tsx +++ b/src/renderer/components/item-list/item-table-list/columns/actions-column.tsx @@ -15,8 +15,9 @@ export const ActionsColumn = (props: ItemTableListInnerColumn) => { icon="ellipsisHorizontal" iconProps={{ color: 'muted', - size: 'lg', + size: 'md', }} + size="xs" variant="subtle" /> diff --git a/src/renderer/components/item-list/item-table-list/columns/artists-column.module.css b/src/renderer/components/item-list/item-table-list/columns/artists-column.module.css new file mode 100644 index 000000000..893343a7f --- /dev/null +++ b/src/renderer/components/item-list/item-table-list/columns/artists-column.module.css @@ -0,0 +1,17 @@ +.group { + gap: var(--theme-spacing-sm) var(--theme-spacing-xs); + overflow: hidden; +} + +.group a { + cursor: pointer; +} + +.artists-container { + display: -webkit-box; + overflow: hidden; + -webkit-line-clamp: 2; + line-height: 1.4; + -webkit-box-orient: vertical; + color: var(--theme-colors-foreground-muted); +} diff --git a/src/renderer/components/item-list/item-table-list/columns/artists-column.tsx b/src/renderer/components/item-list/item-table-list/columns/artists-column.tsx new file mode 100644 index 000000000..797280cef --- /dev/null +++ b/src/renderer/components/item-list/item-table-list/columns/artists-column.tsx @@ -0,0 +1,62 @@ +import { memo, useMemo } from 'react'; +import { generatePath, Link } from 'react-router-dom'; + +import styles from './album-artists-column.module.css'; + +import { + ItemTableListInnerColumn, + TableColumnContainer, + TableColumnTextContainer, +} from '/@/renderer/components/item-list/item-table-list/item-table-list-column'; +import { AppRoute } from '/@/renderer/router/routes'; +import { Skeleton } from '/@/shared/components/skeleton/skeleton'; +import { Text } from '/@/shared/components/text/text'; +import { RelatedAlbumArtist } from '/@/shared/types/domain-types'; + +const ArtistsColumn = (props: ItemTableListInnerColumn) => { + const row: RelatedAlbumArtist[] | undefined = ( + props.data as (RelatedAlbumArtist[] | undefined)[] + )[props.rowIndex]?.[props.columns[props.columnIndex].id]; + + const artists = useMemo(() => { + if (!row) return []; + return row.map((artist) => { + const path = generatePath(AppRoute.LIBRARY_ARTISTS_DETAIL, { + artistId: artist.id, + }); + return { ...artist, path }; + }); + }, [row]); + + if (Array.isArray(row)) { + return ( + +
+ {artists.map((artist, index) => ( + + {artist.name} + {index < artists.length - 1 && ', '} + + ))} +
+
+ ); + } + + return ( + + + + ); +}; + +export const ArtistsColumnMemo = memo(ArtistsColumn); + +export { ArtistsColumnMemo as ArtistsColumn }; diff --git a/src/renderer/components/item-list/item-table-list/columns/favorite-column.tsx b/src/renderer/components/item-list/item-table-list/columns/favorite-column.tsx index 907e3a17d..e7ead4fcc 100644 --- a/src/renderer/components/item-list/item-table-list/columns/favorite-column.tsx +++ b/src/renderer/components/item-list/item-table-list/columns/favorite-column.tsx @@ -18,8 +18,9 @@ export const FavoriteColumn = (props: ItemTableListInnerColumn) => { iconProps={{ color: row ? 'primary' : 'muted', fill: row ? 'primary' : undefined, - size: 'lg', + size: 'md', }} + size="xs" variant="subtle" /> diff --git a/src/renderer/components/item-list/item-table-list/columns/rating-column.tsx b/src/renderer/components/item-list/item-table-list/columns/rating-column.tsx index 67e25c08b..49c83ea08 100644 --- a/src/renderer/components/item-list/item-table-list/columns/rating-column.tsx +++ b/src/renderer/components/item-list/item-table-list/columns/rating-column.tsx @@ -12,7 +12,11 @@ export const RatingColumn = (props: ItemTableListInnerColumn) => { if (typeof row === 'number' || row === null) { return ( - + ); } diff --git a/src/renderer/components/item-list/item-table-list/columns/row-index-column.tsx b/src/renderer/components/item-list/item-table-list/columns/row-index-column.tsx index 4069f57ff..d4e634b8f 100644 --- a/src/renderer/components/item-list/item-table-list/columns/row-index-column.tsx +++ b/src/renderer/components/item-list/item-table-list/columns/row-index-column.tsx @@ -19,7 +19,7 @@ export const RowIndexColumn = (props: ItemTableListInnerColumn) => { controls.onItemExpand?.( props.data[props.rowIndex] as any, @@ -27,6 +27,7 @@ export const RowIndexColumn = (props: ItemTableListInnerColumn) => { e, ) } + size="xs" variant="subtle" /> diff --git a/src/renderer/components/item-list/item-table-list/columns/title-combined-column.module.css b/src/renderer/components/item-list/item-table-list/columns/title-combined-column.module.css new file mode 100644 index 000000000..94fef673e --- /dev/null +++ b/src/renderer/components/item-list/item-table-list/columns/title-combined-column.module.css @@ -0,0 +1,30 @@ +.title-combined { + display: grid; + grid-template-columns: calc(var(--row-height) - var(--theme-spacing-sm)) 1fr; + gap: var(--theme-spacing-sm); + align-items: center; + height: 100%; +} + +.text-container { + display: grid; + grid-template-rows: 1fr 1fr; + gap: 2px; + min-width: 0; +} + +.title { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.artists { + display: block; + overflow: hidden; + text-overflow: ellipsis; + font-size: 0.875rem; + color: var(--theme-colors-foreground-muted); + white-space: nowrap; + user-select: none; +} diff --git a/src/renderer/components/item-list/item-table-list/columns/title-combined-column.tsx b/src/renderer/components/item-list/item-table-list/columns/title-combined-column.tsx new file mode 100644 index 000000000..48447f7d1 --- /dev/null +++ b/src/renderer/components/item-list/item-table-list/columns/title-combined-column.tsx @@ -0,0 +1,66 @@ +import { CSSProperties, useMemo } from 'react'; +import { generatePath, Link } from 'react-router-dom'; + +import styles from './title-combined-column.module.css'; + +import { + ItemTableListInnerColumn, + TableColumnContainer, + TableColumnTextContainer, +} from '/@/renderer/components/item-list/item-table-list/item-table-list-column'; +import { AppRoute } from '/@/renderer/router/routes'; +import { Image } from '/@/shared/components/image/image'; +import { Skeleton } from '/@/shared/components/skeleton/skeleton'; +import { Text } from '/@/shared/components/text/text'; +import { RelatedAlbumArtist } from '/@/shared/types/domain-types'; + +export const TitleCombinedColumn = (props: ItemTableListInnerColumn) => { + const row: object | undefined = (props.data as (any | undefined)[])[props.rowIndex]; + + const artists = useMemo(() => { + if (row && 'artists' in row && Array.isArray(row.artists)) { + return (row.artists as RelatedAlbumArtist[]).map((artist) => { + const path = generatePath(AppRoute.LIBRARY_ARTISTS_DETAIL, { + artistId: artist.id, + }); + return { ...artist, path }; + }); + } + return []; + }, [row]); + + if (row && 'name' in row && 'imageUrl' in row && 'artists' in row) { + const rowHeight = props.getRowHeight(props.rowIndex, props); + + return ( + + +
+ + {row.name as string} + +
+ {artists.map((artist, index) => ( + + + {artist.name} + + {index < artists.length - 1 && ', '} + + ))} +
+
+
+ ); + } + + return ( + + + + ); +}; diff --git a/src/renderer/components/item-list/item-table-list/item-table-list-column.module.css b/src/renderer/components/item-list/item-table-list/item-table-list-column.module.css index 7f797673f..3dedafff0 100644 --- a/src/renderer/components/item-list/item-table-list/item-table-list-column.module.css +++ b/src/renderer/components/item-list/item-table-list/item-table-list-column.module.css @@ -32,6 +32,11 @@ line-height: 1.3; } +.content.compact { + -webkit-line-clamp: 1; + line-clamp: 1; +} + .container.compact { padding: var(--theme-spacing-xs); } diff --git a/src/renderer/components/item-list/item-table-list/item-table-list-column.tsx b/src/renderer/components/item-list/item-table-list/item-table-list-column.tsx index 9a8b6010d..851ce83cd 100644 --- a/src/renderer/components/item-list/item-table-list/item-table-list-column.tsx +++ b/src/renderer/components/item-list/item-table-list/item-table-list-column.tsx @@ -1,5 +1,5 @@ import clsx from 'clsx'; -import React, { ReactNode, useEffect, useRef } from 'react'; +import React, { CSSProperties, ReactNode, useEffect, useRef } from 'react'; import { CellComponentProps } from 'react-window-v2'; import styles from './item-table-list-column.module.css'; @@ -8,6 +8,7 @@ import i18n from '/@/i18n/i18n'; import { itemListControls } from '/@/renderer/components/item-list/helpers/item-list-controls'; 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 { ArtistsColumn } from '/@/renderer/components/item-list/item-table-list/columns/artists-column'; import { CountColumn } from '/@/renderer/components/item-list/item-table-list/columns/count-column'; import { DateColumn, @@ -24,6 +25,7 @@ import { RatingColumn } from '/@/renderer/components/item-list/item-table-list/c import { RowIndexColumn } from '/@/renderer/components/item-list/item-table-list/columns/row-index-column'; import { SizeColumn } from '/@/renderer/components/item-list/item-table-list/columns/size-column'; import { TextColumn } from '/@/renderer/components/item-list/item-table-list/columns/text-column'; +import { TitleCombinedColumn } from '/@/renderer/components/item-list/item-table-list/columns/title-combined-column'; import { TableItemProps } from '/@/renderer/components/item-list/item-table-list/item-table-list'; import { ItemControls } from '/@/renderer/components/item-list/types'; import { Icon } from '/@/shared/components/icon/icon'; @@ -82,6 +84,9 @@ export const ItemTableListColumn = (props: ItemTableListColumn) => { case TableColumn.SONG_COUNT: return ; + case TableColumn.ARTIST: + return ; + case TableColumn.BIOGRAPHY: case TableColumn.COMMENT: return ; @@ -119,6 +124,9 @@ export const ItemTableListColumn = (props: ItemTableListColumn) => { case TableColumn.SIZE: return ; + case TableColumn.TITLE_COMBINED: + return ; + case TableColumn.USER_FAVORITE: return ; @@ -189,7 +197,9 @@ export const TableColumnTextContainer = ( style={props.style} > @@ -203,7 +213,7 @@ export const TableColumnContainer = ( props: ItemTableListColumn & { children: React.ReactNode; className?: string; - containerClassName?: string; + containerStyle?: CSSProperties; controls: ItemControls; type: TableColumn; }, @@ -241,7 +251,7 @@ export const TableColumnContainer = ( return (
{props.children}
@@ -333,7 +343,7 @@ const columnLabelMap: Record = { postProcess: 'upperCase', }) as string, [TableColumn.TITLE]: i18n.t('table.column.title', { postProcess: 'upperCase' }) as string, - [TableColumn.TITLE_COMBINED]: i18n.t('table.column.titleCombined', { + [TableColumn.TITLE_COMBINED]: i18n.t('table.column.title', { postProcess: 'upperCase', }) as string, [TableColumn.TRACK_NUMBER]: i18n.t('table.column.trackNumber', {