mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-07 04:20:12 +02:00
add large table size
This commit is contained in:
+8
-2
@@ -1,12 +1,13 @@
|
||||
import { ItemTableListColumnConfig } from '/@/renderer/components/item-list/types';
|
||||
|
||||
/**
|
||||
* Sorts table columns by their pinned position:
|
||||
* Sorts table columns by their pinned position and filters out disabled columns:
|
||||
* - Left pinned columns come first (maintaining their original order)
|
||||
* - Unpinned columns come next (maintaining their original order)
|
||||
* - Right pinned columns come last (maintaining their original order)
|
||||
* - Columns with isEnabled: false are removed
|
||||
*/
|
||||
export const sortTableColumns = (
|
||||
export const parseTableColumns = (
|
||||
columns: ItemTableListColumnConfig[],
|
||||
): ItemTableListColumnConfig[] => {
|
||||
const leftPinned: ItemTableListColumnConfig[] = [];
|
||||
@@ -14,7 +15,12 @@ export const sortTableColumns = (
|
||||
const rightPinned: ItemTableListColumnConfig[] = [];
|
||||
|
||||
// Separate columns by pinned position while maintaining original order
|
||||
// Only include columns that are enabled (isEnabled !== false)
|
||||
columns.forEach((column) => {
|
||||
if (column.isEnabled === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (column.pinned) {
|
||||
case 'left':
|
||||
leftPinned.push(column);
|
||||
+8
-1
@@ -11,7 +11,14 @@
|
||||
display: -webkit-box;
|
||||
overflow: hidden;
|
||||
-webkit-line-clamp: 2;
|
||||
line-height: 1.4;
|
||||
-webkit-box-orient: vertical;
|
||||
color: var(--theme-colors-foreground-muted);
|
||||
}
|
||||
|
||||
.artists-container.compact {
|
||||
-webkit-line-clamp: 1;
|
||||
}
|
||||
|
||||
.artists-container.large {
|
||||
-webkit-line-clamp: 3;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import clsx from 'clsx';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { generatePath, Link } from 'react-router-dom';
|
||||
|
||||
@@ -31,7 +32,12 @@ const AlbumArtistsColumn = (props: ItemTableListInnerColumn) => {
|
||||
if (Array.isArray(row)) {
|
||||
return (
|
||||
<TableColumnContainer {...props}>
|
||||
<div className={styles.artistsContainer}>
|
||||
<div
|
||||
className={clsx(styles.artistsContainer, {
|
||||
[styles.compact]: props.size === 'compact',
|
||||
[styles.large]: props.size === 'large',
|
||||
})}
|
||||
>
|
||||
{albumArtists.map((albumArtist, index) => (
|
||||
<Text
|
||||
component={Link}
|
||||
|
||||
@@ -11,7 +11,14 @@
|
||||
display: -webkit-box;
|
||||
overflow: hidden;
|
||||
-webkit-line-clamp: 2;
|
||||
line-height: 1.4;
|
||||
-webkit-box-orient: vertical;
|
||||
color: var(--theme-colors-foreground-muted);
|
||||
}
|
||||
|
||||
.artists-container.compact {
|
||||
-webkit-line-clamp: 1;
|
||||
}
|
||||
|
||||
.artists-container.large {
|
||||
-webkit-line-clamp: 3;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import clsx from 'clsx';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { generatePath, Link } from 'react-router-dom';
|
||||
|
||||
@@ -31,7 +32,12 @@ const ArtistsColumn = (props: ItemTableListInnerColumn) => {
|
||||
if (Array.isArray(row)) {
|
||||
return (
|
||||
<TableColumnContainer {...props}>
|
||||
<div className={styles.artistsContainer}>
|
||||
<div
|
||||
className={clsx(styles.artistsContainer, {
|
||||
[styles.compact]: props.size === 'compact',
|
||||
[styles.large]: props.size === 'large',
|
||||
})}
|
||||
>
|
||||
{artists.map((artist, index) => (
|
||||
<Text
|
||||
component={Link}
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
.group {
|
||||
gap: var(--theme-spacing-sm) var(--theme-spacing-xs);
|
||||
padding: var(--theme-spacing-xs) 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.group a {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import { memo, useMemo } from 'react';
|
||||
import { generatePath, Link } from 'react-router-dom';
|
||||
|
||||
import styles from './genre-badge-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 { Badge } from '/@/shared/components/badge/badge';
|
||||
import { Group } from '/@/shared/components/group/group';
|
||||
import { Skeleton } from '/@/shared/components/skeleton/skeleton';
|
||||
import { Genre } from '/@/shared/types/domain-types';
|
||||
import { stringToColor } from '/@/shared/utils/string-to-color';
|
||||
|
||||
const MAX_GENRES = 4;
|
||||
|
||||
const GenreBadgeColumn = (props: ItemTableListInnerColumn) => {
|
||||
const row: Genre[] | undefined = (props.data as (Genre[] | undefined)[])[props.rowIndex]?.[
|
||||
'genres'
|
||||
];
|
||||
|
||||
const genres = useMemo(() => {
|
||||
if (!row) return [];
|
||||
return row.map((genre) => {
|
||||
const { color, isLight } = stringToColor(genre.name);
|
||||
const path = generatePath(AppRoute.LIBRARY_GENRES_ALBUMS, { genreId: genre.id });
|
||||
return { ...genre, color, isLight, path };
|
||||
});
|
||||
}, [row]);
|
||||
|
||||
if (Array.isArray(row)) {
|
||||
return (
|
||||
<TableColumnContainer {...props}>
|
||||
<Group className={styles.group} wrap="wrap">
|
||||
{genres.slice(0, MAX_GENRES).map((genre) => (
|
||||
<Badge
|
||||
component={Link}
|
||||
key={genre.id}
|
||||
style={{
|
||||
backgroundColor: genre.color,
|
||||
color: genre.isLight ? 'black' : 'white',
|
||||
}}
|
||||
to={genre.path}
|
||||
>
|
||||
{genre.name}
|
||||
</Badge>
|
||||
))}
|
||||
</Group>
|
||||
</TableColumnContainer>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<TableColumnTextContainer {...props}>
|
||||
<Skeleton />
|
||||
</TableColumnTextContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export const GenreColumnMemo = memo(GenreBadgeColumn);
|
||||
|
||||
export { GenreColumnMemo as GenreBadgeColumn };
|
||||
@@ -1,10 +1,15 @@
|
||||
.group {
|
||||
gap: var(--theme-spacing-sm) var(--theme-spacing-xs);
|
||||
padding: var(--theme-spacing-xs) 0;
|
||||
.genres-container {
|
||||
display: -webkit-box;
|
||||
overflow: hidden;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
color: var(--theme-colors-foreground-muted);
|
||||
}
|
||||
|
||||
.group a {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
.genres-container.compact {
|
||||
-webkit-line-clamp: 1;
|
||||
}
|
||||
|
||||
.genres-container.large {
|
||||
-webkit-line-clamp: 3;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import clsx from 'clsx';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { generatePath, Link } from 'react-router-dom';
|
||||
|
||||
@@ -9,13 +10,9 @@ import {
|
||||
TableColumnTextContainer,
|
||||
} from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
||||
import { AppRoute } from '/@/renderer/router/routes';
|
||||
import { Badge } from '/@/shared/components/badge/badge';
|
||||
import { Group } from '/@/shared/components/group/group';
|
||||
import { Skeleton } from '/@/shared/components/skeleton/skeleton';
|
||||
import { Text } from '/@/shared/components/text/text';
|
||||
import { Genre } from '/@/shared/types/domain-types';
|
||||
import { stringToColor } from '/@/shared/utils/string-to-color';
|
||||
|
||||
const MAX_GENRES = 4;
|
||||
|
||||
const GenreColumn = (props: ItemTableListInnerColumn) => {
|
||||
const row: Genre[] | undefined = (props.data as (Genre[] | undefined)[])[props.rowIndex]?.[
|
||||
@@ -25,30 +22,36 @@ const GenreColumn = (props: ItemTableListInnerColumn) => {
|
||||
const genres = useMemo(() => {
|
||||
if (!row) return [];
|
||||
return row.map((genre) => {
|
||||
const { color, isLight } = stringToColor(genre.name);
|
||||
const path = generatePath(AppRoute.LIBRARY_GENRES_ALBUMS, { genreId: genre.id });
|
||||
return { ...genre, color, isLight, path };
|
||||
const path = generatePath(AppRoute.LIBRARY_GENRES_ALBUMS, {
|
||||
genreId: genre.id,
|
||||
});
|
||||
return { ...genre, path };
|
||||
});
|
||||
}, [row]);
|
||||
|
||||
if (Array.isArray(row)) {
|
||||
return (
|
||||
<TableColumnContainer {...props}>
|
||||
<Group className={styles.group} wrap="wrap">
|
||||
{genres.slice(0, MAX_GENRES).map((genre) => (
|
||||
<Badge
|
||||
<div
|
||||
className={clsx(styles.genresContainer, {
|
||||
[styles.compact]: props.size === 'compact',
|
||||
[styles.large]: props.size === 'large',
|
||||
})}
|
||||
>
|
||||
{genres.map((genre, index) => (
|
||||
<Text
|
||||
component={Link}
|
||||
isLink
|
||||
isMuted
|
||||
isNoSelect
|
||||
key={genre.id}
|
||||
style={{
|
||||
backgroundColor: genre.color,
|
||||
color: genre.isLight ? 'black' : 'white',
|
||||
}}
|
||||
to={genre.path}
|
||||
>
|
||||
{genre.name}
|
||||
</Badge>
|
||||
{index < genres.length - 1 && ', '}
|
||||
</Text>
|
||||
))}
|
||||
</Group>
|
||||
</div>
|
||||
</TableColumnContainer>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
.text-container {
|
||||
display: -webkit-box;
|
||||
overflow: hidden;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
.text-container.compact {
|
||||
-webkit-line-clamp: 1;
|
||||
}
|
||||
|
||||
.text-container.large {
|
||||
-webkit-line-clamp: 3;
|
||||
}
|
||||
@@ -1,3 +1,7 @@
|
||||
import clsx from 'clsx';
|
||||
|
||||
import styles from './text-column.module.css';
|
||||
|
||||
import {
|
||||
ItemTableListInnerColumn,
|
||||
TableColumnTextContainer,
|
||||
@@ -10,10 +14,17 @@ export const TextColumn = (props: ItemTableListInnerColumn) => {
|
||||
];
|
||||
|
||||
if (typeof row === 'string' && row) {
|
||||
const maxLength = 100;
|
||||
const displayText = row.length > maxLength ? `${row.slice(0, maxLength)}...` : row;
|
||||
|
||||
return <TableColumnTextContainer {...props}>{displayText}</TableColumnTextContainer>;
|
||||
return (
|
||||
<TableColumnTextContainer
|
||||
className={clsx(styles.textContainer, {
|
||||
[styles.compact]: props.size === 'compact',
|
||||
[styles.large]: props.size === 'large',
|
||||
})}
|
||||
{...props}
|
||||
>
|
||||
{row}
|
||||
</TableColumnTextContainer>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
import { UseSuspenseQueryOptions } from '@tanstack/react-query';
|
||||
import { useRef } from 'react';
|
||||
|
||||
import { api } from '/@/renderer/api';
|
||||
import { useItemListInfiniteLoader } from '/@/renderer/components/item-list/helpers/item-list-infinite-loader';
|
||||
import { ItemTableList } 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 { ItemListHandle, ItemTableListColumnConfig } from '/@/renderer/components/item-list/types';
|
||||
import { albumQueries } from '/@/renderer/features/albums/api/album-api';
|
||||
import { useCurrentServer } from '/@/renderer/store';
|
||||
import { AlbumListSort, LibraryItem, SortOrder } from '/@/shared/types/domain-types';
|
||||
import { TableColumn } from '/@/shared/types/types';
|
||||
|
||||
const columns: ItemTableListColumnConfig[] = [
|
||||
{ align: 'center', id: TableColumn.ROW_INDEX, pinned: 'left', width: 50 },
|
||||
{ align: 'center', id: TableColumn.IMAGE, pinned: 'left', width: 60 },
|
||||
{ align: 'start', id: TableColumn.TITLE, pinned: 'left', width: 300 },
|
||||
{ align: 'start', id: TableColumn.GENRE, pinned: null, width: 200 },
|
||||
{ align: 'center', id: TableColumn.YEAR, pinned: null, width: 100 },
|
||||
{ align: 'start', autoWidth: true, id: TableColumn.ALBUM_ARTIST, pinned: null, width: 150 },
|
||||
{ align: 'center', id: TableColumn.DURATION, pinned: null, width: 100 },
|
||||
{ align: 'start', id: TableColumn.LAST_PLAYED, pinned: null, width: 250 },
|
||||
{ align: 'center', id: TableColumn.USER_RATING, pinned: null, width: 120 },
|
||||
{ align: 'center', id: TableColumn.USER_FAVORITE, pinned: null, width: 50 },
|
||||
{ align: 'center', id: TableColumn.ACTIONS, pinned: 'right', width: 50 },
|
||||
{ align: 'center', id: TableColumn.SIZE, pinned: null, width: 100 },
|
||||
{ align: 'center', id: TableColumn.DATE_ADDED, pinned: null, width: 100 },
|
||||
{ align: 'center', id: TableColumn.RELEASE_DATE, pinned: null, width: 100 },
|
||||
{ align: 'center', id: TableColumn.PLAY_COUNT, pinned: null, width: 100 },
|
||||
{ align: 'center', id: TableColumn.SONG_COUNT, pinned: null, width: 100 },
|
||||
];
|
||||
|
||||
// export const Demo = () => {
|
||||
// const server = useCurrentServer();
|
||||
// const recentlyAdded = useSuspenseQuery(
|
||||
// albumQueries.list({
|
||||
// options: {
|
||||
// gcTime: 1000 * 60 * 5,
|
||||
// },
|
||||
// query: {
|
||||
// limit: 100,
|
||||
// sortBy: AlbumListSort.RECENTLY_ADDED,
|
||||
// sortOrder: SortOrder.DESC,
|
||||
// startIndex: 0,
|
||||
// },
|
||||
// serverId: server.id,
|
||||
// }),
|
||||
// );
|
||||
|
||||
// const data = [
|
||||
// Object.fromEntries(columns.map((x) => [x.id, x.label])),
|
||||
// ...recentlyAdded.data.items.map((item, index) => ({
|
||||
// ...item,
|
||||
// rowIndex: index,
|
||||
// })),
|
||||
// ];
|
||||
|
||||
// return (
|
||||
// <ItemTableList
|
||||
// CellComponent={ItemTableListColumn}
|
||||
// columnCount={columns.length}
|
||||
// columns={columns}
|
||||
// columnWidth={(index) => {
|
||||
// return columns[index].width;
|
||||
// }}
|
||||
// data={data}
|
||||
// itemType={LibraryItem.ALBUM}
|
||||
// onCellsRendered={() => {}}
|
||||
// rowHeight={60}
|
||||
// stickyColumnCount={3}
|
||||
// stickyRowCount={1}
|
||||
// totalItemCount={data.length}
|
||||
// />
|
||||
// );
|
||||
// };
|
||||
|
||||
export const Demo = () => {
|
||||
const server = useCurrentServer();
|
||||
|
||||
const query = {
|
||||
sortBy: AlbumListSort.NAME,
|
||||
sortOrder: SortOrder.ASC,
|
||||
};
|
||||
|
||||
const serverId = server.id;
|
||||
|
||||
const listCountQuery = albumQueries.listCount({
|
||||
query: { ...query },
|
||||
serverId: serverId,
|
||||
}) as UseSuspenseQueryOptions<number, Error, number, readonly unknown[]>;
|
||||
|
||||
const listQueryFn = api.controller.getAlbumList;
|
||||
|
||||
const { data, onRangeChanged } = useItemListInfiniteLoader({
|
||||
itemsPerPage: 100,
|
||||
listCountQuery,
|
||||
listQueryFn,
|
||||
query,
|
||||
serverId,
|
||||
});
|
||||
|
||||
// const itemsPerPage = 100;
|
||||
|
||||
// const { currentPage, onChange } = useItemListPagination({ initialPage: 0 });
|
||||
|
||||
// const { data, pageCount, totalItemCount } = useItemListPaginatedLoader({
|
||||
// currentPage,
|
||||
// itemsPerPage,
|
||||
// listCountQuery,
|
||||
// listQueryFn,
|
||||
// query,
|
||||
// serverId,
|
||||
// });
|
||||
|
||||
const ref = useRef<ItemListHandle>(null);
|
||||
|
||||
return (
|
||||
<ItemTableList
|
||||
CellComponent={ItemTableListColumn}
|
||||
columns={columns}
|
||||
data={data || []}
|
||||
enableAlternateRowColors
|
||||
enableExpansion
|
||||
enableHeader
|
||||
enableHorizontalBorders
|
||||
enableRowHoverHighlight
|
||||
enableSelection
|
||||
itemType={LibraryItem.ALBUM}
|
||||
onRangeChanged={onRangeChanged}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
|
||||
// return (
|
||||
// <ItemListWithPagination
|
||||
// currentPage={currentPage}
|
||||
// itemsPerPage={itemsPerPage}
|
||||
// onChange={onChange}
|
||||
// pageCount={pageCount}
|
||||
// totalItemCount={totalItemCount}
|
||||
// >
|
||||
// <ItemTableList
|
||||
// CellComponent={ItemTableListColumn}
|
||||
// columnCount={columns.length}
|
||||
// columns={columns}
|
||||
// columnWidth={(index) => {
|
||||
// return columns[index].width;
|
||||
// }}
|
||||
// data={data || []}
|
||||
// enableHeader
|
||||
// itemType={LibraryItem.ALBUM}
|
||||
// onCellsRendered={() => {}}
|
||||
// pinnedLeftColumnCount={2}
|
||||
// // onRangeChanged={onRangeChanged}
|
||||
// rowHeight={60}
|
||||
// totalItemCount={data?.length || 0}
|
||||
// />
|
||||
// </ItemListWithPagination>
|
||||
// );
|
||||
};
|
||||
@@ -1,15 +0,0 @@
|
||||
import clsx from 'clsx';
|
||||
|
||||
import { ItemTableListColumn } from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
||||
import { TableColumn } from '/@/shared/types/types';
|
||||
|
||||
export const TableColumnHeader = (
|
||||
props: ItemTableListColumn & {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
containerClassName?: string;
|
||||
type: TableColumn;
|
||||
},
|
||||
) => {
|
||||
return <div className={clsx(props.className)}>{props.children}</div>;
|
||||
};
|
||||
+48
-7
@@ -4,9 +4,28 @@
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.container.padding-xs {
|
||||
padding: var(--theme-spacing-xs);
|
||||
}
|
||||
|
||||
.container.padding-sm {
|
||||
padding: var(--theme-spacing-xs) var(--theme-spacing-sm);
|
||||
}
|
||||
|
||||
.container.padding-md {
|
||||
padding: var(--theme-spacing-xs) var(--theme-spacing-md);
|
||||
}
|
||||
|
||||
.container.padding-lg {
|
||||
padding: var(--theme-spacing-xs) var(--theme-spacing-lg);
|
||||
}
|
||||
|
||||
.container.padding-xl {
|
||||
padding: var(--theme-spacing-xs) var(--theme-spacing-xl);
|
||||
}
|
||||
|
||||
.container.center {
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
@@ -29,7 +48,6 @@
|
||||
-webkit-line-clamp: 2;
|
||||
line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.content.compact {
|
||||
@@ -37,8 +55,9 @@
|
||||
line-clamp: 1;
|
||||
}
|
||||
|
||||
.container.compact {
|
||||
padding: var(--theme-spacing-xs);
|
||||
.content.large {
|
||||
-webkit-line-clamp: 3;
|
||||
line-clamp: 3;
|
||||
}
|
||||
|
||||
.container.with-horizontal-border {
|
||||
@@ -86,20 +105,42 @@
|
||||
background: none;
|
||||
}
|
||||
|
||||
.header-container.padding-xs {
|
||||
padding: 0 var(--theme-spacing-xs);
|
||||
}
|
||||
|
||||
.header-container.padding-sm {
|
||||
padding: 0 var(--theme-spacing-sm);
|
||||
}
|
||||
|
||||
.header-container.padding-md {
|
||||
padding: 0 var(--theme-spacing-md);
|
||||
}
|
||||
|
||||
.header-container.padding-lg {
|
||||
padding: 0 var(--theme-spacing-lg);
|
||||
}
|
||||
|
||||
.header-container.padding-xl {
|
||||
padding: 0 var(--theme-spacing-xl);
|
||||
}
|
||||
|
||||
.header-content {
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.header-content.center {
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.header-content.left {
|
||||
justify-content: flex-start;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.header-content.right {
|
||||
justify-content: flex-end;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.container :global(.hover-only),
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
import { DefaultColumn } from '/@/renderer/components/item-list/item-table-list/columns/default-column';
|
||||
import { DurationColumn } from '/@/renderer/components/item-list/item-table-list/columns/duration-column';
|
||||
import { FavoriteColumn } from '/@/renderer/components/item-list/item-table-list/columns/favorite-column';
|
||||
import { GenreBadgeColumn } from '/@/renderer/components/item-list/item-table-list/columns/genre-badge-column';
|
||||
import { GenreColumn } from '/@/renderer/components/item-list/item-table-list/columns/genre-column';
|
||||
import { ImageColumn } from '/@/renderer/components/item-list/item-table-list/columns/image-column';
|
||||
import { NumericColumn } from '/@/renderer/components/item-list/item-table-list/columns/numeric-column';
|
||||
@@ -109,6 +110,9 @@ export const ItemTableListColumn = (props: ItemTableListColumn) => {
|
||||
case TableColumn.GENRE:
|
||||
return <GenreColumn {...props} controls={controls} type={type} />;
|
||||
|
||||
case TableColumn.GENRE_BADGE:
|
||||
return <GenreBadgeColumn {...props} controls={controls} type={type} />;
|
||||
|
||||
case TableColumn.IMAGE:
|
||||
return <ImageColumn {...props} controls={controls} type={type} />;
|
||||
|
||||
@@ -190,7 +194,13 @@ export const TableColumnTextContainer = (
|
||||
[styles.center]: props.columns[props.columnIndex].align === 'center',
|
||||
[styles.compact]: props.size === 'compact',
|
||||
[styles.dataRow]: isDataRow,
|
||||
[styles.large]: props.size === 'large',
|
||||
[styles.left]: props.columns[props.columnIndex].align === 'start',
|
||||
[styles.paddingLg]: props.cellPadding === 'lg',
|
||||
[styles.paddingMd]: props.cellPadding === 'md',
|
||||
[styles.paddingSm]: props.cellPadding === 'sm',
|
||||
[styles.paddingXl]: props.cellPadding === 'xl',
|
||||
[styles.paddingXs]: props.cellPadding === 'xs',
|
||||
[styles.right]: props.columns[props.columnIndex].align === 'end',
|
||||
[styles.rowHoverHighlightEnabled]: isDataRow && props.enableRowHoverHighlight,
|
||||
[styles.withHorizontalBorder]:
|
||||
@@ -204,6 +214,7 @@ export const TableColumnTextContainer = (
|
||||
<Text
|
||||
className={clsx(styles.content, props.className, {
|
||||
[styles.compact]: props.size === 'compact',
|
||||
[styles.large]: props.size === 'large',
|
||||
})}
|
||||
isMuted={!NonMutedColumns.includes(props.type)}
|
||||
isNoSelect
|
||||
@@ -264,7 +275,13 @@ export const TableColumnContainer = (
|
||||
[styles.center]: props.columns[props.columnIndex].align === 'center',
|
||||
[styles.compact]: props.size === 'compact',
|
||||
[styles.dataRow]: isDataRow,
|
||||
[styles.large]: props.size === 'large',
|
||||
[styles.left]: props.columns[props.columnIndex].align === 'start',
|
||||
[styles.paddingLg]: props.cellPadding === 'lg',
|
||||
[styles.paddingMd]: props.cellPadding === 'md',
|
||||
[styles.paddingSm]: props.cellPadding === 'sm',
|
||||
[styles.paddingXl]: props.cellPadding === 'xl',
|
||||
[styles.paddingXs]: props.cellPadding === 'xs',
|
||||
[styles.right]: props.columns[props.columnIndex].align === 'end',
|
||||
[styles.rowHoverHighlightEnabled]: isDataRow && props.enableRowHoverHighlight,
|
||||
[styles.withHorizontalBorder]:
|
||||
@@ -290,7 +307,13 @@ export const TableColumnHeaderContainer = (
|
||||
) => {
|
||||
return (
|
||||
<div
|
||||
className={clsx(styles.container, styles.headerContainer, props.containerClassName, {})}
|
||||
className={clsx(styles.container, styles.headerContainer, props.containerClassName, {
|
||||
[styles.paddingLg]: props.cellPadding === 'lg',
|
||||
[styles.paddingMd]: props.cellPadding === 'md',
|
||||
[styles.paddingSm]: props.cellPadding === 'sm',
|
||||
[styles.paddingXl]: props.cellPadding === 'xl',
|
||||
[styles.paddingXs]: props.cellPadding === 'xs',
|
||||
})}
|
||||
style={props.style}
|
||||
>
|
||||
<Text
|
||||
@@ -333,6 +356,9 @@ const columnLabelMap: Record<TableColumn, ReactNode | string> = {
|
||||
}) as string,
|
||||
[TableColumn.DURATION]: <Icon icon="duration" size="md" />,
|
||||
[TableColumn.GENRE]: i18n.t('table.column.genre', { postProcess: 'upperCase' }) as string,
|
||||
[TableColumn.GENRE_BADGE]: i18n.t('table.column.genre', {
|
||||
postProcess: 'upperCase',
|
||||
}) as string,
|
||||
[TableColumn.ID]: 'ID',
|
||||
[TableColumn.IMAGE]: '',
|
||||
[TableColumn.LAST_PLAYED]: i18n.t('table.column.lastPlayed', {
|
||||
|
||||
@@ -29,23 +29,25 @@ import { ItemListHandle, ItemTableListColumnConfig } from '/@/renderer/component
|
||||
import { LibraryItem } from '/@/shared/types/domain-types';
|
||||
|
||||
export interface TableItemProps {
|
||||
cellPadding?: ItemTableListProps['cellPadding'];
|
||||
columns: ItemTableListColumnConfig[];
|
||||
data: unknown[];
|
||||
enableAlternateRowColors?: boolean;
|
||||
enableExpansion?: boolean;
|
||||
enableHeader?: boolean;
|
||||
enableHorizontalBorders?: boolean;
|
||||
enableRowHoverHighlight?: boolean;
|
||||
enableSelection?: boolean;
|
||||
enableVerticalBorders?: boolean;
|
||||
data: ItemTableListProps['data'];
|
||||
enableAlternateRowColors?: ItemTableListProps['enableAlternateRowColors'];
|
||||
enableExpansion?: ItemTableListProps['enableExpansion'];
|
||||
enableHeader?: ItemTableListProps['enableHeader'];
|
||||
enableHorizontalBorders?: ItemTableListProps['enableHorizontalBorders'];
|
||||
enableRowHoverHighlight?: ItemTableListProps['enableRowHoverHighlight'];
|
||||
enableSelection?: ItemTableListProps['enableSelection'];
|
||||
enableVerticalBorders?: ItemTableListProps['enableVerticalBorders'];
|
||||
getRowHeight: (index: number, cellProps: TableItemProps) => number;
|
||||
internalState: ItemListStateActions;
|
||||
itemType: LibraryItem;
|
||||
size?: 'compact' | 'default';
|
||||
itemType: ItemTableListProps['itemType'];
|
||||
size?: ItemTableListProps['size'];
|
||||
}
|
||||
|
||||
interface ItemTableListProps {
|
||||
CellComponent: JSXElementConstructor<CellComponentProps<TableItemProps>>;
|
||||
cellPadding?: 'lg' | 'md' | 'sm' | 'xl' | 'xs';
|
||||
columns: ItemTableListColumnConfig[];
|
||||
data: unknown[];
|
||||
enableAlternateRowColors?: boolean;
|
||||
@@ -72,11 +74,12 @@ interface ItemTableListProps {
|
||||
onStartReached?: (index: number, internalState: ItemListStateActions) => void;
|
||||
ref?: Ref<ItemListHandle>;
|
||||
rowHeight?: ((index: number, cellProps: TableItemProps) => number) | number;
|
||||
size?: 'compact' | 'default';
|
||||
size?: 'compact' | 'default' | 'large';
|
||||
}
|
||||
|
||||
export const ItemTableList = ({
|
||||
CellComponent,
|
||||
cellPadding = 'sm',
|
||||
columns,
|
||||
data,
|
||||
enableAlternateRowColors = false,
|
||||
@@ -99,8 +102,8 @@ export const ItemTableList = ({
|
||||
size = 'default',
|
||||
}: ItemTableListProps) => {
|
||||
const totalItemCount = data.length;
|
||||
const sortedColumns = useMemo(() => parseTableColumns(columns), [columns]);
|
||||
const columnCount = sortedColumns.length;
|
||||
const parsedColumns = useMemo(() => parseTableColumns(columns), [columns]);
|
||||
const columnCount = parsedColumns.length;
|
||||
|
||||
const [centerContainerWidth, setCenterContainerWidth] = useState(0);
|
||||
|
||||
@@ -127,14 +130,14 @@ export const ItemTableList = ({
|
||||
|
||||
// Compute distributed widths: unpinned columns with autoWidth will share any remaining space
|
||||
const calculatedColumnWidths = useMemo(() => {
|
||||
const baseWidths = sortedColumns.map((c) => c.width);
|
||||
const baseWidths = parsedColumns.map((c) => c.width);
|
||||
const distributed = baseWidths.slice();
|
||||
|
||||
// Identify unpinned columns and auto-width candidates
|
||||
const unpinnedIndices: number[] = [];
|
||||
const autoUnpinnedIndices: number[] = [];
|
||||
|
||||
sortedColumns.forEach((col, idx) => {
|
||||
parsedColumns.forEach((col, idx) => {
|
||||
if (col.pinned === null) {
|
||||
unpinnedIndices.push(idx);
|
||||
if (col.autoSize) {
|
||||
@@ -161,13 +164,13 @@ export const ItemTableList = ({
|
||||
});
|
||||
|
||||
return distributed;
|
||||
}, [sortedColumns, centerContainerWidth]);
|
||||
}, [parsedColumns, centerContainerWidth]);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const columnWidth = (index: number, _cellProps: TableItemProps) =>
|
||||
calculatedColumnWidths[index];
|
||||
const pinnedLeftColumnCount = sortedColumns.filter((col) => col.pinned === 'left').length;
|
||||
const pinnedRightColumnCount = sortedColumns.filter((col) => col.pinned === 'right').length;
|
||||
const pinnedLeftColumnCount = parsedColumns.filter((col) => col.pinned === 'left').length;
|
||||
const pinnedRightColumnCount = parsedColumns.filter((col) => col.pinned === 'right').length;
|
||||
|
||||
const pinnedRowCount = enableHeader ? 1 : 0;
|
||||
const totalRowCount = totalItemCount - pinnedRowCount;
|
||||
@@ -527,7 +530,7 @@ export const ItemTableList = ({
|
||||
|
||||
const getRowHeight = useCallback(
|
||||
(index: number, cellProps: TableItemProps) => {
|
||||
const height = size === 'compact' ? 40 : 64;
|
||||
const height = size === 'compact' ? 40 : size === 'large' ? 88 : 64;
|
||||
|
||||
const baseHeight =
|
||||
typeof rowHeight === 'number' ? rowHeight : rowHeight?.(index, cellProps) || height;
|
||||
@@ -657,7 +660,8 @@ export const ItemTableList = ({
|
||||
);
|
||||
|
||||
const itemProps: TableItemProps = {
|
||||
columns: sortedColumns,
|
||||
cellPadding,
|
||||
columns: parsedColumns,
|
||||
data,
|
||||
enableAlternateRowColors,
|
||||
enableExpansion,
|
||||
|
||||
Reference in New Issue
Block a user