From 70518e12f572edc0d734d2dfa313e08dec483c60 Mon Sep 17 00:00:00 2001 From: jeffvli Date: Tue, 26 Oct 2021 02:10:01 -0700 Subject: [PATCH] Add drag/drop component for column selector --- src/__tests__/App.test.tsx | 251 ++++++++++++++++++ .../settings/ConfigPanels/ListViewConfig.tsx | 192 +++++++++++--- .../ConfigPanels/LookAndFeelConfig.tsx | 162 ++++++----- src/components/viewtypes/ListViewTable.tsx | 89 ++++++- src/redux/configSlice.ts | 86 +++++- src/shared/mockSettings.ts | 105 +++++++- 6 files changed, 749 insertions(+), 136 deletions(-) diff --git a/src/__tests__/App.test.tsx b/src/__tests__/App.test.tsx index d467b97..98ac0c0 100644 --- a/src/__tests__/App.test.tsx +++ b/src/__tests__/App.test.tsx @@ -108,6 +108,257 @@ const folderState: FolderSelection = { const configState: ConfigPage = { active: { tab: 'playback', + columnSelectorTab: 'music', + }, + lookAndFeel: { + listView: { + music: { + columns: [ + { + id: '#', + dataKey: 'index', + alignment: 'center', + resizable: true, + width: 50, + label: '# (Drag/Drop)', + }, + { + id: 'Title', + dataKey: 'combinedtitle', + alignment: 'left', + flexGrow: 5, + label: 'Title (Combined)', + }, + { + id: 'Album', + dataKey: 'album', + alignment: 'left', + flexGrow: 3, + label: 'Album', + }, + { + id: 'Duration', + dataKey: 'duration', + alignment: 'center', + flexGrow: 2, + label: 'Duration', + }, + { + id: 'Bitrate', + dataKey: 'bitRate', + alignment: 'left', + flexGrow: 1, + label: 'Bitrate', + }, + { + id: 'Fav', + dataKey: 'starred', + alignment: 'center', + flexGrow: 1, + label: 'Favorite', + }, + ], + rowHeight: 40, + fontSize: 14, + }, + album: { + columns: [ + { + id: '#', + dataKey: 'index', + alignment: 'center', + resizable: true, + width: 50, + label: '#', + }, + { + id: 'Title', + dataKey: 'combinedtitle', + alignment: 'left', + flexGrow: 5, + label: 'Title (Combined)', + }, + { + id: 'Tracks', + dataKey: 'songCount', + alignment: 'center', + flexGrow: 1, + label: 'Track Count', + }, + { + id: 'Duration', + dataKey: 'duration', + alignment: 'center', + flexGrow: 2, + label: 'Duration', + }, + { + id: 'Fav', + dataKey: 'starred', + alignment: 'center', + flexGrow: 1, + label: 'Favorite', + }, + ], + rowHeight: 40, + fontSize: 14, + }, + playlist: { + columns: [ + { + id: '#', + dataKey: 'index', + alignment: 'center', + resizable: true, + width: 50, + label: '#', + }, + { + id: 'Title', + dataKey: 'name', + alignment: 'left', + flexGrow: 5, + label: 'Title', + }, + { + id: 'Description', + dataKey: 'comment', + alignment: 'left', + flexGrow: 3, + label: 'Description', + }, + { + id: 'Tracks', + dataKey: 'songCount', + alignment: 'center', + flexGrow: 1, + label: 'Track Count', + }, + { + id: 'Owner', + dataKey: 'owner', + alignment: 'left', + flexGrow: 2, + label: 'Owner', + }, + { + id: 'Modified', + dataKey: 'changed', + alignment: 'left', + flexGrow: 1, + label: 'Modified', + }, + ], + rowHeight: 40, + fontSize: 14, + }, + artist: { + columns: [ + { + id: '#', + dataKey: 'index', + alignment: 'center', + resizable: true, + width: 50, + label: '#', + }, + { + id: 'Art', + dataKey: 'coverart', + alignment: 'center', + resizable: true, + width: 50, + label: 'CoverArt', + }, + { + id: 'Name', + dataKey: 'name', + alignment: 'left', + flexGrow: 5, + label: 'Name', + }, + { + id: 'Albums', + dataKey: 'albumCount', + alignment: 'left', + flexGrow: 1, + label: 'Album Count', + }, + { + id: 'Fav', + dataKey: 'starred', + alignment: 'center', + flexGrow: 1, + label: 'Favorite', + }, + ], + rowHeight: 40, + fontSize: 14, + }, + genre: { + columns: [ + { + id: '#', + dataKey: 'index', + alignment: 'center', + resizable: true, + width: 50, + label: '#', + }, + { + id: 'Name', + dataKey: 'name', + alignment: 'left', + flexGrow: 5, + label: 'Name', + }, + { + id: 'Albums', + dataKey: 'albumCount', + alignment: 'left', + flexGrow: 3, + label: 'Album Count', + }, + { + id: 'Tracks', + dataKey: 'songCount', + alignment: 'left', + flexGrow: 1, + label: 'Song Count', + }, + ], + rowHeight: 40, + fontSize: 14, + }, + mini: { + columns: [ + { + id: '#', + dataKey: 'index', + alignment: 'center', + resizable: true, + width: 50, + label: '# (Drag/Drop)', + }, + { + id: 'Title', + dataKey: 'combinedtitle', + alignment: 'left', + flexGrow: 5, + label: 'Title (Combined)', + }, + { + id: 'Duration', + dataKey: 'duration', + alignment: 'center', + flexGrow: 2, + label: 'Duration', + }, + ], + rowHeight: 40, + fontSize: 14, + }, + }, }, }; diff --git a/src/components/settings/ConfigPanels/ListViewConfig.tsx b/src/components/settings/ConfigPanels/ListViewConfig.tsx index d63c8b5..bc0a971 100644 --- a/src/components/settings/ConfigPanels/ListViewConfig.tsx +++ b/src/components/settings/ConfigPanels/ListViewConfig.tsx @@ -1,58 +1,188 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; +import { nanoid } from 'nanoid/non-secure'; import settings from 'electron-settings'; -import { TagPicker, ControlLabel } from 'rsuite'; +import { TagPicker, ControlLabel, Panel } from 'rsuite'; import { StyledInputNumber } from '../../shared/styled'; +import ListViewTable from '../../viewtypes/ListViewTable'; +import { useAppDispatch, useAppSelector } from '../../../redux/hooks'; +import { + setIsDragging, + setRangeSelected, + toggleRangeSelected, + toggleSelected, +} from '../../../redux/multiSelectSlice'; +import { ColumnList, moveToIndex, setColumnList } from '../../../redux/configSlice'; + +const columnSelectorColumns = [ + { + id: '#', + dataKey: 'index', + alignment: 'center', + resizable: false, + width: 50, + label: '#', + }, + { + id: 'Column', + dataKey: 'label', + alignment: 'left', + resizable: false, + flexGrow: 2, + label: 'Column', + }, + { + id: 'Resizable', + dataKey: 'columnResizable', + alignment: 'left', + resizable: false, + width: 100, + label: 'Resizable', + }, +]; const ListViewConfig = ({ defaultColumns, columnPicker, columnList, settingsConfig }: any) => { + const dispatch = useAppDispatch(); + const playQueue = useAppSelector((state) => state.playQueue); + const multiSelect = useAppSelector((state) => state.multiSelect); + const config = useAppSelector((state) => state.config); + const [selectedColumns, setSelectedColumns] = useState([]); + const columnListType = settingsConfig.columnList.split('List')[0]; + + useEffect(() => { + const cols = config.lookAndFeel.listView[columnListType].columns.map((col: any) => { + return col.label; + }); + + setSelectedColumns(cols); + + settings.setSync( + settingsConfig.columnList, + config.lookAndFeel.listView[columnListType].columns + ); + }, [columnListType, config.lookAndFeel.listView, settingsConfig.columnList]); + + let timeout: any = null; + const handleRowClick = (e: any, rowData: any) => { + if (timeout === null) { + timeout = window.setTimeout(() => { + timeout = null; + + if (e.ctrlKey) { + dispatch(toggleSelected(rowData)); + } else if (e.shiftKey) { + dispatch(setRangeSelected(rowData)); + dispatch(toggleRangeSelected(config.lookAndFeel.listView[columnListType].columns)); + } + }, 100); + } + }; + + const handleDragEnd = (listType: ColumnList) => { + if (multiSelect.isDragging) { + dispatch( + moveToIndex({ + entries: multiSelect.selected, + moveBeforeId: multiSelect.currentMouseOverId, + listType, + }) + ); + dispatch(setIsDragging(false)); + } + }; + return (
-
- { - const columns: any[] = []; - - if (e) { - e.map((selected: string) => { - const selectedColumn = columnList.find((column: any) => column.label === selected); - if (selectedColumn) { - return columns.push(selectedColumn.value); +
+ + { + const columns: any[] = []; + if (e) { + e.forEach((selected: string) => { + const alreadySelectedColumn = config.lookAndFeel.listView[ + columnListType + ].columns.find((column: any) => column.label === selected); + + if (alreadySelectedColumn) { + return columns.push(alreadySelectedColumn); + } + + const selectedColumn = columnList.find( + (column: any) => column.label === selected + ); + + if (selectedColumn) { + return columns.push({ ...selectedColumn.value, uniqueId: nanoid() }); + } + + return null; + }); } - return null; - }); - } + const cleanColumns = columns.map((col) => { + const { uniqueId, ...rest } = col; + return rest; + }); + + dispatch(setColumnList({ listType: columnListType, entries: columns })); + settings.setSync(settingsConfig.columnList, cleanColumns); + }} + labelKey="label" + valueKey="label" + /> - settings.setSync(settingsConfig.columnList, columns); - }} - labelKey="label" - valueKey="label" - /> -
+ {}} + handleDragEnd={() => handleDragEnd(columnListType)} + columns={columnSelectorColumns} + rowHeight={35} + fontSize={12} + listType="column" + cacheImages={{ enabled: false }} + playQueue={playQueue} + multiSelect={multiSelect} + isModal={false} + miniView={false} + dnd + disableContextMenu + config={{ option: columnListType, columnList }} + virtualized + /> + +
+ +
+
Row height { - settings.setSync(settingsConfig.rowHeight, e); + onChange={(e: number) => { + settings.setSync(settingsConfig.rowHeight, Number(e)); }} />
-
+
+
Font size { - settings.setSync(settingsConfig.fontSize, e); + onChange={(e: number) => { + settings.setSync(settingsConfig.fontSize, Number(e)); }} />
diff --git a/src/components/settings/ConfigPanels/LookAndFeelConfig.tsx b/src/components/settings/ConfigPanels/LookAndFeelConfig.tsx index 6013d43..193c2b5 100644 --- a/src/components/settings/ConfigPanels/LookAndFeelConfig.tsx +++ b/src/components/settings/ConfigPanels/LookAndFeelConfig.tsx @@ -56,23 +56,60 @@ const LookAndFeelConfig = () => { const currentGenreColumns = genreCols?.map((column: any) => column.label) || []; return ( - -
-

Select the main application theme.

- { - settings.setSync('theme', e); - dispatch(setTheme(e)); - }} - > - Default Dark - Default Light - + <> + +
+

Select the main application theme.

+ { + settings.setSync('theme', e); + dispatch(setTheme(e)); + }} + > + Default Dark + Default Light + +
+ Font +
+ { + settings.setSync('font', e); + dispatch(setFont(e)); + }} + /> +
+
+
+ Titlebar style (requires app restart) +
+ { + settings.setSync('titleBarStyle', e); + dispatch(setMiscSetting({ setting: 'titleBar', value: e })); + }} + /> +

- { > Enable dynamic background -
- Font - -
- { - settings.setSync('font', e); - dispatch(setFont(e)); - }} - /> -
-
-
- Titlebar style (requires app restart) - -
- { - settings.setSync('titleBarStyle', e); - dispatch(setMiscSetting({ setting: 'titleBar', value: e })); - }} - /> -
-
+
- { - settings.setSync('highlightOnRowHover', e); - dispatch( - setMiscSetting({ - setting: 'highlightOnRowHover', - value: e, - }) - ); - setHighlightOnRowHoverChk(e); - }} - > - Show highlight on row hover - -
-

Select the columns you want displayed on pages with a list-view.

- { - setResizableColumn(e); - }} - > - Use resizable columns (check/uncheck this before selecting columns) - -