diff --git a/src/renderer/components/item-list/item-table-list/default-columns.ts b/src/renderer/components/item-list/item-table-list/default-columns.ts index 878b5f05d..b7eb98cee 100644 --- a/src/renderer/components/item-list/item-table-list/default-columns.ts +++ b/src/renderer/components/item-list/item-table-list/default-columns.ts @@ -1,7 +1,8 @@ import i18n from '/@/i18n/i18n'; +import { ItemGridListRowConfig, ItemTableListColumnConfig } from '/@/renderer/store'; import { TableColumn } from '/@/shared/types/types'; -type DefaultTableColumn = { +export type DefaultTableColumn = { align: 'center' | 'end' | 'start'; autoSize: boolean; isEnabled: boolean; @@ -638,6 +639,24 @@ export const GENRE_TABLE_COLUMNS: DefaultTableColumn[] = [ value: TableColumn.TITLE, width: 300, }, + { + align: 'center', + autoSize: false, + isEnabled: true, + label: i18n.t('table.config.label.songCount', { postProcess: 'titleCase' }), + pinned: null, + value: TableColumn.SONG_COUNT, + width: 100, + }, + { + align: 'center', + autoSize: false, + isEnabled: true, + label: i18n.t('table.config.label.albumCount', { postProcess: 'titleCase' }), + pinned: null, + value: TableColumn.ALBUM_COUNT, + width: 100, + }, { align: 'center', autoSize: false, @@ -650,38 +669,77 @@ export const GENRE_TABLE_COLUMNS: DefaultTableColumn[] = [ ]; export const pickTableColumns = (options: { + alignCenterColumns?: TableColumn[]; + alignLeftColumns?: TableColumn[]; + alignRightColumns?: TableColumn[]; autoSizeColumns?: TableColumn[]; columns: DefaultTableColumn[]; enabledColumns: TableColumn[]; + pickColumns?: TableColumn[]; pinnedLeftColumns?: TableColumn[]; pinnedRightColumns?: TableColumn[]; -}) => { +}): ItemTableListColumnConfig[] => { const { + alignCenterColumns = [], + alignLeftColumns = [], + alignRightColumns = [], autoSizeColumns = [], columns, enabledColumns, + pickColumns = [], pinnedLeftColumns = [], pinnedRightColumns = [], } = options; - return columns.map((column) => { - const pinned: 'left' | 'right' | null = pinnedLeftColumns.includes(column.value) - ? 'left' - : pinnedRightColumns.includes(column.value) - ? 'right' - : null; + const columnsToPick: ItemTableListColumnConfig[] = []; + + columns.forEach((column) => { + if (pickColumns.length > 0 && !pickColumns?.includes(column.value)) { + return; + } + + let pinned: 'left' | 'right' | null = column.pinned; + + if (pinnedLeftColumns.includes(column.value)) { + pinned = 'left'; + } else if (pinnedRightColumns.includes(column.value)) { + pinned = 'right'; + } + + let align: 'center' | 'end' | 'start' = column.align; + + if (alignCenterColumns.includes(column.value)) { + align = 'center'; + } else if (alignLeftColumns.includes(column.value)) { + align = 'start'; + } else if (alignRightColumns.includes(column.value)) { + align = 'end'; + } const isEnabled = enabledColumns.includes(column.value); const autoSize = autoSizeColumns.includes(column.value); - return { - align: column.align, + columnsToPick.push({ + align, autoSize, id: column.value, isEnabled, pinned, width: column.width, - }; + }); }); + + return columnsToPick; +}; + +export const pickGridRows = ( + options: Parameters[0], +): ItemGridListRowConfig[] => { + const columns = pickTableColumns(options); + return columns.map((column) => ({ + align: column.align, + id: column.id as TableColumn, + isEnabled: column.isEnabled, + })); }; diff --git a/src/renderer/features/albums/components/album-list-header-filters.tsx b/src/renderer/features/albums/components/album-list-header-filters.tsx index ed5ba5dbd..b8028fc54 100644 --- a/src/renderer/features/albums/components/album-list-header-filters.tsx +++ b/src/renderer/features/albums/components/album-list-header-filters.tsx @@ -1,4 +1,4 @@ -import { ALBUM_TABLE_COLUMNS } from '/@/renderer/components/virtual-table'; +import { ALBUM_TABLE_COLUMNS } from '/@/renderer/components/item-list/item-table-list/default-columns'; import { ListConfigMenu } from '/@/renderer/features/shared/components/list-config-menu'; import { ListFilters } from '/@/renderer/features/shared/components/list-filters'; import { ListMusicFolderDropdown } from '/@/renderer/features/shared/components/list-music-folder-dropdown'; diff --git a/src/renderer/features/artists/components/album-artist-list-header-filters.tsx b/src/renderer/features/artists/components/album-artist-list-header-filters.tsx index 84a573bb0..f6e8175de 100644 --- a/src/renderer/features/artists/components/album-artist-list-header-filters.tsx +++ b/src/renderer/features/artists/components/album-artist-list-header-filters.tsx @@ -1,4 +1,4 @@ -import { ALBUMARTIST_TABLE_COLUMNS } from '/@/renderer/components/virtual-table'; +import { ALBUM_ARTIST_TABLE_COLUMNS } from '/@/renderer/components/item-list/item-table-list/default-columns'; import { ListConfigMenu } from '/@/renderer/features/shared/components/list-config-menu'; import { ListMusicFolderDropdown } from '/@/renderer/features/shared/components/list-music-folder-dropdown'; import { ListRefreshButton } from '/@/renderer/features/shared/components/list-refresh-button'; @@ -33,7 +33,7 @@ export const AlbumArtistListHeaderFilters = () => { diff --git a/src/renderer/features/artists/components/artist-list-header-filters.tsx b/src/renderer/features/artists/components/artist-list-header-filters.tsx index 89b3b6dbd..dfa5d8824 100644 --- a/src/renderer/features/artists/components/artist-list-header-filters.tsx +++ b/src/renderer/features/artists/components/artist-list-header-filters.tsx @@ -1,6 +1,6 @@ import { useQuery } from '@tanstack/react-query'; -import { ARTIST_TABLE_COLUMNS } from '/@/renderer/components/virtual-table'; +import { ALBUMARTIST_TABLE_COLUMNS } from '/@/renderer/components/virtual-table'; import { sharedQueries } from '/@/renderer/features/shared/api/shared-api'; import { ListConfigMenu } from '/@/renderer/features/shared/components/list-config-menu'; import { ListMusicFolderDropdown } from '/@/renderer/features/shared/components/list-music-folder-dropdown'; @@ -52,7 +52,7 @@ export const ArtistListHeaderFilters = () => { diff --git a/src/renderer/features/genres/components/genre-list-header-filters.tsx b/src/renderer/features/genres/components/genre-list-header-filters.tsx index 2795e0ca7..b58ecee78 100644 --- a/src/renderer/features/genres/components/genre-list-header-filters.tsx +++ b/src/renderer/features/genres/components/genre-list-header-filters.tsx @@ -1,4 +1,4 @@ -import { GENRE_TABLE_COLUMNS } from '/@/renderer/components/virtual-table'; +import { GENRE_TABLE_COLUMNS } from '/@/renderer/components/item-list/item-table-list/default-columns'; import { ListConfigMenu } from '/@/renderer/features/shared/components/list-config-menu'; import { ListFilters } from '/@/renderer/features/shared/components/list-filters'; import { ListMusicFolderDropdown } from '/@/renderer/features/shared/components/list-music-folder-dropdown'; diff --git a/src/renderer/features/now-playing/components/play-queue-list-controls.tsx b/src/renderer/features/now-playing/components/play-queue-list-controls.tsx index 25a16f397..fec6b9c1a 100644 --- a/src/renderer/features/now-playing/components/play-queue-list-controls.tsx +++ b/src/renderer/features/now-playing/components/play-queue-list-controls.tsx @@ -1,8 +1,8 @@ import { type MutableRefObject } from 'react'; import { useTranslation } from 'react-i18next'; +import { SONG_TABLE_COLUMNS } from '/@/renderer/components/item-list/item-table-list/default-columns'; import { ItemListHandle } from '/@/renderer/components/item-list/types'; -import { SONG_TABLE_COLUMNS } from '/@/renderer/components/virtual-table'; import { usePlayerContext } from '/@/renderer/features/player/context/player-context'; import { updateSong } from '/@/renderer/features/player/update-remote-song'; import { ListConfigMenu } from '/@/renderer/features/shared/components/list-config-menu'; diff --git a/src/renderer/features/shared/components/grid-config.tsx b/src/renderer/features/shared/components/grid-config.tsx index 8bf505072..004367022 100644 --- a/src/renderer/features/shared/components/grid-config.tsx +++ b/src/renderer/features/shared/components/grid-config.tsx @@ -1,19 +1,41 @@ -import { useMemo } from 'react'; +import { + attachClosestEdge, + type Edge, + extractClosestEdge, +} from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge'; +import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine'; +import { + draggable, + dropTargetForElements, +} from '@atlaskit/pragmatic-drag-and-drop/element/adapter'; +import { disableNativeDragPreview } from '@atlaskit/pragmatic-drag-and-drop/element/disable-native-drag-preview'; +import { useDebouncedState } from '@mantine/hooks'; +import clsx from 'clsx'; +import Fuse from 'fuse.js'; +import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; +import styles from './table-config.module.css'; + import { ListConfigTable } from '/@/renderer/features/shared/components/list-config-menu'; import { DataGridProps, + ItemGridListRowConfig, ItemListSettings, useSettingsStore, useSettingsStoreActions, } from '/@/renderer/store'; -import { ActionIcon } from '/@/shared/components/action-icon/action-icon'; +import { ActionIcon, ActionIconGroup } from '/@/shared/components/action-icon/action-icon'; import { Badge } from '/@/shared/components/badge/badge'; import { Checkbox } from '/@/shared/components/checkbox/checkbox'; +import { Divider } from '/@/shared/components/divider/divider'; import { Group } from '/@/shared/components/group/group'; import { SegmentedControl } from '/@/shared/components/segmented-control/segmented-control'; import { Slider } from '/@/shared/components/slider/slider'; +import { Stack } from '/@/shared/components/stack/stack'; +import { TextInput } from '/@/shared/components/text-input/text-input'; +import { Text } from '/@/shared/components/text/text'; +import { dndUtils, DragData, DragOperation, DragTarget } from '/@/shared/types/drag-and-drop'; import { ItemListKey, ListPaginationType } from '/@/shared/types/types'; type GridConfigProps = { @@ -22,10 +44,11 @@ type GridConfigProps = { id: string; label: string; }[]; + gridRowsData: { label: string; value: string }[]; listKey: ItemListKey; }; -export const GridConfig = ({ extraOptions, listKey }: GridConfigProps) => { +export const GridConfig = ({ extraOptions, gridRowsData, listKey }: GridConfigProps) => { const { t } = useTranslation(); const list = useSettingsStore((state) => state.lists[listKey]) as ItemListSettings; @@ -193,5 +216,405 @@ export const GridConfig = ({ extraOptions, listKey }: GridConfigProps) => { ]; }, [list, t, grid, extraOptions, setList, listKey]); - return ; + return ( + <> + + + setList(listKey, { ...list, grid: { ...grid, rows } })} + value={grid.rows} + /> + + ); }; + +const GridRowConfig = ({ + data, + listKey, + onChange, + value, +}: { + data: { label: string; value: string }[]; + listKey: ItemListKey; + onChange: (value: ItemGridListRowConfig[]) => void; + value: ItemGridListRowConfig[]; +}) => { + const { t } = useTranslation(); + + const labelMap = useMemo(() => { + return data.reduce( + (acc, item) => { + acc[item.value] = item.label; + return acc; + }, + {} as Record, + ); + }, [data]); + + console.log('data', data); + console.log(labelMap); + + const handleChangeEnabled = useCallback( + (item: ItemGridListRowConfig, checked: boolean) => { + const value = useSettingsStore.getState().lists[listKey]?.grid.rows; + if (!value) return; + const index = value.findIndex((v) => v.id === item.id); + const newValues = [...value]; + newValues[index] = { ...newValues[index], isEnabled: checked }; + onChange(newValues); + }, + [listKey, onChange], + ); + + const handleMoveUp = useCallback( + (item: ItemGridListRowConfig) => { + const value = useSettingsStore.getState().lists[listKey]?.grid.rows; + if (!value) return; + const index = value.findIndex((v) => v.id === item.id); + if (index === 0) return; + const newValues = [...value]; + [newValues[index], newValues[index - 1]] = [newValues[index - 1], newValues[index]]; + onChange(newValues); + }, + [listKey, onChange], + ); + + const handleMoveDown = useCallback( + (item: ItemGridListRowConfig) => { + const value = useSettingsStore.getState().lists[listKey]?.grid.rows; + if (!value) return; + const index = value.findIndex((v) => v.id === item.id); + if (index === value.length - 1) return; + const newValues = [...value]; + [newValues[index], newValues[index + 1]] = [newValues[index + 1], newValues[index]]; + onChange(newValues); + }, + [listKey, onChange], + ); + + const handleAlignLeft = useCallback( + (item: ItemGridListRowConfig) => { + const value = useSettingsStore.getState().lists[listKey]?.grid.rows; + if (!value) return; + const index = value.findIndex((v) => v.id === item.id); + const newValues = [...value]; + newValues[index] = { ...newValues[index], align: 'start' }; + onChange(newValues); + }, + [listKey, onChange], + ); + + const handleAlignCenter = useCallback( + (item: ItemGridListRowConfig) => { + const value = useSettingsStore.getState().lists[listKey]?.grid.rows; + if (!value) return; + const index = value.findIndex((v) => v.id === item.id); + const newValues = [...value]; + newValues[index] = { ...newValues[index], align: 'center' }; + onChange(newValues); + }, + [listKey, onChange], + ); + + const handleAlignRight = useCallback( + (item: ItemGridListRowConfig) => { + const value = useSettingsStore.getState().lists[listKey]?.grid.rows; + if (!value) return; + const index = value.findIndex((v) => v.id === item.id); + const newValues = [...value]; + newValues[index] = { ...newValues[index], align: 'end' }; + onChange(newValues); + }, + [listKey, onChange], + ); + + const [searchRows, setSearchRows] = useDebouncedState('', 300); + + const fuse = useMemo(() => { + return new Fuse(value, { + getFn: (obj) => { + return labelMap[obj.id] || ''; + }, + includeMatches: true, + includeScore: true, + keys: ['id', 'label'], + threshold: 0.3, + }); + }, [value, labelMap]); + + const filteredRows = useMemo(() => { + if (!searchRows.trim()) { + return value.map((item) => ({ item, matches: null })); + } + + const results = fuse.search(searchRows); + const resultMap = new Map(results.map((result) => [result.item.id, result.matches])); + + return value.map((item) => ({ + item, + matches: resultMap.get(item.id) || null, + })); + }, [value, searchRows, fuse]); + + const handleReorder = useCallback( + (idFrom: string, idTo: string, edge: Edge | null) => { + const idList = value.map((item) => item.id); + const newIdOrder = dndUtils.reorderById({ + edge, + idFrom, + idTo, + list: idList, + }); + + // Map the new ID order back to full items + const newOrder = newIdOrder.map((id) => value.find((item) => item.id === id)!); + onChange(newOrder); + }, + [onChange, value], + ); + + return ( + + + {t('common.gridRows', { postProcess: 'sentenceCase' })} + setSearchRows(e.currentTarget.value)} + placeholder={t('common.search', { + postProcess: 'sentenceCase', + })} + size="xs" + /> + +
+ {filteredRows.map(({ item, matches }) => ( + + ))} +
+
+ ); +}; + +const DragHandle = ({ dragHandleRef }: { dragHandleRef: React.RefObject }) => { + return ( + + ); +}; + +const GridRowItem = memo( + ({ + handleAlignCenter, + handleAlignLeft, + handleAlignRight, + handleChangeEnabled, + handleMoveDown, + handleMoveUp, + handleReorder, + item, + label, + matches, + }: { + handleAlignCenter: (item: ItemGridListRowConfig) => void; + handleAlignLeft: (item: ItemGridListRowConfig) => void; + handleAlignRight: (item: ItemGridListRowConfig) => void; + handleChangeEnabled: (item: ItemGridListRowConfig, checked: boolean) => void; + handleMoveDown: (item: ItemGridListRowConfig) => void; + handleMoveUp: (item: ItemGridListRowConfig) => void; + handleReorder: (idFrom: string, idTo: string, edge: Edge | null) => void; + item: ItemGridListRowConfig; + label: string; + matches: null | readonly Fuse.FuseResultMatch[]; + }) => { + const { t } = useTranslation(); + const ref = useRef(null); + const dragHandleRef = useRef(null); + const [isDragging, setIsDragging] = useState(false); + const [isDraggedOver, setIsDraggedOver] = useState(null); + + useEffect(() => { + if (!ref.current || !dragHandleRef.current) { + return; + } + + return combine( + draggable({ + element: dragHandleRef.current, + getInitialData: () => { + const data = dndUtils.generateDragData({ + id: [item.id], + operation: [DragOperation.REORDER], + type: DragTarget.GRID_ROW, + }); + return data; + }, + onDragStart: () => { + setIsDragging(true); + }, + onDrop: () => { + setIsDragging(false); + }, + onGenerateDragPreview: (data) => { + disableNativeDragPreview({ nativeSetDragImage: data.nativeSetDragImage }); + }, + }), + dropTargetForElements({ + canDrop: (args) => { + const data = args.source.data as unknown as DragData; + const isSelf = (args.source.data.id as string[])[0] === item.id; + return dndUtils.isDropTarget(data.type, [DragTarget.GRID_ROW]) && !isSelf; + }, + element: ref.current, + getData: ({ element, input }) => { + const data = dndUtils.generateDragData({ + id: [item.id], + operation: [DragOperation.REORDER], + type: DragTarget.GRID_ROW, + }); + + return attachClosestEdge(data, { + allowedEdges: ['top', 'bottom'], + element, + input, + }); + }, + onDrag: (args) => { + const closestEdgeOfTarget: Edge | null = extractClosestEdge(args.self.data); + setIsDraggedOver(closestEdgeOfTarget); + }, + onDragLeave: () => { + setIsDraggedOver(null); + }, + onDrop: (args) => { + const closestEdgeOfTarget: Edge | null = extractClosestEdge(args.self.data); + + const from = args.source.data.id as string[]; + const to = args.self.data.id as string[]; + + handleReorder(from[0], to[0], closestEdgeOfTarget); + setIsDraggedOver(null); + }, + }), + ); + }, [item.id, handleReorder]); + + return ( +
0, + })} + ref={ref} + > + + + handleChangeEnabled(item, e.currentTarget.checked)} + size="sm" + /> + + + + handleMoveUp(item)} + size="xs" + tooltip={{ + label: t('table.config.general.moveUp', { + postProcess: 'sentenceCase', + }), + }} + variant="subtle" + /> + handleMoveDown(item)} + size="xs" + tooltip={{ + label: t('table.config.general.moveDown', { + postProcess: 'sentenceCase', + }), + }} + variant="subtle" + /> + + + handleAlignLeft(item)} + size="xs" + tooltip={{ + label: t('table.config.general.alignLeft', { + postProcess: 'sentenceCase', + }), + }} + variant={item.align === 'start' ? 'filled' : 'subtle'} + /> + handleAlignCenter(item)} + size="xs" + tooltip={{ + label: t('table.config.general.alignCenter', { + postProcess: 'sentenceCase', + }), + }} + variant={item.align === 'center' ? 'filled' : 'subtle'} + /> + handleAlignRight(item)} + size="xs" + tooltip={{ + label: t('table.config.general.alignRight', { + postProcess: 'sentenceCase', + }), + }} + variant={item.align === 'end' ? 'filled' : 'subtle'} + /> + + +
+ ); + }, + (prevProps, nextProps) => { + return ( + prevProps.item.id === nextProps.item.id && + prevProps.item.isEnabled === nextProps.item.isEnabled && + prevProps.item.align === nextProps.item.align && + prevProps.label === nextProps.label && + prevProps.matches === nextProps.matches + ); + }, +); diff --git a/src/renderer/features/shared/components/list-config-menu.tsx b/src/renderer/features/shared/components/list-config-menu.tsx index cf168058d..cd3dd9b50 100644 --- a/src/renderer/features/shared/components/list-config-menu.tsx +++ b/src/renderer/features/shared/components/list-config-menu.tsx @@ -122,14 +122,15 @@ export const ListConfigMenu = (props: ListConfigMenuProps) => { const Config = ({ displayType, + tableColumnsData, ...props }: ListConfigMenuProps & { displayType: ListDisplayType }) => { switch (displayType) { case ListDisplayType.GRID: - return ; + return ; case ListDisplayType.TABLE: - return ; + return ; default: return null; diff --git a/src/renderer/store/settings.store.ts b/src/renderer/store/settings.store.ts index e3356a6ea..e7ebaab31 100644 --- a/src/renderer/store/settings.store.ts +++ b/src/renderer/store/settings.store.ts @@ -11,6 +11,7 @@ import { ALBUM_ARTIST_TABLE_COLUMNS, ALBUM_TABLE_COLUMNS, GENRE_TABLE_COLUMNS, + pickGridRows, pickTableColumns, PLAYLIST_SONG_TABLE_COLUMNS, PLAYLIST_TABLE_COLUMNS, @@ -124,6 +125,16 @@ const ItemTableListColumnConfigSchema = z.object({ width: z.number(), }); +export type ItemTableListColumnConfig = z.infer; + +const ItemGridListRowConfigSchema = z.object({ + align: z.enum(['center', 'end', 'start']), + id: z.nativeEnum(TableColumn), + isEnabled: z.boolean(), +}); + +export type ItemGridListRowConfig = z.infer; + const ItemTableListPropsSchema = z.object({ autoFitColumns: z.boolean(), columns: z.array(ItemTableListColumnConfigSchema), @@ -140,6 +151,7 @@ const ItemListConfigSchema = z.object({ itemGap: z.enum(['lg', 'md', 'sm', 'xl', 'xs']), itemsPerRow: z.number(), itemsPerRowEnabled: z.boolean(), + rows: z.array(ItemGridListRowConfigSchema), }), itemsPerPage: z.number(), pagination: z.nativeEnum(ListPaginationType), @@ -421,6 +433,7 @@ export type DataGridProps = { itemGap: 'lg' | 'md' | 'sm' | 'xl' | 'xs'; itemsPerRow: number; itemsPerRowEnabled: boolean; + rows: ItemGridListRowConfig[]; }; export type DataTableProps = z.infer; @@ -650,6 +663,7 @@ const initialState: SettingsState = { itemGap: 'md', itemsPerRow: 6, itemsPerRowEnabled: false, + rows: [], }, itemsPerPage: 100, pagination: ListPaginationType.INFINITE, @@ -676,6 +690,28 @@ const initialState: SettingsState = { itemGap: 'md', itemsPerRow: 6, itemsPerRowEnabled: false, + rows: pickGridRows({ + alignLeftColumns: [ + TableColumn.TITLE, + TableColumn.ALBUM_ARTIST, + TableColumn.YEAR, + ], + columns: ALBUM_TABLE_COLUMNS, + enabledColumns: [TableColumn.TITLE, TableColumn.ALBUM_ARTIST, TableColumn.YEAR], + pickColumns: [ + TableColumn.TITLE, + TableColumn.DURATION, + TableColumn.ALBUM_ARTIST, + TableColumn.BIT_RATE, + TableColumn.BPM, + TableColumn.DATE_ADDED, + TableColumn.DURATION, + TableColumn.GENRE, + TableColumn.PLAY_COUNT, + TableColumn.SONG_COUNT, + TableColumn.YEAR, + ], + }), }, itemsPerPage: 100, pagination: ListPaginationType.INFINITE, @@ -702,6 +738,17 @@ const initialState: SettingsState = { itemGap: 'md', itemsPerRow: 6, itemsPerRowEnabled: false, + rows: pickGridRows({ + alignLeftColumns: [TableColumn.TITLE], + columns: ALBUM_ARTIST_TABLE_COLUMNS, + enabledColumns: [TableColumn.TITLE], + pickColumns: [ + TableColumn.TITLE, + TableColumn.PLAY_COUNT, + TableColumn.ALBUM_COUNT, + TableColumn.SONG_COUNT, + ], + }), }, itemsPerPage: 100, pagination: ListPaginationType.INFINITE, @@ -728,6 +775,17 @@ const initialState: SettingsState = { itemGap: 'md', itemsPerRow: 6, itemsPerRowEnabled: false, + rows: pickGridRows({ + alignLeftColumns: [TableColumn.TITLE], + columns: ALBUM_ARTIST_TABLE_COLUMNS, + enabledColumns: [TableColumn.TITLE], + pickColumns: [ + TableColumn.TITLE, + TableColumn.PLAY_COUNT, + TableColumn.ALBUM_COUNT, + TableColumn.SONG_COUNT, + ], + }), }, itemsPerPage: 100, pagination: ListPaginationType.INFINITE, @@ -754,6 +812,24 @@ const initialState: SettingsState = { itemGap: 'md', itemsPerRow: 6, itemsPerRowEnabled: false, + rows: pickGridRows({ + alignLeftColumns: [ + TableColumn.TITLE, + TableColumn.SONG_COUNT, + TableColumn.ALBUM_COUNT, + ], + columns: GENRE_TABLE_COLUMNS, + enabledColumns: [ + TableColumn.TITLE, + TableColumn.SONG_COUNT, + TableColumn.ALBUM_COUNT, + ], + pickColumns: [ + TableColumn.TITLE, + TableColumn.ALBUM_COUNT, + TableColumn.SONG_COUNT, + ], + }), }, itemsPerPage: 100, pagination: ListPaginationType.INFINITE, @@ -780,6 +856,12 @@ const initialState: SettingsState = { itemGap: 'md', itemsPerRow: 6, itemsPerRowEnabled: false, + rows: pickGridRows({ + alignLeftColumns: [TableColumn.TITLE, TableColumn.SONG_COUNT], + columns: PLAYLIST_TABLE_COLUMNS, + enabledColumns: [TableColumn.TITLE], + pickColumns: [TableColumn.TITLE, TableColumn.SONG_COUNT], + }), }, itemsPerPage: 100, pagination: ListPaginationType.INFINITE, @@ -806,6 +888,7 @@ const initialState: SettingsState = { itemGap: 'md', itemsPerRow: 6, itemsPerRowEnabled: false, + rows: [], }, itemsPerPage: 100, pagination: ListPaginationType.INFINITE, @@ -832,6 +915,7 @@ const initialState: SettingsState = { itemGap: 'md', itemsPerRow: 6, itemsPerRowEnabled: false, + rows: [], }, itemsPerPage: 100, pagination: ListPaginationType.INFINITE, @@ -858,6 +942,25 @@ const initialState: SettingsState = { itemGap: 'md', itemsPerRow: 6, itemsPerRowEnabled: false, + rows: pickGridRows({ + alignLeftColumns: [TableColumn.TITLE, TableColumn.ARTIST], + columns: SONG_TABLE_COLUMNS, + enabledColumns: [TableColumn.TITLE, TableColumn.ARTIST], + pickColumns: [ + TableColumn.TITLE, + TableColumn.ARTIST, + TableColumn.DURATION, + TableColumn.YEAR, + TableColumn.BIT_RATE, + TableColumn.BPM, + TableColumn.CODEC, + TableColumn.DATE_ADDED, + TableColumn.GENRE, + TableColumn.LAST_PLAYED, + TableColumn.RELEASE_DATE, + TableColumn.TRACK_NUMBER, + ], + }), }, itemsPerPage: 100, pagination: ListPaginationType.INFINITE, @@ -884,6 +987,7 @@ const initialState: SettingsState = { itemGap: 'md', itemsPerRow: 6, itemsPerRowEnabled: false, + rows: [], }, itemsPerPage: 100, pagination: ListPaginationType.INFINITE,