Update table component

This commit is contained in:
jeffvli 2022-12-27 13:13:12 -08:00
parent d1c038ea6f
commit 2b0d4c44a6
9 changed files with 189 additions and 88 deletions

View file

@ -31,7 +31,6 @@ export const AlbumArtistCell = ({ value, data }: ICellRendererParams) => {
<React.Fragment key={`row-${item.id}-${data.uniqueId}`}>
{index > 0 && (
<Text
$link
$secondary
size="sm"
style={{ display: 'inline-block' }}

View file

@ -31,7 +31,6 @@ export const ArtistCell = ({ value, data }: ICellRendererParams) => {
<React.Fragment key={`row-${item.id}-${data.uniqueId}`}>
{index > 0 && (
<Text
$link
$secondary
size="sm"
style={{ display: 'inline-block' }}

View file

@ -11,6 +11,7 @@ import { Text } from '/@/renderer/components/text';
import { AppRoute } from '/@/renderer/router/routes';
import { ServerType } from '/@/renderer/types';
import { Skeleton } from '/@/renderer/components/skeleton';
import { CELL_VARIANTS } from '/@/renderer/components/virtual-table/cells/generic-cell';
const CellContainer = styled(motion.div)<{ height: number }>`
display: grid;

View file

@ -1,12 +1,20 @@
import type { ICellRendererParams } from '@ag-grid-community/core';
import { motion, Variants } from 'framer-motion';
import { Link } from 'react-router-dom';
import styled from 'styled-components';
import { Skeleton } from '/@/renderer/components/skeleton';
import { Text } from '/@/renderer/components/text';
export const CellContainer = styled.div<{
position?: 'left' | 'center' | 'right';
}>`
export const CELL_VARIANTS: Variants = {
animate: {
opacity: 1,
},
initial: {
opacity: 0,
},
};
export const CellContainer = styled(motion.div)<{ position?: 'left' | 'center' | 'right' }>`
display: flex;
align-items: center;
justify-content: ${(props) =>

View file

@ -17,7 +17,6 @@ export const GenreCell = ({ value, data }: ICellRendererParams) => {
<React.Fragment key={`row-${item.id}-${data.uniqueId}`}>
{index > 0 && (
<Text
$link
$secondary
size="sm"
style={{ display: 'inline-block' }}

View file

@ -71,6 +71,26 @@ const tableColumns: { [key: string]: ColDef } = {
valueFormatter: (params: ValueFormatterParams) => `${params.value} kbps`,
valueGetter: (params: ValueGetterParams) => (params.data ? params.data.bitRate : undefined),
},
bpm: {
cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'center' }),
colId: TableColumn.BPM,
headerComponent: (params: IHeaderParams) => GenericTableHeader(params, { position: 'center' }),
headerName: 'BPM',
valueGetter: (params: ValueGetterParams) => (params.data ? params.data.bpm : undefined),
},
channels: {
cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'center' }),
colId: TableColumn.CHANNELS,
field: 'channels',
headerComponent: (params: IHeaderParams) => GenericTableHeader(params, { position: 'center' }),
valueGetter: (params: ValueGetterParams) => (params.data ? params.data.channels : undefined),
},
comment: {
cellRenderer: GenericCell,
colId: TableColumn.COMMENT,
headerName: 'Note',
valueGetter: (params: ValueGetterParams) => (params.data ? params.data.comment : undefined),
},
dateAdded: {
cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'left' }),
colId: TableColumn.DATE_ADDED,
@ -106,6 +126,27 @@ const tableColumns: { [key: string]: ColDef } = {
headerName: 'Genre',
valueGetter: (params: ValueGetterParams) => (params.data ? params.data.genres : undefined),
},
lastPlayedAt: {
cellRenderer: GenericCell,
colId: TableColumn.LAST_PLAYED,
headerName: 'Last Played',
valueGetter: (params: ValueGetterParams) =>
params.data ? params.data.lastPlayedAt : undefined,
},
path: {
cellRenderer: GenericCell,
colId: TableColumn.PATH,
headerName: 'Path',
valueGetter: (params: ValueGetterParams) => (params.data ? params.data.path : undefined),
},
playCount: {
cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'center' }),
colId: TableColumn.PLAY_COUNT,
field: 'playCount',
headerComponent: (params: IHeaderParams) => GenericTableHeader(params, { position: 'center' }),
headerName: 'Plays',
valueGetter: (params: ValueGetterParams) => (params.data ? params.data.playCount : undefined),
},
releaseDate: {
cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'center' }),
colId: TableColumn.RELEASE_DATE,

View file

@ -7,7 +7,7 @@ import { Text } from '/@/renderer/components/text';
import { useSettingsStoreActions, useSettingsStore } from '/@/renderer/store/settings.store';
import { TableColumn, TableType } from '/@/renderer/types';
export const tableColumns = [
export const SONG_TABLE_COLUMNS = [
{ label: 'Row Index', value: TableColumn.ROW_INDEX },
{ label: 'Title', value: TableColumn.TITLE },
{ label: 'Title (Combined)', value: TableColumn.TITLE_COMBINED },
@ -21,13 +21,17 @@ export const tableColumns = [
{ label: 'Disc Number', value: TableColumn.DISC_NUMBER },
{ label: 'Track Number', value: TableColumn.TRACK_NUMBER },
{ label: 'Bitrate', value: TableColumn.BIT_RATE },
// { label: 'Size', value: TableColumn.SIZE },
// { label: 'Skip', value: TableColumn.SKIP },
// { label: 'Path', value: TableColumn.PATH },
// { label: 'Play Count', value: TableColumn.PLAY_COUNT },
{ label: 'Last Played', value: TableColumn.LAST_PLAYED },
{ label: 'Note', value: TableColumn.COMMENT },
{ label: 'Channels', value: TableColumn.CHANNELS },
{ label: 'BPM', value: TableColumn.BPM },
{ label: 'Date Added', value: TableColumn.DATE_ADDED },
{ label: 'Path', value: TableColumn.PATH },
{ label: 'Plays', value: TableColumn.PLAY_COUNT },
// { label: 'Favorite', value: TableColumn.FAVORITE },
// { label: 'Rating', value: TableColumn.RATING },
{ label: 'Date Added', value: TableColumn.DATE_ADDED },
// { label: 'Size', value: TableColumn.SIZE },
// { label: 'Skip', value: TableColumn.SKIP },
];
interface TableConfigDropdownProps {
@ -130,7 +134,7 @@ export const TableConfigDropdown = ({ type }: TableConfigDropdownProps) => {
<Text>Table Columns</Text>
<MultiSelect
clearable
data={tableColumns}
data={SONG_TABLE_COLUMNS}
defaultValue={tableConfig[type]?.columns.map((column) => column.column)}
dropdownPosition="top"
width={300}

View file

@ -1,104 +1,144 @@
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
import { Group } from '@mantine/core';
import { useForm } from '@mantine/form';
import { useDisclosure } from '@mantine/hooks';
import { MutableRefObject } from 'react';
import { RiHashtag } from 'react-icons/ri';
import { Button } from '/@/renderer/components/button';
import { MotionFlex } from '../motion';
import { NumberInput } from '/@/renderer/components/input';
import { Pagination } from '/@/renderer/components/pagination';
import { Popover } from '/@/renderer/components/popover';
import { Text } from '/@/renderer/components/text';
import { useContainerQuery } from '/@/renderer/hooks';
import { TablePagination as TablePaginationType } from '/@/renderer/types';
interface TablePaginationProps {
containerQuery: ReturnType<typeof useContainerQuery>;
pagination: {
currentPage: number;
itemsPerPage: number;
totalPages: number;
};
tableRef: any;
pagination: TablePaginationType;
setPagination: (pagination: Partial<TablePaginationType>) => void;
tableRef: MutableRefObject<AgGridReactType | null>;
}
export const TablePagination = ({ tableRef, containerQuery, pagination }: TablePaginationProps) => {
export const TablePagination = ({ tableRef, pagination, setPagination }: TablePaginationProps) => {
const [isGoToPageOpen, handlers] = useDisclosure(false);
const containerQuery = useContainerQuery();
const form = useForm({
const goToForm = useForm({
initialValues: {
pageNumber: undefined,
},
});
const handlePagination = (index: number) => {
tableRef.current?.api.paginationGoToPage(index - 1);
const newPage = index - 1;
tableRef.current?.api.paginationGoToPage(newPage);
setPagination({ currentPage: newPage });
};
const handleGoSubmit = form.onSubmit((values) => {
const handleGoSubmit = goToForm.onSubmit((values) => {
handlers.close();
if (!values.pageNumber || values.pageNumber < 1 || values.pageNumber > pagination.totalPages) {
return;
}
tableRef.current?.api.paginationGoToPage(values.pageNumber - 1);
const newPage = values.pageNumber - 1;
tableRef.current?.api.paginationGoToPage(newPage);
setPagination({ currentPage: newPage });
});
return (
<Group
ref={containerQuery.ref}
noWrap
spacing="sm"
>
<Popover
trapFocus
opened={isGoToPageOpen}
position="bottom-start"
transition="fade"
onClose={() => handlers.close()}
>
<Popover.Target>
<Button
compact
radius="sm"
size="lg"
sx={{ height: '32px', padding: 0, width: '32px' }}
tooltip={{ label: 'Go to page' }}
variant="default"
onClick={() => handlers.toggle()}
>
<RiHashtag size={15} />
</Button>
</Popover.Target>
<Popover.Dropdown>
<form onSubmit={handleGoSubmit}>
<Group>
<NumberInput
{...form.getInputProps('pageNumber')}
hideControls={false}
max={pagination.totalPages}
min={1}
width={70}
/>
<Button
type="submit"
variant="filled"
>
Go
</Button>
</Group>
</form>
</Popover.Dropdown>
</Popover>
const currentPageStartIndex = pagination.currentPage * pagination.itemsPerPage + 1;
const currentPageStopIndex = (pagination.currentPage + 1) * pagination.itemsPerPage;
<Pagination
return (
<MotionFlex
ref={containerQuery.ref}
layout
align="center"
animate={{ y: 0 }}
exit={{ y: 50 }}
initial={{ y: 50 }}
justify="space-between"
p="1rem"
sx={{ borderTop: '1px solid var(--generic-border-color)' }}
>
<Text
$secondary
size="md"
>
{containerQuery.isMd ? (
<>
Showing <b>{currentPageStartIndex}</b> - <b>{currentPageStopIndex}</b> of{' '}
<b>{pagination.totalItems}</b> items
</>
) : containerQuery.isSm ? (
<>
<b>{currentPageStartIndex}</b> - <b>{currentPageStopIndex}</b> of{' '}
<b>{pagination.totalItems}</b> items
</>
) : (
<>
<b>{currentPageStartIndex}</b> - <b>{currentPageStopIndex}</b> of{' '}
<b>{pagination.totalItems}</b>
</>
)}
</Text>
<Group
ref={containerQuery.ref}
noWrap
$hideDividers={!containerQuery.isMd}
boundaries={1}
page={pagination.currentPage + 1}
radius="sm"
// siblings={containerQuery.isSm ? 1 : 0}
siblings={1}
total={pagination.totalPages - 1}
withControls={containerQuery.isSm}
onChange={handlePagination}
/>
</Group>
spacing="sm"
>
<Popover
trapFocus
opened={isGoToPageOpen}
position="bottom-start"
transition="fade"
onClose={() => handlers.close()}
>
<Popover.Target>
<Button
compact
radius="sm"
size="lg"
sx={{ height: '32px', padding: 0, width: '32px' }}
tooltip={{ label: 'Go to page' }}
variant="default"
onClick={() => handlers.toggle()}
>
<RiHashtag size={15} />
</Button>
</Popover.Target>
<Popover.Dropdown>
<form onSubmit={handleGoSubmit}>
<Group>
<NumberInput
{...goToForm.getInputProps('pageNumber')}
hideControls={false}
max={pagination.totalPages}
min={1}
width={70}
/>
<Button
type="submit"
variant="filled"
>
Go
</Button>
</Group>
</form>
</Popover.Dropdown>
</Popover>
<Pagination
noWrap
$hideDividers={!containerQuery.isMd}
boundaries={1}
page={pagination.currentPage + 1}
radius="sm"
siblings={1}
total={pagination.totalPages - 1}
withControls={containerQuery.isSm}
onChange={handlePagination}
/>
</Group>
</MotionFlex>
);
};

View file

@ -1,6 +1,13 @@
import { Album, AlbumArtist, Artist } from '/@/renderer/api/types';
import { AppRoute } from '/@/renderer/router/routes';
export type TablePagination = {
currentPage: number;
itemsPerPage: number;
totalItems: number;
totalPages: number;
};
export type RouteSlug = {
idProperty: string;
slugProperty: string;
@ -128,18 +135,21 @@ export enum TableColumn {
ALBUM_ARTIST = 'albumArtist',
ARTIST = 'artist',
BIT_RATE = 'bitRate',
BPM = 'bpm',
CHANNELS = 'channels',
COMMENT = 'comment',
DATE_ADDED = 'dateAdded',
DISC_NUMBER = 'discNumber',
DURATION = 'duration',
// FAVORITE = 'favorite',
FAVORITE = 'favorite',
GENRE = 'genre',
// PATH = 'path',
// PLAY_COUNT = 'playCount',
// RATING = 'rating',
LAST_PLAYED = 'lastPlayedAt',
PATH = 'path',
PLAY_COUNT = 'playCount',
RATING = 'rating',
RELEASE_DATE = 'releaseDate',
ROW_INDEX = 'rowIndex',
// SKIP = 'skip',
// SIZE = 'size',
TITLE = 'title',
TITLE_COMBINED = 'titleCombined',
TRACK_NUMBER = 'trackNumber',