diff --git a/src/renderer/components/virtual-table/headers/generic-table-header.tsx b/src/renderer/components/virtual-table/headers/generic-table-header.tsx
index d718a871..ddbd67f9 100644
--- a/src/renderer/components/virtual-table/headers/generic-table-header.tsx
+++ b/src/renderer/components/virtual-table/headers/generic-table-header.tsx
@@ -3,6 +3,7 @@ import type { IHeaderParams } from '@ag-grid-community/core';
import { AiOutlineNumber } from 'react-icons/ai';
import { FiClock } from 'react-icons/fi';
import styled from 'styled-components';
+import { _Text } from '/@/renderer/components/text';
type Presets = 'duration' | 'rowIndex';
@@ -12,7 +13,7 @@ type Options = {
preset?: Presets;
};
-const HeaderWrapper = styled.div<{ position: 'left' | 'center' | 'right' }>`
+const HeaderWrapper = styled.div<{ position: Options['position'] }>`
display: flex;
justify-content: ${(props) =>
props.position === 'right'
@@ -25,6 +26,19 @@ const HeaderWrapper = styled.div<{ position: 'left' | 'center' | 'right' }>`
text-transform: uppercase;
`;
+const TextHeaderWrapper = styled(_Text)<{ position: Options['position'] }>`
+ width: 100%;
+ color: var(--ag-header-foreground-color);
+ font-weight: 500;
+ text-align: ${(props) =>
+ props.position === 'right'
+ ? 'flex-end'
+ : props.position === 'center'
+ ? 'center'
+ : 'flex-start'};
+ text-transform: uppercase;
+`;
+
const headerPresets = { duration: , rowIndex: };
export const GenericTableHeader = (
@@ -32,10 +46,18 @@ export const GenericTableHeader = (
{ preset, children, position }: Options,
) => {
if (preset) {
- return {headerPresets[preset]};
+ return {headerPresets[preset]};
}
- return {children || displayName};
+ return (
+
+ {children || displayName}
+
+ );
};
GenericTableHeader.defaultProps = {
diff --git a/src/renderer/components/virtual-table/index.tsx b/src/renderer/components/virtual-table/index.tsx
index 03242535..d4a48198 100644
--- a/src/renderer/components/virtual-table/index.tsx
+++ b/src/renderer/components/virtual-table/index.tsx
@@ -11,6 +11,8 @@ import type { AgGridReactProps } from '@ag-grid-community/react';
import { AgGridReact } from '@ag-grid-community/react';
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
import { useMergedRef } from '@mantine/hooks';
+import dayjs from 'dayjs';
+import relativeTime from 'dayjs/plugin/relativeTime';
import formatDuration from 'format-duration';
import { generatePath } from 'react-router';
import styled from 'styled-components';
@@ -34,6 +36,8 @@ const TableWrapper = styled.div`
height: 100%;
`;
+dayjs.extend(relativeTime);
+
const tableColumns: { [key: string]: ColDef } = {
album: {
cellRenderer: (params: ICellRendererParams) =>
@@ -49,6 +53,7 @@ const tableColumns: { [key: string]: ColDef } = {
value: params.data?.album,
}
: undefined,
+ width: 200,
},
albumArtist: {
cellRenderer: AlbumArtistCell,
@@ -56,6 +61,7 @@ const tableColumns: { [key: string]: ColDef } = {
headerName: 'Album Artist',
valueGetter: (params: ValueGetterParams) =>
params.data ? params.data.albumArtists : undefined,
+ width: 150,
},
albumCount: {
cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'center' }),
@@ -63,36 +69,42 @@ const tableColumns: { [key: string]: ColDef } = {
field: 'albumCount',
headerComponent: (params: IHeaderParams) => GenericTableHeader(params, { position: 'center' }),
headerName: 'Albums',
+ suppressSizeToFit: true,
valueGetter: (params: ValueGetterParams) => (params.data ? params.data.albumCount : undefined),
+ width: 80,
},
artist: {
cellRenderer: ArtistCell,
colId: TableColumn.ARTIST,
headerName: 'Artist',
valueGetter: (params: ValueGetterParams) => (params.data ? params.data.artists : undefined),
+ width: 150,
},
biography: {
cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'left' }),
colId: TableColumn.BIOGRAPHY,
field: 'biography',
- headerComponent: (params: IHeaderParams) => GenericTableHeader(params, { position: 'left' }),
headerName: 'Biography',
- valueGetter: (params: ValueGetterParams) => (params.data ? params.data.biography : undefined),
+ valueGetter: (params: ValueGetterParams) => (params.data ? params.data.biography : ''),
+ width: 200,
},
bitRate: {
cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'left' }),
colId: TableColumn.BIT_RATE,
field: 'bitRate',
- headerComponent: (params: IHeaderParams) => GenericTableHeader(params, { position: 'left' }),
+ suppressSizeToFit: true,
valueFormatter: (params: ValueFormatterParams) => `${params.value} kbps`,
valueGetter: (params: ValueGetterParams) => (params.data ? params.data.bitRate : undefined),
+ width: 90,
},
bpm: {
cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'center' }),
colId: TableColumn.BPM,
headerComponent: (params: IHeaderParams) => GenericTableHeader(params, { position: 'center' }),
headerName: 'BPM',
+ suppressSizeToFit: true,
valueGetter: (params: ValueGetterParams) => (params.data ? params.data.bpm : undefined),
+ width: 60,
},
channels: {
cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'center' }),
@@ -100,60 +112,70 @@ const tableColumns: { [key: string]: ColDef } = {
field: 'channels',
headerComponent: (params: IHeaderParams) => GenericTableHeader(params, { position: 'center' }),
valueGetter: (params: ValueGetterParams) => (params.data ? params.data.channels : undefined),
+ width: 100,
},
comment: {
cellRenderer: GenericCell,
colId: TableColumn.COMMENT,
headerName: 'Note',
valueGetter: (params: ValueGetterParams) => (params.data ? params.data.comment : undefined),
+ width: 150,
},
dateAdded: {
cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'left' }),
colId: TableColumn.DATE_ADDED,
field: 'createdAt',
- headerComponent: (params: IHeaderParams) => GenericTableHeader(params, { position: 'left' }),
headerName: 'Date Added',
- valueFormatter: (params: ValueFormatterParams) => params.value?.split('T')[0],
+ suppressSizeToFit: true,
+ valueFormatter: (params: ValueFormatterParams) =>
+ params.value ? dayjs(params.value).format('MMM D, YYYY') : '',
valueGetter: (params: ValueGetterParams) => (params.data ? params.data.createdAt : undefined),
+ width: 110,
},
discNumber: {
- cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'center' }),
+ cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'right' }),
colId: TableColumn.DISC_NUMBER,
field: 'discNumber',
- headerComponent: (params: IHeaderParams) => GenericTableHeader(params, { position: 'center' }),
+ headerComponent: (params: IHeaderParams) => GenericTableHeader(params, { position: 'right' }),
headerName: 'Disc',
- initialWidth: 75,
suppressSizeToFit: true,
valueGetter: (params: ValueGetterParams) => (params.data ? params.data.discNumber : undefined),
+ width: 60,
},
duration: {
- cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'center' }),
+ cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'right' }),
colId: TableColumn.DURATION,
field: 'duration',
headerComponent: (params: IHeaderParams) =>
- GenericTableHeader(params, { position: 'center', preset: 'duration' }),
- initialWidth: 100,
+ GenericTableHeader(params, { position: 'right', preset: 'duration' }),
+ suppressSizeToFit: true,
valueFormatter: (params: ValueFormatterParams) => formatDuration(params.value * 1000),
valueGetter: (params: ValueGetterParams) => (params.data ? params.data.duration : undefined),
+ width: 60,
},
genre: {
cellRenderer: GenreCell,
colId: TableColumn.GENRE,
headerName: 'Genre',
valueGetter: (params: ValueGetterParams) => (params.data ? params.data.genres : undefined),
+ width: 100,
},
lastPlayedAt: {
cellRenderer: GenericCell,
colId: TableColumn.LAST_PLAYED,
headerName: 'Last Played',
+ valueFormatter: (params: ValueFormatterParams) =>
+ params.value ? dayjs(params.value).fromNow() : '',
valueGetter: (params: ValueGetterParams) =>
params.data ? params.data.lastPlayedAt : undefined,
+ width: 130,
},
path: {
cellRenderer: GenericCell,
colId: TableColumn.PATH,
headerName: 'Path',
valueGetter: (params: ValueGetterParams) => (params.data ? params.data.path : undefined),
+ width: 200,
},
playCount: {
cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'center' }),
@@ -161,16 +183,20 @@ const tableColumns: { [key: string]: ColDef } = {
field: 'playCount',
headerComponent: (params: IHeaderParams) => GenericTableHeader(params, { position: 'center' }),
headerName: 'Plays',
+ suppressSizeToFit: true,
valueGetter: (params: ValueGetterParams) => (params.data ? params.data.playCount : undefined),
+ width: 90,
},
releaseDate: {
- cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'center' }),
+ cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'left' }),
colId: TableColumn.RELEASE_DATE,
field: 'releaseDate',
- headerComponent: (params: IHeaderParams) => GenericTableHeader(params, { position: 'center' }),
headerName: 'Release Date',
- valueFormatter: (params: ValueFormatterParams) => params.value?.split('T')[0],
+ suppressSizeToFit: true,
+ valueFormatter: (params: ValueFormatterParams) =>
+ params.value ? dayjs(params.value).format('MMM D, YYYY') : '',
valueGetter: (params: ValueGetterParams) => (params.data ? params.data.releaseDate : undefined),
+ width: 130,
},
releaseYear: {
cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'center' }),
@@ -178,26 +204,29 @@ const tableColumns: { [key: string]: ColDef } = {
field: 'releaseYear',
headerComponent: (params: IHeaderParams) => GenericTableHeader(params, { position: 'center' }),
headerName: 'Year',
+ suppressSizeToFit: true,
valueGetter: (params: ValueGetterParams) => (params.data ? params.data.releaseYear : undefined),
+ width: 60,
},
rowIndex: {
- cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'left' }),
+ cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'right' }),
colId: TableColumn.ROW_INDEX,
headerComponent: (params: IHeaderParams) =>
- GenericTableHeader(params, { position: 'left', preset: 'rowIndex' }),
- initialWidth: 50,
+ GenericTableHeader(params, { position: 'right', preset: 'rowIndex' }),
suppressSizeToFit: true,
valueGetter: (params) => {
return (params.node?.rowIndex || 0) + 1;
},
+ width: 65,
},
songCount: {
cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'center' }),
colId: TableColumn.SONG_COUNT,
field: 'songCount',
- headerComponent: (params: IHeaderParams) => GenericTableHeader(params, { position: 'center' }),
headerName: 'Songs',
+ suppressSizeToFit: true,
valueGetter: (params: ValueGetterParams) => (params.data ? params.data.songCount : undefined),
+ width: 80,
},
title: {
cellRenderer: (params: ICellRendererParams) =>
@@ -206,6 +235,7 @@ const tableColumns: { [key: string]: ColDef } = {
field: 'name',
headerName: 'Title',
valueGetter: (params: ValueGetterParams) => (params.data ? params.data.name : undefined),
+ width: 250,
},
titleCombined: {
cellRenderer: CombinedTitleCell,
@@ -224,16 +254,17 @@ const tableColumns: { [key: string]: ColDef } = {
type: params.data?.type,
}
: undefined,
+ width: 250,
},
trackNumber: {
- cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'center' }),
+ cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'right' }),
colId: TableColumn.TRACK_NUMBER,
field: 'trackNumber',
- headerComponent: (params: IHeaderParams) => GenericTableHeader(params, { position: 'center' }),
+ headerComponent: (params: IHeaderParams) => GenericTableHeader(params, { position: 'right' }),
headerName: 'Track',
- initialWidth: 75,
suppressSizeToFit: true,
valueGetter: (params: ValueGetterParams) => (params.data ? params.data.trackNumber : undefined),
+ width: 80,
},
};
diff --git a/src/renderer/features/albums/components/album-detail-content.tsx b/src/renderer/features/albums/components/album-detail-content.tsx
index bc75edba..01912c04 100644
--- a/src/renderer/features/albums/components/album-detail-content.tsx
+++ b/src/renderer/features/albums/components/album-detail-content.tsx
@@ -42,13 +42,6 @@ const ContentContainer = styled.div`
.ag-header-container {
z-index: 1000;
}
-
- .ag-header-cell-resize {
- top: 25%;
- width: 7px;
- height: 50%;
- background-color: rgb(70, 70, 70, 20%);
- }
`;
interface AlbumDetailContentProps {
diff --git a/src/renderer/features/playlists/components/playlist-detail-content.tsx b/src/renderer/features/playlists/components/playlist-detail-content.tsx
index e72983a3..ac52d800 100644
--- a/src/renderer/features/playlists/components/playlist-detail-content.tsx
+++ b/src/renderer/features/playlists/components/playlist-detail-content.tsx
@@ -28,13 +28,6 @@ const ContentContainer = styled.div`
.ag-header-container {
z-index: 1000;
}
-
- .ag-header-cell-resize {
- top: 25%;
- width: 7px;
- height: 50%;
- background-color: rgb(70, 70, 70, 20%);
- }
`;
interface PlaylistDetailContentProps {
diff --git a/src/renderer/themes/default.scss b/src/renderer/themes/default.scss
index a273b68d..7f60590b 100644
--- a/src/renderer/themes/default.scss
+++ b/src/renderer/themes/default.scss
@@ -116,10 +116,27 @@
--ag-selected-row-background-color: rgba(100, 100, 100, 0.4);
}
+ .ag-header {
+ border-bottom: 1px solid rgb(50, 50, 50, 0.5);
+ margin-bottom: 1rem;
+ }
+
+ .ag-header-cell-comp-wrapper {
+ margin: 0.5rem 0.5rem;
+ }
+
+ .ag-header-cell,
+ .ag-header-group-cell {
+ padding-left: 0.5rem;
+ padding-right: 0.5rem;
+ }
+
.ag-header-cell-resize {
- background-color: rgb(70, 70, 70, 0.4);
- height: 50%;
- top: 25%;
+ background-color: transparent;
+ }
+
+ .ag-header:hover .ag-header-cell-resize {
+ border-left: 2px rgb(70, 70, 70, 1) solid;
width: 7px;
}