Browse Source

Add refs for picker components

master
jeffvli 3 years ago
committed by Jeff
parent
commit
31bba837ca
  1. 52
      src/components/library/AlbumList.tsx
  2. 65
      src/components/library/FolderList.tsx
  3. 91
      src/components/settings/ConfigPanels/ListViewConfig.tsx
  4. 18
      src/components/settings/ConfigPanels/LookAndFeelConfig.tsx
  5. 16
      src/components/settings/ConfigPanels/PlaybackConfig.tsx
  6. 29
      src/components/settings/ConfigPanels/ServerConfig.tsx
  7. 1
      src/components/shared/ContextMenu.tsx
  8. 4
      src/components/shared/styled.ts
  9. 42
      src/components/viewtypes/ViewTypeButtons.tsx
  10. 6
      src/styles/App.global.css
  11. 1
      src/styles/custom-theme.less

52
src/components/library/AlbumList.tsx

@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useRef, useState } from 'react';
import _ from 'lodash'; import _ from 'lodash';
import settings from 'electron-settings'; import settings from 'electron-settings';
import { ButtonToolbar } from 'rsuite'; import { ButtonToolbar } from 'rsuite';
@ -18,7 +18,7 @@ import {
toggleRangeSelected, toggleRangeSelected,
clearSelected, clearSelected,
} from '../../redux/multiSelectSlice'; } from '../../redux/multiSelectSlice';
import { StyledInputPicker } from '../shared/styled'; import { StyledInputPicker, StyledInputPickerContainer } from '../shared/styled';
import { RefreshButton } from '../shared/ToolbarButtons'; import { RefreshButton } from '../shared/ToolbarButtons';
import { setActive } from '../../redux/albumSlice'; import { setActive } from '../../redux/albumSlice';
@ -42,6 +42,7 @@ const AlbumList = () => {
const [sortTypes, setSortTypes] = useState<any[]>(); const [sortTypes, setSortTypes] = useState<any[]>();
const [viewType, setViewType] = useState(settings.getSync('albumViewType')); const [viewType, setViewType] = useState(settings.getSync('albumViewType'));
const [musicFolder, setMusicFolder] = useState(undefined); const [musicFolder, setMusicFolder] = useState(undefined);
const albumFilterPickerContainerRef = useRef(null);
useEffect(() => { useEffect(() => {
if (folder.applied.albums) { if (folder.applied.albums) {
@ -150,23 +151,36 @@ const AlbumList = () => {
<GenericPageHeader <GenericPageHeader
title="Albums" title="Albums"
subtitle={ subtitle={
<ButtonToolbar> <StyledInputPickerContainer ref={albumFilterPickerContainerRef}>
<StyledInputPicker <ButtonToolbar>
size="sm" <StyledInputPicker
width={180} container={() => albumFilterPickerContainerRef.current}
defaultValue={album.active.filter} size="sm"
groupBy="role" width={180}
data={sortTypes} defaultValue={album.active.filter}
cleanable={false} groupBy="role"
placeholder="Sort Type" data={sortTypes}
onChange={async (value: string) => { cleanable={false}
await queryClient.cancelQueries(['albumList', album.active.filter, musicFolder]); placeholder="Sort Type"
setSearchQuery(''); onChange={async (value: string) => {
dispatch(setActive({ ...album.active, filter: value })); await queryClient.cancelQueries([
}} 'albumList',
/> album.active.filter,
<RefreshButton onClick={handleRefresh} size="sm" loading={isRefreshing} width={100} /> musicFolder,
</ButtonToolbar> ]);
setSearchQuery('');
dispatch(setActive({ ...album.active, filter: value }));
}}
/>
<RefreshButton
onClick={handleRefresh}
size="sm"
loading={isRefreshing}
width={100}
/>
</ButtonToolbar>
</StyledInputPickerContainer>
} }
subsidetitle={<></>} subsidetitle={<></>}
searchQuery={searchQuery} searchQuery={searchQuery}

65
src/components/library/FolderList.tsx

@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useRef, useState } from 'react';
import settings from 'electron-settings'; import settings from 'electron-settings';
import _ from 'lodash'; import _ from 'lodash';
import { useQuery, useQueryClient } from 'react-query'; import { useQuery, useQueryClient } from 'react-query';
@ -23,7 +23,7 @@ import {
} from '../../redux/multiSelectSlice'; } from '../../redux/multiSelectSlice';
import GenericPage from '../layout/GenericPage'; import GenericPage from '../layout/GenericPage';
import GenericPageHeader from '../layout/GenericPageHeader'; import GenericPageHeader from '../layout/GenericPageHeader';
import { StyledButton, StyledInputPicker } from '../shared/styled'; import { StyledButton, StyledInputPicker, StyledInputPickerContainer } from '../shared/styled';
import { fixPlayer2Index, setPlayQueueByRowClick, setRate } from '../../redux/playQueueSlice'; import { fixPlayer2Index, setPlayQueueByRowClick, setRate } from '../../redux/playQueueSlice';
import { setStatus } from '../../redux/playerSlice'; import { setStatus } from '../../redux/playerSlice';
import useSearchQuery from '../../hooks/useSearchQuery'; import useSearchQuery from '../../hooks/useSearchQuery';
@ -37,6 +37,7 @@ const FolderList = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const folder = useAppSelector((state) => state.folder); const folder = useAppSelector((state) => state.folder);
const [musicFolder, setMusicFolder] = useState(folder.musicFolder); const [musicFolder, setMusicFolder] = useState(folder.musicFolder);
const folderPickerContainerRef = useRef(null);
const { isLoading, isError, data: indexData, error }: any = useQuery( const { isLoading, isError, data: indexData, error }: any = useQuery(
['indexes', musicFolder], ['indexes', musicFolder],
@ -163,33 +164,39 @@ const FolderList = () => {
showTitleTooltip showTitleTooltip
subtitle={ subtitle={
<> <>
<ButtonToolbar> <StyledInputPickerContainer ref={folderPickerContainerRef}>
<StyledInputPicker <ButtonToolbar>
data={musicFolders} <StyledInputPicker
defaultValue={musicFolder} container={() => folderPickerContainerRef.current}
valueKey="id" size="sm"
labelKey="name" width={180}
onChange={(e: any) => { data={musicFolders}
setMusicFolder(e); defaultValue={musicFolder}
}} valueKey="id"
style={{ width: '250px' }} labelKey="name"
/> onChange={(e: any) => {
setMusicFolder(e);
<StyledButton }}
size="sm" />
onClick={() => {
history.push( <StyledButton
`/library/folder?folderId=${folderData?.parent ? folderData.parent : ''}` size="sm"
); onClick={() => {
dispatch( history.push(
setCurrentViewedFolder(folderData?.parent ? folderData.parent : '') `/library/folder?folderId=${
); folderData?.parent ? folderData.parent : ''
}} }`
> );
<Icon icon="level-up" style={{ marginRight: '10px' }} /> dispatch(
Go up setCurrentViewedFolder(folderData?.parent ? folderData.parent : '')
</StyledButton> );
</ButtonToolbar> }}
>
<Icon icon="level-up" style={{ marginRight: '10px' }} />
Go up
</StyledButton>
</ButtonToolbar>
</StyledInputPickerContainer>
</> </>
} }
/> />

91
src/components/settings/ConfigPanels/ListViewConfig.tsx

@ -1,8 +1,13 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useRef, useState } from 'react';
import { nanoid } from 'nanoid/non-secure'; import { nanoid } from 'nanoid/non-secure';
import settings from 'electron-settings'; import settings from 'electron-settings';
import { ControlLabel } from 'rsuite'; import { ControlLabel } from 'rsuite';
import { StyledInputNumber, StyledPanel, StyledTagPicker } from '../../shared/styled'; import {
StyledInputNumber,
StyledInputPickerContainer,
StyledPanel,
StyledTagPicker,
} from '../../shared/styled';
import ListViewTable from '../../viewtypes/ListViewTable'; import ListViewTable from '../../viewtypes/ListViewTable';
import { useAppDispatch, useAppSelector } from '../../../redux/hooks'; import { useAppDispatch, useAppSelector } from '../../../redux/hooks';
import { import {
@ -53,6 +58,7 @@ const ListViewConfig = ({ defaultColumns, columnPicker, columnList, settingsConf
const config = useAppSelector((state) => state.config); const config = useAppSelector((state) => state.config);
const [selectedColumns, setSelectedColumns] = useState([]); const [selectedColumns, setSelectedColumns] = useState([]);
const columnListType = settingsConfig.columnList.split('List')[0]; const columnListType = settingsConfig.columnList.split('List')[0];
const columnPickerContainerRef = useRef(null);
useEffect(() => { useEffect(() => {
const cols = config.lookAndFeel.listView[columnListType].columns.map((col: any) => { const cols = config.lookAndFeel.listView[columnListType].columns.map((col: any) => {
@ -100,46 +106,49 @@ const ListViewConfig = ({ defaultColumns, columnPicker, columnList, settingsConf
<div style={{ width: '100%' }}> <div style={{ width: '100%' }}>
<div> <div>
<StyledPanel bordered bodyFill> <StyledPanel bordered bodyFill>
<StyledTagPicker <StyledInputPickerContainer ref={columnPickerContainerRef}>
data={columnPicker} <StyledTagPicker
defaultValue={defaultColumns} container={() => columnPickerContainerRef.current}
value={selectedColumns} data={columnPicker}
style={{ width: '100%' }} defaultValue={defaultColumns}
onChange={(e: any) => { value={selectedColumns}
const columns: any[] = []; style={{ width: '100%' }}
if (e) { onChange={(e: any) => {
e.forEach((selected: string) => { const columns: any[] = [];
const alreadySelectedColumn = config.lookAndFeel.listView[ if (e) {
columnListType e.forEach((selected: string) => {
].columns.find((column: any) => column.label === selected); const alreadySelectedColumn = config.lookAndFeel.listView[
columnListType
if (alreadySelectedColumn) { ].columns.find((column: any) => column.label === selected);
return columns.push(alreadySelectedColumn);
} if (alreadySelectedColumn) {
return columns.push(alreadySelectedColumn);
const selectedColumn = columnList.find( }
(column: any) => column.label === selected
); const selectedColumn = columnList.find(
(column: any) => column.label === selected
if (selectedColumn) { );
return columns.push({ ...selectedColumn.value, uniqueId: nanoid() });
} 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 }));
const cleanColumns = columns.map((col) => { settings.setSync(settingsConfig.columnList, cleanColumns);
const { uniqueId, ...rest } = col; }}
return rest; labelKey="label"
}); valueKey="label"
/>
dispatch(setColumnList({ listType: columnListType, entries: columns })); </StyledInputPickerContainer>
settings.setSync(settingsConfig.columnList, cleanColumns);
}}
labelKey="label"
valueKey="label"
/>
<ListViewTable <ListViewTable
data={config.lookAndFeel.listView[columnListType].columns || []} data={config.lookAndFeel.listView[columnListType].columns || []}

18
src/components/settings/ConfigPanels/LookAndFeelConfig.tsx

@ -1,4 +1,4 @@
import React, { useState } from 'react'; import React, { useRef, useState } from 'react';
import settings from 'electron-settings'; import settings from 'electron-settings';
import { RadioGroup, ControlLabel, Nav } from 'rsuite'; import { RadioGroup, ControlLabel, Nav } from 'rsuite';
import { ConfigPanel } from '../styled'; import { ConfigPanel } from '../styled';
@ -8,6 +8,7 @@ import {
StyledNavItem, StyledNavItem,
StyledInputNumber, StyledInputNumber,
StyledCheckbox, StyledCheckbox,
StyledInputPickerContainer,
} from '../../shared/styled'; } from '../../shared/styled';
import ListViewConfig from './ListViewConfig'; import ListViewConfig from './ListViewConfig';
import { Fonts } from '../Fonts'; import { Fonts } from '../Fonts';
@ -36,6 +37,8 @@ const LookAndFeelConfig = () => {
const [highlightOnRowHoverChk, setHighlightOnRowHoverChk] = useState( const [highlightOnRowHoverChk, setHighlightOnRowHoverChk] = useState(
Boolean(settings.getSync('highlightOnRowHover')) Boolean(settings.getSync('highlightOnRowHover'))
); );
const fontPickerContainerRef = useRef(null);
const titleBarPickerContainerRef = useRef(null);
const songCols: any = settings.getSync('musicListColumns'); const songCols: any = settings.getSync('musicListColumns');
const albumCols: any = settings.getSync('albumListColumns'); const albumCols: any = settings.getSync('albumListColumns');
@ -67,10 +70,14 @@ const LookAndFeelConfig = () => {
<StyledRadio value="defaultDark">Default Dark</StyledRadio> <StyledRadio value="defaultDark">Default Dark</StyledRadio>
<StyledRadio value="defaultLight">Default Light</StyledRadio> <StyledRadio value="defaultLight">Default Light</StyledRadio>
</RadioGroup> </RadioGroup>
<br /> </div>
<br />
<StyledInputPickerContainer ref={fontPickerContainerRef}>
<ControlLabel>Font</ControlLabel> <ControlLabel>Font</ControlLabel>
<br /> <br />
<StyledInputPicker <StyledInputPicker
container={() => fontPickerContainerRef.current}
data={Fonts} data={Fonts}
groupBy="role" groupBy="role"
cleanable={false} cleanable={false}
@ -80,12 +87,13 @@ const LookAndFeelConfig = () => {
dispatch(setFont(e)); dispatch(setFont(e));
}} }}
/> />
</div> </StyledInputPickerContainer>
<br /> <br />
<div> <StyledInputPickerContainer ref={titleBarPickerContainerRef}>
<ControlLabel>Titlebar style (requires app restart)</ControlLabel> <ControlLabel>Titlebar style (requires app restart)</ControlLabel>
<br /> <br />
<StyledInputPicker <StyledInputPicker
container={() => titleBarPickerContainerRef.current}
data={[ data={[
{ {
label: 'macOS', label: 'macOS',
@ -103,7 +111,7 @@ const LookAndFeelConfig = () => {
dispatch(setMiscSetting({ setting: 'titleBar', value: e })); dispatch(setMiscSetting({ setting: 'titleBar', value: e }));
}} }}
/> />
</div> </StyledInputPickerContainer>
<br /> <br />
<StyledCheckbox <StyledCheckbox
defaultChecked={dynamicBackgroundChk} defaultChecked={dynamicBackgroundChk}

16
src/components/settings/ConfigPanels/PlaybackConfig.tsx

@ -1,14 +1,21 @@
import React from 'react'; import React, { useRef } from 'react';
import settings from 'electron-settings'; import settings from 'electron-settings';
import { ControlLabel, RadioGroup } from 'rsuite'; import { ControlLabel, RadioGroup } from 'rsuite';
import { ConfigPanel } from '../styled'; import { ConfigPanel } from '../styled';
import { StyledInputNumber, StyledInputPicker, StyledRadio } from '../../shared/styled'; import {
StyledInputNumber,
StyledInputPicker,
StyledInputPickerContainer,
StyledRadio,
} from '../../shared/styled';
import { useAppDispatch, useAppSelector } from '../../../redux/hooks'; import { useAppDispatch, useAppSelector } from '../../../redux/hooks';
import { setPlaybackSetting, setPlayerVolume } from '../../../redux/playQueueSlice'; import { setPlaybackSetting, setPlayerVolume } from '../../../redux/playQueueSlice';
const PlaybackConfig = () => { const PlaybackConfig = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const playQueue = useAppSelector((state) => state.playQueue); const playQueue = useAppSelector((state) => state.playQueue);
const crossfadePickerContainerRef = useRef(null);
return ( return (
<ConfigPanel header="Playback" bordered> <ConfigPanel header="Playback" bordered>
<p> <p>
@ -77,10 +84,11 @@ const PlaybackConfig = () => {
}} }}
/> />
<br /> <br />
<div> <StyledInputPickerContainer ref={crossfadePickerContainerRef}>
<ControlLabel>Crossfade type</ControlLabel> <ControlLabel>Crossfade type</ControlLabel>
<br /> <br />
<StyledInputPicker <StyledInputPicker
container={() => crossfadePickerContainerRef.current}
data={[ data={[
{ {
label: 'Equal Power', label: 'Equal Power',
@ -118,7 +126,7 @@ const PlaybackConfig = () => {
dispatch(setPlaybackSetting({ setting: 'fadeType', value: e })); dispatch(setPlaybackSetting({ setting: 'fadeType', value: e }));
}} }}
/> />
</div> </StyledInputPickerContainer>
<br /> <br />
<ControlLabel>Volume fade</ControlLabel> <ControlLabel>Volume fade</ControlLabel>

29
src/components/settings/ConfigPanels/ServerConfig.tsx

@ -1,9 +1,9 @@
import React from 'react'; import React, { useRef } from 'react';
import settings from 'electron-settings'; import settings from 'electron-settings';
import { useQuery } from 'react-query'; import { useQuery } from 'react-query';
import { CheckboxGroup } from 'rsuite'; import { CheckboxGroup } from 'rsuite';
import { ConfigPanel } from '../styled'; import { ConfigPanel } from '../styled';
import { StyledCheckbox, StyledInputPicker } from '../../shared/styled'; import { StyledCheckbox, StyledInputPicker, StyledInputPickerContainer } from '../../shared/styled';
import { useAppDispatch, useAppSelector } from '../../../redux/hooks'; import { useAppDispatch, useAppSelector } from '../../../redux/hooks';
import { getMusicFolders } from '../../../api/api'; import { getMusicFolders } from '../../../api/api';
import { setAppliedFolderViews, setMusicFolder } from '../../../redux/folderSlice'; import { setAppliedFolderViews, setMusicFolder } from '../../../redux/folderSlice';
@ -12,21 +12,26 @@ const ServerConfig = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const folder = useAppSelector((state) => state.folder); const folder = useAppSelector((state) => state.folder);
const { isLoading, data: musicFolders } = useQuery(['musicFolders'], getMusicFolders); const { isLoading, data: musicFolders } = useQuery(['musicFolders'], getMusicFolders);
const musicFolderPickerContainerRef = useRef(null);
return ( return (
<ConfigPanel header="Server" bordered> <ConfigPanel header="Server" bordered>
<p>Select a music folder (leaving this blank will use all folders).</p> <p>Select a music folder (leaving this blank will use all folders).</p>
<br /> <br />
<StyledInputPicker <StyledInputPickerContainer ref={musicFolderPickerContainerRef}>
data={isLoading ? [] : musicFolders} <StyledInputPicker
defaultValue={folder.musicFolder} container={() => musicFolderPickerContainerRef.current}
valueKey="id" data={isLoading ? [] : musicFolders}
labelKey="name" defaultValue={folder.musicFolder}
onChange={(e: any) => { valueKey="id"
settings.setSync('musicFolder.id', e); labelKey="name"
dispatch(setMusicFolder(e)); onChange={(e: any) => {
}} settings.setSync('musicFolder.id', e);
/> dispatch(setMusicFolder(e));
}}
/>
</StyledInputPickerContainer>
<div> <div>
<br /> <br />
<p>Select which pages to apply music folder filtering to:</p> <p>Select which pages to apply music folder filtering to:</p>

1
src/components/shared/ContextMenu.tsx

@ -109,6 +109,7 @@ export const GlobalContextMenu = () => {
const [shouldCreatePlaylist, setShouldCreatePlaylist] = useState(false); const [shouldCreatePlaylist, setShouldCreatePlaylist] = useState(false);
const [newPlaylistName, setNewPlaylistName] = useState(''); const [newPlaylistName, setNewPlaylistName] = useState('');
const [indexToMoveTo, setIndexToMoveTo] = useState(0); const [indexToMoveTo, setIndexToMoveTo] = useState(0);
const playlistPickerContainerRef = useRef(null);
const { data: playlists }: any = useQuery(['playlists', 'name'], () => getPlaylists('name')); const { data: playlists }: any = useQuery(['playlists', 'name'], () => getPlaylists('name'));

4
src/components/shared/styled.ts

@ -337,10 +337,6 @@ export const StyledInputPicker = styled(InputPicker)<{ width?: number }>`
border-radius: ${(props) => props.theme.other.input.borderRadius}; border-radius: ${(props) => props.theme.other.input.borderRadius};
} }
.rs-picker-select-menu-item {
background: ${(props) => `${props.theme.colors.background.input} !important`};
}
.rs-btn-default { .rs-btn-default {
background: ${(props) => `${props.theme.colors.input.background} !important`}; background: ${(props) => `${props.theme.colors.input.background} !important`};
} }

42
src/components/viewtypes/ViewTypeButtons.tsx

@ -1,31 +1,31 @@
import React from 'react'; import React from 'react';
import { ButtonToolbar, ButtonGroup, Icon } from 'rsuite'; import { ButtonToolbar, Icon } from 'rsuite';
import settings from 'electron-settings'; import settings from 'electron-settings';
import { StyledIconButton } from '../shared/styled'; import { StyledIconButton } from '../shared/styled';
const ViewTypeButtons = ({ handleListClick, handleGridClick, viewTypeSetting }: any) => { const ViewTypeButtons = ({ handleListClick, handleGridClick, viewTypeSetting }: any) => {
return ( return (
<ButtonToolbar> <ButtonToolbar>
<ButtonGroup> <StyledIconButton
<StyledIconButton icon={<Icon icon="list" />}
icon={<Icon icon="list" />} size="sm"
appearance="subtle" appearance="subtle"
onClick={async () => { onClick={async () => {
handleListClick(); handleListClick();
localStorage.setItem(`${viewTypeSetting}ViewType`, 'list'); localStorage.setItem(`${viewTypeSetting}ViewType`, 'list');
settings.setSync(`${viewTypeSetting}ViewType`, 'list'); settings.setSync(`${viewTypeSetting}ViewType`, 'list');
}} }}
/> />
<StyledIconButton <StyledIconButton
icon={<Icon icon="th-large" />} icon={<Icon icon="th-large" />}
appearance="subtle" size="sm"
onClick={async () => { appearance="subtle"
handleGridClick(); onClick={async () => {
localStorage.setItem(`${viewTypeSetting}ViewType`, 'grid'); handleGridClick();
settings.setSync(`${viewTypeSetting}ViewType`, 'grid'); localStorage.setItem(`${viewTypeSetting}ViewType`, 'grid');
}} settings.setSync(`${viewTypeSetting}ViewType`, 'grid');
/> }}
</ButtonGroup> />
</ButtonToolbar> </ButtonToolbar>
); );
}; };

6
src/styles/App.global.css

@ -57,6 +57,12 @@ body {
} }
*/ */
/* Fixes the picker menu position */
.rs-picker-menu {
left: inherit !important;
top: inherit !important;
}
*::-webkit-scrollbar { *::-webkit-scrollbar {
width: 10px; width: 10px;
} }

1
src/styles/custom-theme.less

@ -45,6 +45,7 @@
@table-head-font-color: undefined; @table-head-font-color: undefined;
@table-resize-mouse-color: #292d33; @table-resize-mouse-color: #292d33;
@table-sort-icon-unsort: ''; @table-sort-icon-unsort: '';
@zindex-table-header-row-wrapper: 0;
// Sidenav // Sidenav
@sidenav-default-bg: undefined; @sidenav-default-bg: undefined;

Loading…
Cancel
Save