Browse Source

Update theme template

master
jeffvli 3 years ago
committed by Jeff
parent
commit
cf91cc283a
  1. 33
      src/components/card/styled.tsx
  2. 15
      src/components/layout/GenericPageHeader.tsx
  3. 42
      src/components/layout/Sidebar.tsx
  4. 28
      src/components/layout/styled.tsx
  5. 14
      src/components/library/ArtistView.tsx
  6. 5
      src/components/loader/PageLoader.tsx
  7. 4
      src/components/modal/PageModal.tsx
  8. 81
      src/components/player/NowPlayingView.tsx
  9. 7
      src/components/player/PlayerBar.tsx
  10. 51
      src/components/player/styled.tsx
  11. 8
      src/components/playlist/PlaylistList.tsx
  12. 12
      src/components/playlist/PlaylistView.tsx
  13. 41
      src/components/scrollingmenu/ScrollingMenu.tsx
  14. 20
      src/components/settings/Config.tsx
  15. 63
      src/components/settings/ConfigPanels/CacheConfig.tsx
  16. 8
      src/components/settings/ConfigPanels/ListViewConfig.tsx
  17. 6
      src/components/settings/DisconnectButton.tsx
  18. 10
      src/components/settings/styled.tsx
  19. 150
      src/components/shared/ContextMenu.tsx
  20. 7
      src/components/shared/CustomTooltip.tsx
  21. 8
      src/components/shared/ToolbarButtons.tsx
  22. 289
      src/components/shared/setDefaultSettings.ts
  23. 342
      src/components/shared/styled.ts
  24. 4
      src/components/viewtypes/ListViewTable.tsx
  25. 18
      src/components/viewtypes/styled.tsx
  26. 6
      src/styles/custom-theme.less
  27. 323
      src/styles/styledTheme.ts

33
src/components/card/styled.tsx

@ -19,12 +19,12 @@ interface Card {
}; */ }; */
export const CardPanel = styled(Panel)<Card>` export const CardPanel = styled(Panel)<Card>`
border-radius: ${(props) => props.theme.other.card.borderRadius};
text-align: center; text-align: center;
width: ${(props) => `${Number(props.cardsize) + 2}px`}; width: ${(props) => `${Number(props.cardsize) + 2}px`};
height: ${(props) => `${Number(props.cardsize) + 55}px`}; height: ${(props) => `${Number(props.cardsize) + 55}px`};
border-radius: 0px !important;
&:hover { &:hover {
border: 1px solid ${(props) => props.theme.primary.main}; border: 1px solid ${(props) => props.theme.colors.primary};
img { img {
filter: brightness(70%); filter: brightness(70%);
-webkit-filter: brightness(70%); -webkit-filter: brightness(70%);
@ -41,7 +41,7 @@ export const InfoPanel = styled(Panel)<Card>`
`; `;
export const InfoSpan = styled.div` export const InfoSpan = styled.div`
color: ${(props) => props.theme.secondary.text}; color: ${(props) => props.theme.colors.layout.page.colorSecondary};
`; `;
export const CardButton = styled(Button)` export const CardButton = styled(Button)`
@ -53,14 +53,26 @@ export const CardButton = styled(Button)`
export const CardTitleButton = styled(CardButton)` export const CardTitleButton = styled(CardButton)`
padding-top: 5px; padding-top: 5px;
padding-bottom: 2px; padding-bottom: 2px;
color: ${(props) => props.theme.primary.text}; color: ${(props) => props.theme.colors.layout.page.color};
width: ${(props) => `${props.cardsize}px`}; width: ${(props) => `${props.cardsize}px`};
&:hover {
text-decoration: none;
color: ${(props) =>
!props.onClick ? props.theme.colors.layout.page.color : props.theme.colors.primary};
}
`; `;
export const CardSubtitleButton = styled(CardButton)` export const CardSubtitleButton = styled(CardButton)`
padding-bottom: 5px; padding-bottom: 5px;
color: ${(props) => props.theme.secondary.text}; color: ${(props) => props.theme.colors.layout.page.colorSecondary};
width: ${(props) => `${props.cardsize}px`}; width: ${(props) => `${props.cardsize}px`};
&:hover {
text-decoration: none;
color: ${(props) =>
!props.onClick ? props.theme.colors.layout.page.color : props.theme.colors.primary};
}
`; `;
export const CardSubtitle = styled.div<Card>` export const CardSubtitle = styled.div<Card>`
@ -83,7 +95,6 @@ export const LazyCardImg = styled(LazyLoadImage)<Card>`
`; `;
export const Overlay = styled.div<Card>` export const Overlay = styled.div<Card>`
background-color: #1a1d24;
position: relative; position: relative;
height: ${(props) => `${props.cardsize}px`}; height: ${(props) => `${props.cardsize}px`};
width: ${(props) => `${props.cardsize}px`}; width: ${(props) => `${props.cardsize}px`};
@ -93,7 +104,7 @@ export const Overlay = styled.div<Card>`
.corner-triangle { .corner-triangle {
position: absolute; position: absolute;
background-color: ${(props) => props.theme.primary.main}; background-color: ${(props) => props.theme.colors.primary};
box-shadow: 0 0 10px 8px rgba(0, 0, 0, 0.8); box-shadow: 0 0 10px 8px rgba(0, 0, 0, 0.8);
height: 80px; height: 80px;
left: -50px; left: -50px;
@ -108,16 +119,18 @@ export const Overlay = styled.div<Card>`
const OverlayButton = styled(IconButton)` const OverlayButton = styled(IconButton)`
display: none; display: none;
position: absolute !important; position: absolute !important;
opacity: 0.8; opacity: ${(props) => props.theme.colors.card.overlayButton.opacity};
width: 0px; width: 0px;
height: 0px; height: 0px;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%); -ms-transform: translate(-50%, -50%);
background: #000; background: ${(props) => props.theme.colors.card.overlayButton.background};
color: ${(props) => props.theme.colors.card.overlayButton.color};
&:hover { &:hover {
opacity: 1; opacity: 1;
background: ${(props) => props.theme.primary.main}; background: ${(props) => props.theme.colors.card.overlayButton.backgroundHover};
color: ${(props) => props.theme.colors.card.overlayButton.colorHover};
} }
`; `;

15
src/components/layout/GenericPageHeader.tsx

@ -2,9 +2,14 @@ import React, { useState } from 'react';
import { LazyLoadImage } from 'react-lazy-load-image-component'; import { LazyLoadImage } from 'react-lazy-load-image-component';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import { useHotkeys } from 'react-hotkeys-hook'; import { useHotkeys } from 'react-hotkeys-hook';
import { Icon, Input, InputGroup } from 'rsuite'; import { Icon, InputGroup } from 'rsuite';
import ViewTypeButtons from '../viewtypes/ViewTypeButtons'; import ViewTypeButtons from '../viewtypes/ViewTypeButtons';
import { StyledIconButton, StyledInputGroup } from '../shared/styled'; import {
StyledIconButton,
StyledInput,
StyledInputGroup,
StyledInputGroupButton,
} from '../shared/styled';
import { CoverArtWrapper, PageHeaderTitle } from './styled'; import { CoverArtWrapper, PageHeaderTitle } from './styled';
import cacheImage from '../shared/cacheImage'; import cacheImage from '../shared/cacheImage';
import CustomTooltip from '../shared/CustomTooltip'; import CustomTooltip from '../shared/CustomTooltip';
@ -98,7 +103,7 @@ const GenericPageHeader = ({
<InputGroup.Addon> <InputGroup.Addon>
<Icon icon="search" /> <Icon icon="search" />
</InputGroup.Addon> </InputGroup.Addon>
<Input <StyledInput
id="local-search-input" id="local-search-input"
value={searchQuery} value={searchQuery}
onChange={handleSearch} onChange={handleSearch}
@ -115,7 +120,7 @@ const GenericPageHeader = ({
}} }}
style={{ width: '180px' }} style={{ width: '180px' }}
/> />
<InputGroup.Button <StyledInputGroupButton
appearance="subtle" appearance="subtle"
onClick={() => { onClick={() => {
clearSearchQuery(); clearSearchQuery();
@ -123,7 +128,7 @@ const GenericPageHeader = ({
}} }}
> >
<Icon icon="close" /> <Icon icon="close" />
</InputGroup.Button> </StyledInputGroupButton>
</StyledInputGroup> </StyledInputGroup>
) : ( ) : (
<StyledIconButton <StyledIconButton

42
src/components/layout/Sidebar.tsx

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import { Sidenav, Nav, Icon } from 'rsuite'; import { Sidenav, Nav, Icon } from 'rsuite';
import { FixedSidebar } from './styled'; import { FixedSidebar, SidebarNavItem } from './styled';
const Sidebar = ({ const Sidebar = ({
expand, expand,
@ -28,87 +28,87 @@ const Sidebar = ({
<Sidenav.Header /> <Sidenav.Header />
<Sidenav.Body> <Sidenav.Body>
<Nav> <Nav>
<Nav.Item <SidebarNavItem
eventKey="discover" eventKey="discover"
icon={<Icon icon="dashboard" />} icon={<Icon icon="dashboard" />}
onSelect={handleSidebarSelect} onSelect={handleSidebarSelect}
disabled={disableSidebar} disabled={disableSidebar}
> >
Dashboard Dashboard
</Nav.Item> </SidebarNavItem>
<Nav.Item <SidebarNavItem
eventKey="nowplaying" eventKey="nowplaying"
icon={<Icon icon="music" />} icon={<Icon icon="music" />}
onSelect={handleSidebarSelect} onSelect={handleSidebarSelect}
disabled={disableSidebar} disabled={disableSidebar}
> >
Now Playing Now Playing
</Nav.Item> </SidebarNavItem>
<Nav.Item <SidebarNavItem
eventKey="playlists" eventKey="playlists"
icon={<Icon icon="list-ul" />} icon={<Icon icon="list-ul" />}
onSelect={handleSidebarSelect} onSelect={handleSidebarSelect}
disabled={disableSidebar} disabled={disableSidebar}
> >
Playlists Playlists
</Nav.Item> </SidebarNavItem>
<Nav.Item <SidebarNavItem
eventKey="starred" eventKey="starred"
icon={<Icon icon="heart" />} icon={<Icon icon="heart" />}
onSelect={handleSidebarSelect} onSelect={handleSidebarSelect}
disabled={disableSidebar} disabled={disableSidebar}
> >
Favorites Favorites
</Nav.Item> </SidebarNavItem>
<Nav.Item <SidebarNavItem
eventKey="albums" eventKey="albums"
icon={<Icon icon="book2" />} icon={<Icon icon="book2" />}
onSelect={handleSidebarSelect} onSelect={handleSidebarSelect}
disabled={disableSidebar} disabled={disableSidebar}
> >
Albums Albums
</Nav.Item> </SidebarNavItem>
<Nav.Item <SidebarNavItem
eventKey="artists" eventKey="artists"
icon={<Icon icon="people-group" />} icon={<Icon icon="people-group" />}
onSelect={handleSidebarSelect} onSelect={handleSidebarSelect}
disabled={disableSidebar} disabled={disableSidebar}
> >
Artists Artists
</Nav.Item> </SidebarNavItem>
<Nav.Item <SidebarNavItem
eventKey="genres" eventKey="genres"
icon={<Icon icon="globe2" />} icon={<Icon icon="globe2" />}
onSelect={handleSidebarSelect} onSelect={handleSidebarSelect}
disabled={disableSidebar} disabled={disableSidebar}
> >
Genres Genres
</Nav.Item> </SidebarNavItem>
<Nav.Item <SidebarNavItem
eventKey="folders" eventKey="folders"
icon={<Icon icon="folder-open" />} icon={<Icon icon="folder-open" />}
onSelect={handleSidebarSelect} onSelect={handleSidebarSelect}
disabled={disableSidebar} disabled={disableSidebar}
> >
Folders Folders
</Nav.Item> </SidebarNavItem>
</Nav> </Nav>
<Nav> <Nav>
<Nav.Item <SidebarNavItem
eventKey="config" eventKey="config"
icon={<Icon icon="gear-circle" />} icon={<Icon icon="gear-circle" />}
onSelect={handleSidebarSelect} onSelect={handleSidebarSelect}
disabled={disableSidebar} disabled={disableSidebar}
> >
Config Config
</Nav.Item> </SidebarNavItem>
<Nav.Item <SidebarNavItem
icon={<Icon icon={expand ? 'arrow-left' : 'arrow-right'} />} icon={<Icon icon={expand ? 'arrow-left' : 'arrow-right'} />}
onSelect={handleToggle} onSelect={handleToggle}
disabled={disableSidebar} disabled={disableSidebar}
> >
{expand ? 'Collapse' : 'Expand'} {expand ? 'Collapse' : 'Expand'}
</Nav.Item> </SidebarNavItem>
</Nav> </Nav>
</Sidenav.Body> </Sidenav.Body>
</Sidenav> </Sidenav>

28
src/components/layout/styled.tsx

@ -1,13 +1,13 @@
import React from 'react'; import React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import { Container, Content, Footer, Header, Sidebar } from 'rsuite'; import { Container, Content, Footer, Header, Nav, Sidebar } from 'rsuite';
// Layout.tsx // Layout.tsx
export const RootContainer = styled(Container)<{ font: string }>` export const RootContainer = styled(Container)<{ font: string }>`
background: ${(props) => props.theme.primary.background}; background: ${(props) => props.theme.colors.layout.page.background};
height: 100vh; height: 100vh;
color: ${(props) => props.theme.primary.text}; color: ${(props) => props.theme.colors.layout.page.color};
font-size: ${(props) => props.theme.all.fonts.pageFontSize}; font-size: ${(props) => `${props.theme.fonts.size.page} !important`};
font-family: ${(props) => `${props.font?.split(/Light|Medium/)[0]}`}; font-family: ${(props) => `${props.font?.split(/Light|Medium/)[0]}`};
font-weight: ${(props) => font-weight: ${(props) =>
props.font?.match('Light') ? 300 : props.font?.match('Medium') ? 500 : 400}; props.font?.match('Light') ? 300 : props.font?.match('Medium') ? 500 : 400};
@ -42,9 +42,9 @@ export const TitleHeader = styled.header<{ font: string }>`
position: fixed; position: fixed;
height: 32px; height: 32px;
width: ${(props) => (props.className?.includes('maximized') ? '100%' : 'calc(100%)')}; width: ${(props) => (props.className?.includes('maximized') ? '100%' : 'calc(100%)')};
background: ${(props) => props.theme.primary.titleBar}; background: ${(props) => props.theme.colors.layout.titleBar.background};
padding: 4px; padding: 4px;
color: ${(props) => props.theme.primary.titleText}; color: ${(props) => props.theme.colors.layout.titleBar.color};
font-family: ${(props) => `${props.font?.split(/Light|Medium/)[0]}`}; font-family: ${(props) => `${props.font?.split(/Light|Medium/)[0]}`};
font-weight: ${(props) => font-weight: ${(props) =>
props.font?.match('Light') ? 300 : props.font?.match('Medium') ? 500 : 400}; props.font?.match('Light') ? 300 : props.font?.match('Medium') ? 500 : 400};
@ -161,7 +161,7 @@ export const PageContent = styled(Content)<{ padding?: string }>`
// Sidebar.tsx // Sidebar.tsx
// Add 1 to top if you add window border // Add 1 to top if you add window border
export const FixedSidebar = styled(Sidebar)<{ font: string }>` export const FixedSidebar = styled(Sidebar)<{ font: string }>`
background: ${(props) => props.theme.primary.sideBar} !important; background: ${(props) => props.theme.colors.layout.sideBar.background} !important;
position: fixed; position: fixed;
top: 32px; top: 32px;
z-index: 1; z-index: 1;
@ -177,14 +177,24 @@ export const FixedSidebar = styled(Sidebar)<{ font: string }>`
} }
`; `;
export const SidebarNavItem = styled(Nav.Item)`
a {
color: ${(props) => props.theme.colors.layout.sideBar.button.color} !important;
&:hover {
color: ${(props) => props.theme.colors.layout.sideBar.button.colorHover} !important;
}
}
`;
export const CoverArtWrapper = styled.div` export const CoverArtWrapper = styled.div`
display: inline-block; display: inline-block;
filter: ${(props) => `drop-shadow(0px 5px 8px ${props.theme.primary.coverArtShadow})`}; filter: ${(props) => props.theme.other.coverArtFilter};
`; `;
export const PageHeaderTitle = styled.h1` export const PageHeaderTitle = styled.h1`
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
font-size: ${(props) => props.theme.all.fonts.pageTitleFontSize}; font-size: ${(props) => props.theme.fonts.size.pageTitle};
`; `;

14
src/components/library/ArtistView.tsx

@ -3,7 +3,7 @@ import React, { useState } from 'react';
import _ from 'lodash'; import _ from 'lodash';
import { shell } from 'electron'; import { shell } from 'electron';
import settings from 'electron-settings'; import settings from 'electron-settings';
import { ButtonToolbar, Tag, Whisper, Button, Popover, TagGroup } from 'rsuite'; import { ButtonToolbar, Whisper, TagGroup } from 'rsuite';
import { useQuery, useQueryClient } from 'react-query'; import { useQuery, useQueryClient } from 'react-query';
import { useParams, useHistory } from 'react-router-dom'; import { useParams, useHistory } from 'react-router-dom';
import { import {
@ -32,7 +32,7 @@ import { addModalPage } from '../../redux/miscSlice';
import { appendPlayQueue, setPlayQueue } from '../../redux/playQueueSlice'; import { appendPlayQueue, setPlayQueue } from '../../redux/playQueueSlice';
import { notifyToast } from '../shared/toast'; import { notifyToast } from '../shared/toast';
import { isCached } from '../../shared/utils'; import { isCached } from '../../shared/utils';
import { StyledButton } from '../shared/styled'; import { StyledButton, StyledPopover, StyledTag } from '../shared/styled';
import { setStatus } from '../../redux/playerSlice'; import { setStatus } from '../../redux/playerSlice';
interface ArtistParams { interface ArtistParams {
@ -213,12 +213,12 @@ const ArtistView = ({ ...rest }: any) => {
trigger="hover" trigger="hover"
enterable enterable
speaker={ speaker={
<Popover style={{ width: '400px' }}> <StyledPopover style={{ width: '400px' }}>
<div> <div>
<h6>Related artists</h6> <h6>Related artists</h6>
<TagGroup> <TagGroup>
{artistInfo.similarArtist?.map((artist: any) => ( {artistInfo.similarArtist?.map((artist: any) => (
<Tag key={artist.id}> <StyledTag key={artist.id}>
<TagLink <TagLink
onClick={() => { onClick={() => {
if (!rest.isModal) { if (!rest.isModal) {
@ -235,7 +235,7 @@ const ArtistView = ({ ...rest }: any) => {
> >
{artist.name} {artist.name}
</TagLink> </TagLink>
</Tag> </StyledTag>
))} ))}
</TagGroup> </TagGroup>
</div> </div>
@ -247,10 +247,10 @@ const ArtistView = ({ ...rest }: any) => {
> >
View on Last.FM View on Last.FM
</StyledButton> </StyledButton>
</Popover> </StyledPopover>
} }
> >
<Button size="md">Info</Button> <StyledButton size="md">Info</StyledButton>
</Whisper> </Whisper>
</ButtonToolbar> </ButtonToolbar>
</div> </div>

5
src/components/loader/PageLoader.tsx

@ -10,10 +10,11 @@ const LoaderContainer = styled.div`
div { div {
span { span {
&:after { &:after {
border-color: ${(props) => `${props.theme.primary.main} transparent transparent`}; border-color: ${(props) =>
`${props.theme.colors.spinner.foreground} transparent transparent`};
} }
&:before { &:before {
border: ${(props) => `3px solid ${props.theme.primary.spinnerBackground}`}; border: ${(props) => `3px solid ${props.theme.colors.spinner.background}`};
} }
} }
} }

4
src/components/modal/PageModal.tsx

@ -9,14 +9,14 @@ import AlbumView from '../library/AlbumView';
import PlaylistView from '../playlist/PlaylistView'; import PlaylistView from '../playlist/PlaylistView';
const StyledModal = styled(Modal)` const StyledModal = styled(Modal)`
color: ${(props) => `${props.theme.primary.text} !important`}; color: ${(props) => `${props.theme.colors.layout.page.color} !important`};
.rs-modal-body { .rs-modal-body {
margin-top: 0px; margin-top: 0px;
} }
.rs-modal-content { .rs-modal-content {
background: ${(props) => `${props.theme.primary.background} !important`}; background: ${(props) => `${props.theme.colors.layout.page.background} !important`};
} }
.rs-container { .rs-container {

81
src/components/player/NowPlayingView.tsx

@ -37,9 +37,9 @@ import { AutoPlaylistButton, ClearQueueButton, ShuffleButton } from '../shared/T
import { import {
StyledButton, StyledButton,
StyledCheckbox, StyledCheckbox,
StyledInputGroup,
StyledInputNumber, StyledInputNumber,
StyledInputPicker, StyledInputPicker,
StyledInputPickerContainer,
StyledPopover, StyledPopover,
} from '../shared/styled'; } from '../shared/styled';
import { errorMessages, getCurrentEntryList, isFailedResponse } from '../../shared/utils'; import { errorMessages, getCurrentEntryList, isFailedResponse } from '../../shared/utils';
@ -48,6 +48,7 @@ import { notifyToast } from '../shared/toast';
const NowPlayingView = () => { const NowPlayingView = () => {
const tableRef = useRef<any>(); const tableRef = useRef<any>();
const pickerContainerRef = useRef(null);
const autoPlaylistTriggerRef = useRef<any>(); const autoPlaylistTriggerRef = useRef<any>();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const playQueue = useAppSelector((state) => state.playQueue); const playQueue = useAppSelector((state) => state.playQueue);
@ -268,71 +269,66 @@ const NowPlayingView = () => {
speaker={ speaker={
<StyledPopover> <StyledPopover>
<ControlLabel>How many tracks? (1-500)*</ControlLabel> <ControlLabel>How many tracks? (1-500)*</ControlLabel>
<StyledInputGroup> <StyledInputNumber
<StyledInputNumber min={1}
min={1} max={500}
max={500} step={10}
step={10} defaultValue={autoPlaylistTrackCount}
defaultValue={autoPlaylistTrackCount} value={autoPlaylistTrackCount}
value={autoPlaylistTrackCount} onChange={(e: number) => {
onChange={(e: number) => { settings.setSync('randomPlaylistTrackCount', Number(e));
settings.setSync('randomPlaylistTrackCount', Number(e)); setRandomPlaylistTrackCount(Number(e));
setRandomPlaylistTrackCount(Number(e)); }}
}} />
/>
</StyledInputGroup>
<br /> <br />
<FlexboxGrid justify="space-between"> <FlexboxGrid justify="space-between">
<FlexboxGrid.Item> <FlexboxGrid.Item>
<ControlLabel>From year</ControlLabel> <ControlLabel>From year</ControlLabel>
<div> <div>
<StyledInputGroup> <StyledInputNumber
<StyledInputNumber width={100}
width={100} min={0}
min={0} max={3000}
max={3000} step={1}
step={1} defaultValue={autoPlaylistFromYear}
defaultValue={autoPlaylistFromYear} value={autoPlaylistFromYear}
value={autoPlaylistFromYear} onChange={(e: number) => {
onChange={(e: number) => { setRandomPlaylistFromYear(Number(e));
setRandomPlaylistFromYear(Number(e)); }}
}} />
/>
</StyledInputGroup>
</div> </div>
</FlexboxGrid.Item> </FlexboxGrid.Item>
<FlexboxGrid.Item> <FlexboxGrid.Item>
<ControlLabel>To year</ControlLabel> <ControlLabel>To year</ControlLabel>
<div> <div>
<StyledInputGroup> <StyledInputNumber
<StyledInputNumber width={100}
width={100} min={0}
min={0} max={3000}
max={3000} step={1}
step={1} defaultValue={autoPlaylistToYear}
defaultValue={autoPlaylistToYear} value={autoPlaylistToYear}
value={autoPlaylistToYear} onChange={(e: number) => setRandomPlaylistToYear(Number(e))}
onChange={(e: number) => setRandomPlaylistToYear(Number(e))} />
/>
</StyledInputGroup>
</div> </div>
</FlexboxGrid.Item> </FlexboxGrid.Item>
</FlexboxGrid> </FlexboxGrid>
<br /> <br />
<ControlLabel>Genre</ControlLabel> <ControlLabel>Genre</ControlLabel>
<div> <StyledInputPickerContainer ref={pickerContainerRef}>
<StyledInputPicker <StyledInputPicker
style={{ width: '100%' }}
container={() => pickerContainerRef.current}
data={genres} data={genres}
value={randomPlaylistGenre} value={randomPlaylistGenre}
virtualized virtualized
onChange={(e: string) => setRandomPlaylistGenre(e)} onChange={(e: string) => setRandomPlaylistGenre(e)}
/> />
</div> </StyledInputPickerContainer>
<br /> <br />
<ButtonToolbar> <ButtonToolbar>
<StyledButton <StyledButton
appearance="subtle"
onClick={() => handlePlayRandom('addNext')} onClick={() => handlePlayRandom('addNext')}
loading={isLoadingRandom} loading={isLoadingRandom}
disabled={!(typeof autoPlaylistTrackCount === 'number')} disabled={!(typeof autoPlaylistTrackCount === 'number')}
@ -340,6 +336,7 @@ const NowPlayingView = () => {
<Icon icon="plus-circle" style={{ marginRight: '10px' }} /> Add (next) <Icon icon="plus-circle" style={{ marginRight: '10px' }} /> Add (next)
</StyledButton> </StyledButton>
<StyledButton <StyledButton
appearance="subtle"
onClick={() => handlePlayRandom('addLater')} onClick={() => handlePlayRandom('addLater')}
loading={isLoadingRandom} loading={isLoadingRandom}
disabled={!(typeof autoPlaylistTrackCount === 'number')} disabled={!(typeof autoPlaylistTrackCount === 'number')}

7
src/components/player/PlayerBar.tsx

@ -1,7 +1,7 @@
import React, { useEffect, useState, useRef } from 'react'; import React, { useEffect, useState, useRef } from 'react';
import { useQueryClient } from 'react-query'; import { useQueryClient } from 'react-query';
import settings from 'electron-settings'; import settings from 'electron-settings';
import { FlexboxGrid, Grid, Row, Col, Whisper, Popover } from 'rsuite'; import { FlexboxGrid, Grid, Row, Col, Whisper } from 'rsuite';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import { LazyLoadImage } from 'react-lazy-load-image-component'; import { LazyLoadImage } from 'react-lazy-load-image-component';
import format from 'format-duration'; import format from 'format-duration';
@ -34,6 +34,7 @@ import placeholderImg from '../../img/placeholder.jpg';
import DebugWindow from '../debug/DebugWindow'; import DebugWindow from '../debug/DebugWindow';
import { CoverArtWrapper } from '../layout/styled'; import { CoverArtWrapper } from '../layout/styled';
import { getCurrentEntryList, isCached } from '../../shared/utils'; import { getCurrentEntryList, isCached } from '../../shared/utils';
import { StyledPopover } from '../shared/styled';
const PlayerBar = () => { const PlayerBar = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
@ -321,7 +322,7 @@ const PlayerBar = () => {
placement="topStart" placement="topStart"
preventOverflow preventOverflow
speaker={ speaker={
<Popover> <StyledPopover>
<div style={{ height: '500px' }}> <div style={{ height: '500px' }}>
<LazyLoadImage <LazyLoadImage
src={ src={
@ -341,7 +342,7 @@ const PlayerBar = () => {
height={500} height={500}
/> />
</div> </div>
</Popover> </StyledPopover>
} }
> >
<LazyLoadImage <LazyLoadImage

51
src/components/player/styled.tsx

@ -2,9 +2,13 @@ import { Icon, Slider } from 'rsuite';
import styled from 'styled-components'; import styled from 'styled-components';
export const PlayerContainer = styled.div` export const PlayerContainer = styled.div`
background: ${(props) => props.theme.primary.playerBar}; background: ${(props) => props.theme.colors.layout.playerBar.background};
height: 100%; height: 100%;
border-top: 1px solid #48545c; border-top: ${(props) => props.theme.other.playerBar.borderTop};
border-right: ${(props) => props.theme.other.playerBar.borderRight};
border-bottom: ${(props) => props.theme.other.playerBar.borderBottom};
border-left: ${(props) => props.theme.other.playerBar.borderLeft};
filter: ${(props) => props.theme.other.playerBar.filter};
`; `;
export const PlayerColumn = styled.div<{ export const PlayerColumn = styled.div<{
@ -23,14 +27,16 @@ export const PlayerColumn = styled.div<{
export const PlayerControlIcon = styled(Icon)` export const PlayerControlIcon = styled(Icon)`
font-size: medium; font-size: medium;
color: ${(props) => color: ${(props) =>
props.active === 'true' ? props.theme.primary.main : props.theme.primary.playerBarButtons}; props.active === 'true'
? props.theme.colors.primary
: props.theme.colors.layout.playerBar.button.color};
padding-left: 10px; padding-left: 10px;
padding-right: 10px; padding-right: 10px;
&:hover { &:hover {
color: ${(props) => color: ${(props) =>
props.active === 'true' props.active === 'true'
? props.theme.primary.main ? props.theme.colors.primary
: props.theme.primary.playerBarButtonsHover}; : props.theme.colors.layout.playerBar.button.colorHover};
} }
cursor: pointer; cursor: pointer;
`; `;
@ -43,23 +49,23 @@ export const LinkButton = styled.a<{ subtitle?: string }>`
overflow: hidden; overflow: hidden;
color: ${(props) => color: ${(props) =>
props.subtitle === 'true' props.subtitle === 'true'
? props.theme.secondary.playerBarText ? props.theme.colors.layout.playerBar.colorSecondary
: props.theme.primary.playerBarText}; : props.theme.colors.layout.playerBar.color};
&:hover { &:hover {
text-decoration: underline; text-decoration: underline;
color: ${(props) => color: ${(props) =>
props.subtitle === 'true' props.subtitle === 'true'
? props.theme.secondary.playerBarText ? props.theme.colors.layout.playerBar.colorSecondary
: props.theme.primary.playerBarText}; : props.theme.colors.layout.playerBar.color};
cursor: pointer; cursor: pointer;
} }
&:active { &:active {
color: ${(props) => color: ${(props) =>
props.subtitle === 'true' props.subtitle === 'true'
? props.theme.secondary.playerBarText ? props.theme.colors.layout.playerBar.colorSecondary
: props.theme.primary.playerBarText}; : props.theme.colors.layout.playerBar.color};
} }
`; `;
@ -69,26 +75,31 @@ export const CustomSlider = styled(Slider)<{ isDragging?: boolean }>`
display: block; display: block;
} }
.rs-slider-progress-bar { .rs-slider-progress-bar {
background-color: ${(props) => props.theme.primary.main}; background-color: ${(props) => props.theme.colors.primary};
} }
} }
.rs-slider-bar {
background-color: ${(props) => props.theme.colors.slider.background};
}
.rs-slider-progress-bar { .rs-slider-progress-bar {
background-color: ${(props) => background-color: ${(props) =>
props.$isDragging ? props.theme.primary.main : props.theme.primary.sliderBackground}; props.$isDragging ? props.theme.colors.primary : props.theme.colors.slider.progressBar};
} }
.rs-slider-handle::before { .rs-slider-handle::before {
display: none; display: none;
border: ${(props) => `1px solid ${props.theme.primary.main} !important`}; border: ${(props) => `1px solid ${props.theme.colors.primary} !important`};
} }
`; `;
export const DurationSpan = styled.span` export const DurationSpan = styled.span`
color: ${(props) => props.theme.primary.playerBarText}; color: ${(props) => props.theme.colors.layout.playerBar.color};
`; `;
export const VolumeIcon = styled(Icon)` export const VolumeIcon = styled(Icon)`
color: ${(props) => props.theme.primary.playerBarText}; color: ${(props) => props.theme.colors.layout.playerBar.color};
`; `;
export const MiniViewContainer = styled.div<{ display: string }>` export const MiniViewContainer = styled.div<{ display: string }>`
@ -99,12 +110,12 @@ export const MiniViewContainer = styled.div<{ display: string }>`
right: 25px; right: 25px;
padding: 8px; padding: 8px;
width: 400px; width: 400px;
height: 450px; height: ${(props) => props.theme.other.miniPlayer.height};
background: ${(props) => props.theme.primary.background}; background: ${(props) => props.theme.colors.layout.page.background};
border: 1px #000 solid; border: 1px #000 solid;
filter: drop-shadow(0px 1px 2px #121316); filter: drop-shadow(0px 1px 2px #121316);
overflow: hidden auto; overflow: hidden auto;
opacity: ${(props) => (props.display === 'true' ? 0.95 : 0)}; opacity: ${(props) => (props.display === 'true' ? props.theme.other.miniPlayer.opacity : 0)};
color: ${(props) => `${props.theme.primary.text} !important`}; color: ${(props) => `${props.theme.colors.layout.page.color} !important`};
z-index: 500; z-index: 500;
`; `;

8
src/components/playlist/PlaylistList.tsx

@ -1,7 +1,7 @@
import React, { useRef, useState } from 'react'; import React, { useRef, useState } from 'react';
import { useQuery, useQueryClient } from 'react-query'; import { useQuery, useQueryClient } from 'react-query';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import { Form, Input, Popover, Whisper } from 'rsuite'; import { Form, Input, Whisper } from 'rsuite';
import settings from 'electron-settings'; import settings from 'electron-settings';
import useSearchQuery from '../../hooks/useSearchQuery'; import useSearchQuery from '../../hooks/useSearchQuery';
import { createPlaylist, getPlaylists } from '../../api/api'; import { createPlaylist, getPlaylists } from '../../api/api';
@ -10,7 +10,7 @@ import PageLoader from '../loader/PageLoader';
import GenericPage from '../layout/GenericPage'; import GenericPage from '../layout/GenericPage';
import GenericPageHeader from '../layout/GenericPageHeader'; import GenericPageHeader from '../layout/GenericPageHeader';
import GridViewType from '../viewtypes/GridViewType'; import GridViewType from '../viewtypes/GridViewType';
import { StyledButton, StyledInputGroup } from '../shared/styled'; import { StyledButton, StyledInputGroup, StyledPopover } from '../shared/styled';
import { errorMessages, isFailedResponse } from '../../shared/utils'; import { errorMessages, isFailedResponse } from '../../shared/utils';
import { notifyToast } from '../shared/toast'; import { notifyToast } from '../shared/toast';
import { AddPlaylistButton } from '../shared/ToolbarButtons'; import { AddPlaylistButton } from '../shared/ToolbarButtons';
@ -99,7 +99,7 @@ const PlaylistList = () => {
placement="auto" placement="auto"
trigger="click" trigger="click"
speaker={ speaker={
<Popover> <StyledPopover>
<Form> <Form>
<StyledInputGroup> <StyledInputGroup>
<Input <Input
@ -123,7 +123,7 @@ const PlaylistList = () => {
Create Create
</StyledButton> </StyledButton>
</Form> </Form>
</Popover> </StyledPopover>
} }
> >
<AddPlaylistButton <AddPlaylistButton

12
src/components/playlist/PlaylistView.tsx

@ -3,7 +3,7 @@ import _ from 'lodash';
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
import settings from 'electron-settings'; import settings from 'electron-settings';
import { ButtonToolbar, Form, Input, Popover, Whisper } from 'rsuite'; import { ButtonToolbar, Form, Input, Whisper } from 'rsuite';
import { useHotkeys } from 'react-hotkeys-hook'; import { useHotkeys } from 'react-hotkeys-hook';
import { useQuery, useQueryClient } from 'react-query'; import { useQuery, useQueryClient } from 'react-query';
import { useParams, useHistory } from 'react-router-dom'; import { useParams, useHistory } from 'react-router-dom';
@ -58,7 +58,7 @@ import GenericPageHeader from '../layout/GenericPageHeader';
import { setStatus } from '../../redux/playerSlice'; import { setStatus } from '../../redux/playerSlice';
import { notifyToast } from '../shared/toast'; import { notifyToast } from '../shared/toast';
import { addProcessingPlaylist, removeProcessingPlaylist } from '../../redux/miscSlice'; import { addProcessingPlaylist, removeProcessingPlaylist } from '../../redux/miscSlice';
import { StyledButton, StyledCheckbox, StyledInputGroup } from '../shared/styled'; import { StyledButton, StyledCheckbox, StyledInputGroup, StyledPopover } from '../shared/styled';
import { import {
moveToIndex, moveToIndex,
removeFromPlaylist, removeFromPlaylist,
@ -414,7 +414,7 @@ const PlaylistView = ({ ...rest }) => {
placement="auto" placement="auto"
trigger="click" trigger="click"
speaker={ speaker={
<Popover> <StyledPopover>
<Form> <Form>
<StyledInputGroup> <StyledInputGroup>
<Input <Input
@ -450,7 +450,7 @@ const PlaylistView = ({ ...rest }) => {
Edit Edit
</StyledButton> </StyledButton>
</Form> </Form>
</Popover> </StyledPopover>
} }
> >
<EditButton size="md" disabled={misc.isProcessingPlaylist.includes(data?.id)} /> <EditButton size="md" disabled={misc.isProcessingPlaylist.includes(data?.id)} />
@ -461,12 +461,12 @@ const PlaylistView = ({ ...rest }) => {
placement="auto" placement="auto"
trigger="click" trigger="click"
speaker={ speaker={
<Popover> <StyledPopover>
<p>Are you sure you want to delete this playlist?</p> <p>Are you sure you want to delete this playlist?</p>
<StyledButton onClick={handleDelete} appearance="link"> <StyledButton onClick={handleDelete} appearance="link">
Yes Yes
</StyledButton> </StyledButton>
</Popover> </StyledPopover>
} }
> >
<DeleteButton <DeleteButton

41
src/components/scrollingmenu/ScrollingMenu.tsx

@ -41,7 +41,6 @@ const ScrollingMenu = ({
tabIndex={0} tabIndex={0}
onClick={onClickTitle} onClick={onClickTitle}
onKeyDown={(e: any) => { onKeyDown={(e: any) => {
console.log(e);
if (e.key === ' ' || e.key === 'Enter') { if (e.key === ' ' || e.key === 'Enter') {
onClickTitle(); onClickTitle();
} }
@ -51,24 +50,28 @@ const ScrollingMenu = ({
</SectionTitle> </SectionTitle>
</FlexboxGrid.Item> </FlexboxGrid.Item>
<FlexboxGrid.Item> <FlexboxGrid.Item>
<ButtonToolbar> {data.length > 0 && (
<ButtonGroup> <ButtonToolbar>
<StyledIconButton <ButtonGroup>
icon={<Icon icon="arrow-left" />} <StyledIconButton
onClick={() => { appearance="subtle"
scrollContainerRef.current.scrollLeft -= icon={<Icon icon="arrow-left" />}
config.lookAndFeel.gridView.cardSize * 5; onClick={() => {
}} scrollContainerRef.current.scrollLeft -=
/> config.lookAndFeel.gridView.cardSize * 5;
<StyledIconButton }}
icon={<Icon icon="arrow-right" />} />
onClick={() => { <StyledIconButton
scrollContainerRef.current.scrollLeft += appearance="subtle"
config.lookAndFeel.gridView.cardSize * 5; icon={<Icon icon="arrow-right" />}
}} onClick={() => {
/> scrollContainerRef.current.scrollLeft +=
</ButtonGroup> config.lookAndFeel.gridView.cardSize * 5;
</ButtonToolbar> }}
/>
</ButtonGroup>
</ButtonToolbar>
)}
</FlexboxGrid.Item> </FlexboxGrid.Item>
</FlexboxGrid> </FlexboxGrid>
</SectionTitleWrapper> </SectionTitleWrapper>

20
src/components/settings/Config.tsx

@ -1,13 +1,13 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import axios from 'axios'; import axios from 'axios';
import { shell } from 'electron'; import { shell } from 'electron';
import { Button, Whisper, Popover, Nav, ButtonToolbar } from 'rsuite'; import { Whisper, Nav, ButtonToolbar } from 'rsuite';
import { startScan, getScanStatus } from '../../api/api'; import { startScan, getScanStatus } from '../../api/api';
import GenericPage from '../layout/GenericPage'; import GenericPage from '../layout/GenericPage';
import DisconnectButton from './DisconnectButton'; import DisconnectButton from './DisconnectButton';
import GenericPageHeader from '../layout/GenericPageHeader'; import GenericPageHeader from '../layout/GenericPageHeader';
import setDefaultSettings from '../shared/setDefaultSettings'; import setDefaultSettings from '../shared/setDefaultSettings';
import { StyledButton, StyledNavItem } from '../shared/styled'; import { StyledButton, StyledNavItem, StyledPopover } from '../shared/styled';
import PlaybackConfig from './ConfigPanels/PlaybackConfig'; import PlaybackConfig from './ConfigPanels/PlaybackConfig';
import LookAndFeelConfig from './ConfigPanels/LookAndFeelConfig'; import LookAndFeelConfig from './ConfigPanels/LookAndFeelConfig';
import PlayerConfig from './ConfigPanels/PlayerConfig'; import PlayerConfig from './ConfigPanels/PlayerConfig';
@ -106,23 +106,23 @@ const Config = () => {
trigger="click" trigger="click"
placement="auto" placement="auto"
speaker={ speaker={
<Popover title="Confirm"> <StyledPopover title="Confirm">
<div>Are you sure you want to reset your settings to default?</div> <div>Are you sure you want to reset your settings to default?</div>
<strong>WARNING: This will reload the application</strong>
<div> <div>
<Button <StyledButton
id="reset-submit-button" id="reset-submit-button"
size="sm" size="sm"
onClick={() => { onClick={() => {
setDefaultSettings(true); setDefaultSettings(true);
window.location.reload(); window.location.reload();
}} }}
appearance="link" appearance="primary"
> >
Yes Yes
</Button> </StyledButton>
<strong>WARNING: This will reload the application</strong>
</div> </div>
</Popover> </StyledPopover>
} }
> >
<StyledButton size="sm">Reset defaults</StyledButton> <StyledButton size="sm">Reset defaults</StyledButton>
@ -133,7 +133,7 @@ const Config = () => {
enterable enterable
preventOverflow preventOverflow
speaker={ speaker={
<Popover> <StyledPopover>
<> <>
Current version: {packageJson.version} Current version: {packageJson.version}
<br /> <br />
@ -165,7 +165,7 @@ const Config = () => {
View CHANGELOG View CHANGELOG
</StyledButton> </StyledButton>
</> </>
</Popover> </StyledPopover>
} }
> >
<StyledButton <StyledButton

63
src/components/settings/ConfigPanels/CacheConfig.tsx

@ -3,9 +3,18 @@ import settings from 'electron-settings';
import { shell } from 'electron'; import { shell } from 'electron';
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
import { InputGroup, Button, Tag, Message, Icon, ButtonToolbar, Whisper, Popover } from 'rsuite'; import { Message, Icon, ButtonToolbar, Whisper } from 'rsuite';
import { ConfigPanel } from '../styled'; import { ConfigPanel } from '../styled';
import { StyledInput, StyledCheckbox, StyledInputGroup, StyledLink } from '../../shared/styled'; import {
StyledInput,
StyledCheckbox,
StyledInputGroup,
StyledLink,
StyledPopover,
StyledTag,
StyledButton,
StyledInputGroupButton,
} from '../../shared/styled';
import { getSongCachePath, getImageCachePath, getRootCachePath } from '../../../shared/utils'; import { getSongCachePath, getImageCachePath, getRootCachePath } from '../../../shared/utils';
import { notifyToast } from '../../shared/toast'; import { notifyToast } from '../../shared/toast';
import { setMiscSetting } from '../../../redux/miscSlice'; import { setMiscSetting } from '../../../redux/miscSlice';
@ -112,7 +121,7 @@ const CacheConfig = () => {
<> <>
<StyledInputGroup> <StyledInputGroup>
<StyledInput value={newCachePath} onChange={(e: string) => setNewCachePath(e)} /> <StyledInput value={newCachePath} onChange={(e: string) => setNewCachePath(e)} />
<InputGroup.Button <StyledInputGroupButton
onClick={() => { onClick={() => {
const check = fs.existsSync(newCachePath); const check = fs.existsSync(newCachePath);
if (check) { if (check) {
@ -131,16 +140,16 @@ const CacheConfig = () => {
}} }}
> >
<Icon icon="check" /> <Icon icon="check" />
</InputGroup.Button> </StyledInputGroupButton>
<InputGroup.Button <StyledInputGroupButton
onClick={() => { onClick={() => {
setIsEditingCachePath(false); setIsEditingCachePath(false);
setErrorMessage(''); setErrorMessage('');
}} }}
> >
<Icon icon="close" /> <Icon icon="close" />
</InputGroup.Button> </StyledInputGroupButton>
<InputGroup.Button <StyledInputGroupButton
onClick={() => { onClick={() => {
const defaultPath = path.join(path.dirname(settings.file())); const defaultPath = path.join(path.dirname(settings.file()));
settings.setSync('cachePath', defaultPath); settings.setSync('cachePath', defaultPath);
@ -151,7 +160,7 @@ const CacheConfig = () => {
}} }}
> >
Reset to default Reset to default
</InputGroup.Button> </StyledInputGroupButton>
</StyledInputGroup> </StyledInputGroup>
<p style={{ fontSize: 'smaller' }}> <p style={{ fontSize: 'smaller' }}>
*You will need to manually move any existing cached files to their new location. *You will need to manually move any existing cached files to their new location.
@ -163,7 +172,7 @@ const CacheConfig = () => {
Location:{' '} Location:{' '}
<div style={{ overflow: 'auto' }}> <div style={{ overflow: 'auto' }}>
<StyledLink onClick={() => shell.openPath(getRootCachePath())}> <StyledLink onClick={() => shell.openPath(getRootCachePath())}>
{getRootCachePath()} {getRootCachePath()} <Icon icon="external-link" />
</StyledLink> </StyledLink>
</div> </div>
</> </>
@ -177,9 +186,9 @@ const CacheConfig = () => {
}} }}
> >
Songs{' '} Songs{' '}
<Tag> <StyledTag>
{songCacheSize} MB {imgCacheSize === 9999999 && '- Folder not found'} {songCacheSize} MB {imgCacheSize === 9999999 && '- Folder not found'}
</Tag> </StyledTag>
</StyledCheckbox> </StyledCheckbox>
<StyledCheckbox <StyledCheckbox
defaultChecked={cacheImages} defaultChecked={cacheImages}
@ -189,41 +198,41 @@ const CacheConfig = () => {
}} }}
> >
Images{' '} Images{' '}
<Tag> <StyledTag>
{imgCacheSize} MB {imgCacheSize === 9999999 && '- Folder not found'} {imgCacheSize} MB {imgCacheSize === 9999999 && '- Folder not found'}
</Tag> </StyledTag>
</StyledCheckbox> </StyledCheckbox>
</div> </div>
<br /> <br />
<ButtonToolbar> <ButtonToolbar>
<Button onClick={() => setIsEditingCachePath(true)}>Edit cache location</Button> <StyledButton onClick={() => setIsEditingCachePath(true)}>Edit cache location</StyledButton>
<Whisper <Whisper
trigger="click" trigger="click"
placement="autoVertical" placement="autoVertical"
speaker={ speaker={
<Popover> <StyledPopover>
Which cache would you like to clear? Which cache would you like to clear?
<ButtonToolbar> <ButtonToolbar>
<Button size="sm" onClick={handleClearSongCache}> <StyledButton size="sm" onClick={handleClearSongCache}>
Songs Songs
</Button> </StyledButton>
<Button size="sm" onClick={() => handleClearImageCache('playlist')}> <StyledButton size="sm" onClick={() => handleClearImageCache('playlist')}>
Playlist images Playlist images
</Button> </StyledButton>
<Button size="sm" onClick={() => handleClearImageCache('album')}> <StyledButton size="sm" onClick={() => handleClearImageCache('album')}>
Album images Album images
</Button> </StyledButton>
<Button size="sm" onClick={() => handleClearImageCache('artist')}> <StyledButton size="sm" onClick={() => handleClearImageCache('artist')}>
Artist images Artist images
</Button> </StyledButton>
<Button size="sm" onClick={() => handleClearImageCache('folder')}> <StyledButton size="sm" onClick={() => handleClearImageCache('folder')}>
Folder images Folder images
</Button> </StyledButton>
</ButtonToolbar> </ButtonToolbar>
</Popover> </StyledPopover>
} }
> >
<Button>Clear cache</Button> <StyledButton>Clear cache</StyledButton>
</Whisper> </Whisper>
</ButtonToolbar> </ButtonToolbar>
</ConfigPanel> </ConfigPanel>

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

@ -1,8 +1,8 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, 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 { TagPicker, ControlLabel } from 'rsuite'; import { ControlLabel } from 'rsuite';
import { StyledInputNumber, StyledPanel } from '../../shared/styled'; import { StyledInputNumber, 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 {
@ -100,12 +100,12 @@ const ListViewConfig = ({ defaultColumns, columnPicker, columnList, settingsConf
<div style={{ width: '100%' }}> <div style={{ width: '100%' }}>
<div> <div>
<StyledPanel bordered bodyFill> <StyledPanel bordered bodyFill>
<TagPicker <StyledTagPicker
data={columnPicker} data={columnPicker}
defaultValue={defaultColumns} defaultValue={defaultColumns}
value={selectedColumns} value={selectedColumns}
style={{ width: '100%' }} style={{ width: '100%' }}
onChange={(e) => { onChange={(e: any) => {
const columns: any[] = []; const columns: any[] = [];
if (e) { if (e) {
e.forEach((selected: string) => { e.forEach((selected: string) => {

6
src/components/settings/DisconnectButton.tsx

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import settings from 'electron-settings'; import settings from 'electron-settings';
import { Button } from 'rsuite'; import { StyledButton } from '../shared/styled';
const DisconnectButton = () => { const DisconnectButton = () => {
const handleDisconnect = () => { const handleDisconnect = () => {
@ -17,9 +17,9 @@ const DisconnectButton = () => {
window.location.reload(); window.location.reload();
}; };
return ( return (
<Button onClick={handleDisconnect} size="sm"> <StyledButton onClick={handleDisconnect} size="sm">
Disconnect Disconnect
</Button> </StyledButton>
); );
}; };

10
src/components/settings/styled.tsx

@ -2,27 +2,27 @@ import styled from 'styled-components';
import { Panel } from 'rsuite'; import { Panel } from 'rsuite';
export const ConfigPanel = styled(Panel)` export const ConfigPanel = styled(Panel)`
color: ${(props) => props.theme.primary.text}; color: ${(props) => props.theme.colors.layout.page.color};
padding: 20px; padding: 20px;
min-width: 500px; min-width: 500px;
max-width: 800px; max-width: 800px;
margin: 15px auto 15px auto; margin: 15px auto 15px auto;
border-radius: 0px; border-radius: ${(props) => props.theme.other.panel.borderRadius};
.rs-panel-heading { .rs-panel-heading {
font-size: ${(props) => props.theme.all.fonts.panelHeaderFontSize}; font-size: ${(props) => props.theme.fonts.size.panelTitle};
} }
`; `;
export const MockFooter = styled.div` export const MockFooter = styled.div`
width: 100%; width: 100%;
height: 100%; height: 100%;
background: ${(props) => props.theme.primary.playerBar}; background: ${(props) => props.theme.colors.layout.playerBar.background};
border-top: 1px solid #48545c; border-top: 1px solid #48545c;
`; `;
export const LoginPanel = styled(Panel)` export const LoginPanel = styled(Panel)`
color: ${(props) => props.theme.primary.text}; color: ${(props) => props.theme.colors.layout.page.color};
padding: 20px; padding: 20px;
min-width: 300px; min-width: 300px;
max-width: 300px; max-width: 300px;

150
src/components/shared/ContextMenu.tsx

@ -4,7 +4,7 @@ import _ from 'lodash';
import { nanoid } from 'nanoid/non-secure'; import { nanoid } from 'nanoid/non-secure';
import { useQuery, useQueryClient } from 'react-query'; import { useQuery, useQueryClient } from 'react-query';
import { useHistory } from 'react-router'; import { useHistory } from 'react-router';
import { Col, FlexboxGrid, Form, Grid, Icon, Input, Row, Whisper } from 'rsuite'; import { Col, FlexboxGrid, Form, Grid, Icon, Row, Whisper } from 'rsuite';
import { import {
getPlaylists, getPlaylists,
updatePlaylistSongsLg, updatePlaylistSongsLg,
@ -51,9 +51,10 @@ import {
StyledInputPicker, StyledInputPicker,
StyledButton, StyledButton,
StyledInputGroup, StyledInputGroup,
StyledPopover,
StyledInputNumber, StyledInputNumber,
StyledIconButton, StyledInputPickerContainer,
ContextMenuPopover,
StyledInput,
} from './styled'; } from './styled';
import { notifyToast } from './toast'; import { notifyToast } from './toast';
import { errorMessages, getCurrentEntryList, isFailedResponse } from '../../shared/utils'; import { errorMessages, getCurrentEntryList, isFailedResponse } from '../../shared/utils';
@ -555,8 +556,38 @@ export const GlobalContextMenu = () => {
enterable enterable
placement="autoHorizontalStart" placement="autoHorizontalStart"
trigger="hover" trigger="hover"
delayShow={300}
speaker={ speaker={
<StyledPopover> <ContextMenuPopover style={{ width: '150px' }}>
<Grid fluid>
<Row>
<Col xs={12}>
<StyledButton onClick={handleMoveToTop} block>
<Icon icon="angle-double-up" />
</StyledButton>
</Col>
<Col xs={12}>
<StyledButton onClick={handleMoveUpOne} block>
<Icon icon="angle-up" />
</StyledButton>
</Col>
</Row>
<Row>
<Col xs={12}>
<StyledButton onClick={handleMoveToBottom} block>
<Icon icon="angle-double-down" />
</StyledButton>
</Col>
<Col xs={12}>
<StyledButton onClick={handleMoveDownOne} block>
<Icon icon="angle-down" />
</StyledButton>
</Col>
</Row>
</Grid>
<br />
<Form> <Form>
<StyledInputGroup> <StyledInputGroup>
<StyledInputNumber <StyledInputNumber
@ -583,51 +614,7 @@ export const GlobalContextMenu = () => {
</StyledButton> </StyledButton>
</StyledInputGroup> </StyledInputGroup>
</Form> </Form>
</ContextMenuPopover>
<Grid fluid>
<Row>
<Col xs={12}>
<StyledIconButton
icon={<Icon icon="angle-double-up" />}
onClick={handleMoveToTop}
block
>
Top
</StyledIconButton>
</Col>
<Col xs={12}>
<StyledIconButton
icon={<Icon icon="angle-up" />}
onClick={handleMoveUpOne}
block
>
Up
</StyledIconButton>
</Col>
</Row>
<Row>
<Col xs={12}>
<StyledIconButton
icon={<Icon icon="angle-double-down" />}
onClick={handleMoveToBottom}
block
>
Bottom
</StyledIconButton>
</Col>
<Col xs={12}>
<StyledIconButton
icon={<Icon icon="angle-down" />}
onClick={handleMoveDownOne}
block
>
Down
</StyledIconButton>
</Col>
</Row>
</Grid>
</StyledPopover>
} }
> >
<ContextMenuButton <ContextMenuButton
@ -643,27 +630,31 @@ export const GlobalContextMenu = () => {
placement="autoHorizontalStart" placement="autoHorizontalStart"
trigger="none" trigger="none"
speaker={ speaker={
<StyledPopover> <ContextMenuPopover>
<StyledInputGroup> <StyledInputPickerContainer ref={playlistPickerContainerRef}>
<StyledInputPicker <StyledInputGroup>
data={playlists} <StyledInputPicker
placement="autoVerticalStart" container={() => playlistPickerContainerRef.current}
virtualized data={playlists}
labelKey="name" placement="autoVerticalStart"
valueKey="id" virtualized
width={200} labelKey="name"
onChange={(e: any) => setSelectedPlaylistId(e)} valueKey="id"
/> width={200}
<StyledButton onChange={(e: any) => setSelectedPlaylistId(e)}
disabled={ />
!selectedPlaylistId || misc.isProcessingPlaylist.includes(selectedPlaylistId) <StyledButton
} disabled={
loading={misc.isProcessingPlaylist.includes(selectedPlaylistId)} !selectedPlaylistId ||
onClick={handleAddToPlaylist} misc.isProcessingPlaylist.includes(selectedPlaylistId)
> }
Add loading={misc.isProcessingPlaylist.includes(selectedPlaylistId)}
</StyledButton> onClick={handleAddToPlaylist}
</StyledInputGroup> >
Add
</StyledButton>
</StyledInputGroup>
</StyledInputPickerContainer>
<div> <div>
<StyledButton <StyledButton
@ -676,14 +667,11 @@ export const GlobalContextMenu = () => {
{shouldCreatePlaylist && ( {shouldCreatePlaylist && (
<Form> <Form>
<StyledInputGroup> <StyledInput
<Input placeholder="Enter name..."
placeholder="Enter name..." value={newPlaylistName}
value={newPlaylistName} onChange={(e: string) => setNewPlaylistName(e)}
onChange={(e) => setNewPlaylistName(e)} />
/>
</StyledInputGroup>
<br />
<StyledButton <StyledButton
size="sm" size="sm"
type="submit" type="submit"
@ -699,7 +687,7 @@ export const GlobalContextMenu = () => {
</StyledButton> </StyledButton>
</Form> </Form>
)} )}
</StyledPopover> </ContextMenuPopover>
} }
> >
<ContextMenuButton <ContextMenuButton
@ -718,12 +706,12 @@ export const GlobalContextMenu = () => {
placement="autoHorizontalStart" placement="autoHorizontalStart"
trigger="none" trigger="none"
speaker={ speaker={
<StyledPopover> <ContextMenuPopover>
<p>Are you sure you want to delete {multiSelect.selected?.length} playlist(s)?</p> <p>Are you sure you want to delete {multiSelect.selected?.length} playlist(s)?</p>
<StyledButton onClick={handleDeletePlaylist} appearance="link"> <StyledButton onClick={handleDeletePlaylist} appearance="link">
Yes Yes
</StyledButton> </StyledButton>
</StyledPopover> </ContextMenuPopover>
} }
> >
<ContextMenuButton <ContextMenuButton

7
src/components/shared/CustomTooltip.tsx

@ -3,10 +3,11 @@ import { Tooltip, Whisper } from 'rsuite';
import styled from 'styled-components'; import styled from 'styled-components';
const StyledTooltip = styled(Tooltip)` const StyledTooltip = styled(Tooltip)`
border: 1px #3c3f43 solid;
.rs-tooltip-inner { .rs-tooltip-inner {
background-color: ${(props) => props.theme.primary.background}; background-color: ${(props) => props.theme.colors.tooltip.background};
color: ${(props) => props.theme.primary.text}; color: ${(props) => props.theme.colors.tooltip.color};
border-radius: ${(props) => props.theme.other.tooltip.borderRadius};
border: ${(props) => props.theme.other.tooltip.border};
} }
`; `;

8
src/components/shared/ToolbarButtons.tsx

@ -98,7 +98,9 @@ export const DownloadButton = ({ ...rest }) => {
export const ShuffleButton = ({ ...rest }) => { export const ShuffleButton = ({ ...rest }) => {
return ( return (
<CustomTooltip text="Shuffle queue" placement="bottom"> <CustomTooltip text="Shuffle queue" placement="bottom">
<StyledIconButton icon={<Icon icon="random" />} tabIndex={0} {...rest} /> <StyledButton tabIndex={0} {...rest}>
<Icon icon="random" />
</StyledButton>
</CustomTooltip> </CustomTooltip>
); );
}; };
@ -106,7 +108,9 @@ export const ShuffleButton = ({ ...rest }) => {
export const ClearQueueButton = ({ ...rest }) => { export const ClearQueueButton = ({ ...rest }) => {
return ( return (
<CustomTooltip text="Clear queue" placement="bottom"> <CustomTooltip text="Clear queue" placement="bottom">
<StyledIconButton icon={<Icon icon="trash2" />} tabIndex={0} {...rest} /> <StyledButton tabIndex={0} {...rest}>
<Icon icon="trash2" />
</StyledButton>
</CustomTooltip> </CustomTooltip>
); );
}; };

289
src/components/shared/setDefaultSettings.ts

@ -425,6 +425,295 @@ const setDefaultSettings = (force: boolean) => {
}, },
]); ]);
} }
if (force || !settings.hasSync('themes')) {
settings.setSync('themes', [
{
label: 'Default Dark',
value: 'defaultDark',
fonts: {
size: {
page: '14px',
pageTitle: '40px',
panelTitle: '20px',
},
},
colors: {
primary: '#2196F3',
layout: {
page: {
color: '#D8D8D8',
colorSecondary: '#888e94',
background: '#181A1F',
},
playerBar: {
color: '#D8D8D8',
colorSecondary: '#888e94',
background: '#101010',
button: {
color: 'rgba(240, 240, 240, 0.8)',
colorHover: '#FFFFFF',
},
},
sideBar: {
background: '#101010',
button: {
color: '#D8D8D8',
colorHover: '#FFFFFF',
},
},
titleBar: {
color: '#FFFFFF',
background: '#101010',
},
},
button: {
default: {
color: '#D8D8D8',
colorHover: '#FFFFFF',
background: '#292D33',
backgroundHover: '#3C3F43',
},
primary: {
color: '#FFFFFF',
colorHover: '#FFFFFF',
backgroundHover: '#3B89EC',
},
subtle: {
color: '#D8D8D8',
colorHover: '#D8D8D8',
backgroundHover: 'transparent',
},
link: {
color: '#2196F3',
colorHover: '#3B89EC',
},
},
card: {
overlayButton: {
color: '#FFFFFF',
background: '#000000',
opacity: 0.8,
},
},
contextMenu: {
color: '#D8D8D8',
colorDisabled: '#6A6F76',
background: '#1E2125',
backgroundHover: '#292D33',
},
input: {
color: '#D8D8D8',
background: '#25292E',
backgroundHover: '#353A45',
backgroundActive: 'rgba(240, 240, 240, .2)',
},
nav: {
color: '#D8D8D8',
},
popover: {
color: '#D8D8D8',
background: '#1E2125',
},
slider: {
background: '#3C3F43',
progressBar: '#888E94',
},
spinner: {
background: 'rgba(233, 235, 240, 0.3)',
foreground: '#2196F3',
},
table: {
selectedRow: '#4D5156',
},
tag: {
background: '#3C3F43',
text: '#E2E4E9',
},
tooltip: {
color: '#D8D8D8',
background: '#1E2125',
},
},
other: {
button: {
borderRadius: '0px',
},
coverArtFilter: 'drop-shadow(0px 3px 5px #000000)',
card: {
borderRadius: '0px',
},
input: {
borderRadius: '0px',
},
miniPlayer: {
height: '450px',
opacity: 0.95,
},
panel: {
borderRadius: '0px',
},
playerBar: {
borderTop: '1px solid rgba(240, 240, 240, .15)',
borderRight: 'none',
borderBottom: 'none',
borderLeft: 'none',
filter: 'none',
},
tag: {
borderRadius: '0px',
},
tooltip: {
border: '1px #3c3f43 solid',
borderRadius: '0px',
},
},
},
{
label: 'Default Light',
value: 'defaultLight',
fonts: {
size: {
page: '14px',
pageTitle: '30px',
panelTitle: '20px',
},
},
colors: {
primary: '#285DA0',
layout: {
page: {
color: '#000000',
colorSecondary: '#888e94',
background: '#FFFFFF',
},
playerBar: {
color: '#FFFFFF',
colorSecondary: '#888e94',
background: '#161B22',
button: {
color: 'rgba(240, 240, 240, 0.8)',
colorHover: '#FFFFFF',
},
},
sideBar: {
background: '#161B22',
button: {
color: '#D8D8D8',
colorHover: '#FFFFFF',
},
},
titleBar: {
color: '#FFFFFF',
background: '#161B22',
},
},
button: {
default: {
color: '#575757',
colorHover: '#000000',
background: '#DFDFE2',
backgroundHover: '#D2D2D6',
},
primary: {
color: '#FFFFFF',
colorHover: '#FFFFFF',
backgroundHover: '#347AD3',
},
subtle: {
color: '#575757',
colorHover: '#575757',
backgroundHover: 'transparent',
},
link: {
color: '#575757',
colorHover: '#575757',
},
},
card: {
overlayButton: {
color: '#FFFFFF',
colorHover: '#FFFFFF',
background: '#000000',
backgroundHover: '#285DA0',
opacity: 0.8,
},
},
contextMenu: {
color: '#575757',
colorDisabled: '#BABABA',
background: '#FFFFFF',
backgroundHover: '#D2D2D6',
},
input: {
color: '#000000',
background: '#FFFFFF',
backgroundHover: '#E5E5EA',
backgroundActive: 'rgba(0, 0, 0, .2)',
},
nav: {
color: '#000000',
},
popover: {
color: '#000000',
background: '#FFFFFF',
},
slider: {
background: '#3C3F43',
progressBar: '#888E94',
},
spinner: {
background: 'rgba(0, 0, 0, 0.3)',
foreground: '#285DA0',
},
table: {
selectedRow: '#CCCCCC',
},
tag: {
background: '#DFDFE2',
text: '#000000',
},
tooltip: {
color: '#000000',
background: '#FFFFFF',
},
},
other: {
button: {
borderRadius: '0px',
},
coverArtFilter: 'drop-shadow(0px 3px 5px #000000)',
card: {
borderRadius: '0px',
},
input: {
borderRadius: '0px',
},
miniPlayer: {
height: '450px',
opacity: 0.95,
},
panel: {
borderRadius: '0px',
},
playerBar: {
borderTop: '1px solid rgba(240, 240, 240, .15)',
borderRight: 'none',
borderBottom: 'none',
borderLeft: 'none',
filter: 'none',
},
tag: {
borderRadius: '0px',
},
tooltip: {
border: '1px #3c3f43 solid',
borderRadius: '0px',
},
},
},
]);
}
}; };
export default setDefaultSettings; export default setDefaultSettings;

342
src/components/shared/styled.ts

@ -13,6 +13,8 @@ import {
InputPicker, InputPicker,
Popover, Popover,
Panel, Panel,
TagPicker,
Tag,
} from 'rsuite'; } from 'rsuite';
import styled from 'styled-components'; import styled from 'styled-components';
@ -22,31 +24,121 @@ export const HeaderButton = styled(Button)`
`; `;
export const StyledButton = styled(Button)<{ width: number }>` export const StyledButton = styled(Button)<{ width: number }>`
border-radius: ${(props) => props.theme.other.button.borderRadius} !important;
background: ${(props) => background: ${(props) =>
props.appearance === 'primary' ? `${props.theme.primary.main} !important` : undefined}; props.appearance === 'primary'
? `${props.theme.colors.primary}`
: props.appearance === 'subtle' || props.appearance === 'link'
? undefined
: `${props.theme.colors.button.default.background}`} !important;
color: ${(props) =>
props.appearance === 'primary'
? `${props.theme.colors.button.primary.color}`
: props.appearance === 'subtle'
? `${props.theme.colors.button.subtle.color}`
: props.appearance === 'link'
? `${props.theme.colors.button.link.color}`
: `${props.theme.colors.button.default.color}`} !important;
width: ${(props) => `${props.width}px`}; width: ${(props) => `${props.width}px`};
&:active,
&:focus,
&:hover {
text-decoration: none;
color: ${(props) =>
props.appearance === 'primary'
? `${props.theme.colors.button.primary.colorHover}`
: props.appearance === 'subtle'
? `${props.theme.colors.button.subtle.colorHover}`
: props.appearance === 'link'
? `${props.theme.colors.button.link.colorHover}`
: `${props.theme.colors.button.default.colorHover}`} !important;
background: ${(props) =>
props.appearance === 'primary'
? `${props.theme.colors.button.primary.backgroundHover}`
: props.appearance === 'subtle'
? `${props.theme.colors.button.subtle.backgroundHover}`
: props.appearance === 'link'
? undefined
: `${props.theme.colors.button.default.backgroundHover}`} !important;
}
`; `;
export const StyledInputGroup = styled(InputGroup)` export const StyledInputGroup = styled(InputGroup)`
input { border-radius: ${(props) => props.theme.other.input.borderRadius};
background-color: ${(props) => props.theme.primary.inputBackground}; `;
color: ${(props) => `${props.theme.primary.text} !important`};
export const StyledInputGroupButton = styled(InputGroup.Button)`
background: ${(props) =>
props.appearance === 'primary'
? `${props.theme.colors.primary}`
: props.appearance === 'subtle' || props.appearance === 'link'
? undefined
: `${props.theme.colors.button.default.background}`} !important;
color: ${(props) =>
props.appearance === 'primary'
? `${props.theme.colors.button.primary.color}`
: props.appearance === 'subtle'
? `${props.theme.colors.button.subtle.color}`
: props.appearance === 'link'
? 'none'
: `${props.theme.colors.button.default.color}`} !important;
&:active,
&:focus,
&:hover {
background: ${(props) =>
props.appearance === 'primary'
? `${props.theme.colors.button.primary.backgroundHover}`
: props.appearance === 'subtle' || props.appearance === 'link'
? `none !important`
: `${props.theme.colors.button.default.backgroundHover} !important`};
}
&:hover {
color: ${(props) =>
props.appearance === 'primary'
? `${props.theme.colors.button.primary.colorHover}`
: props.appearance !== 'subtle'
? `${props.theme.colors.button.default.colorHover}`
: `${props.theme.colors.button.subtle.colorHover} !important`};
} }
border-radius: ${(props) => props.theme.other.input.borderRadius};
border-bottom-right-radius: ${(props) => props.theme.other.input.borderRadius} !important;
border-top-right-radius: ${(props) => props.theme.other.input.borderRadius} !important;
`; `;
export const StyledInputNumber = styled(InputNumber)<{ width: number }>` export const StyledInputNumber = styled(InputNumber)<{ width: number }>`
border: 1px #3c3f43 solid !important;
border-radius: ${(props) => props.theme.other.input.borderRadius} !important;
input { input {
background-color: ${(props) => props.theme.primary.inputBackground}; background-color: ${(props) => props.theme.colors.input.background};
}
.rs-input {
border-radius: ${(props) => props.theme.other.input.borderRadius} !important;
} }
&:hover,
&:active,
&:focus {
border-color: ${(props) => props.theme.colors.primary} !important;
}
width: ${(props) => `${props.width}px`}; width: ${(props) => `${props.width}px`};
`; `;
export const StyledInput = styled(Input)<{ width: number }>` export const StyledInput = styled(Input)<{ width: number }>`
input { border: 1px #3c3f43 solid;
background-color: ${(props) => props.theme.primary.inputBackground}; border-radius: ${(props) => props.theme.other.input.borderRadius} !important;
}
color: ${(props) => props.theme.colors.input.color} !important;
background: ${(props) => props.theme.colors.input.background} !important;
width: ${(props) => `${props.width}px`}; width: ${(props) => `${props.width}px`};
border-radius: ${(props) => props.theme.other.input.borderRadius};
`; `;
export const StyledCheckbox = styled(Checkbox)` export const StyledCheckbox = styled(Checkbox)`
@ -54,12 +146,12 @@ export const StyledCheckbox = styled(Checkbox)`
label { label {
span { span {
&:before { &:before {
border: ${(props) => `1px solid ${props.theme.primary.main}`}; border: ${(props) => `1px solid ${props.theme.colors.primary}`};
} }
span { span {
&:before { &:before {
background-color: ${(props) => background-color: ${(props) =>
props.defaultChecked ? `${props.theme.primary.main} !important` : undefined}; props.defaultChecked ? `${props.theme.colors.primary} !important` : undefined};
} }
&:after { &:after {
border: transparent !important; border: transparent !important;
@ -79,7 +171,7 @@ export const StyledRadio = styled(Radio)`
background-color: transparent !important; background-color: transparent !important;
} }
&:after { &:after {
background: ${(props) => `${props.theme.primary.main} !important`}; background: ${(props) => `${props.theme.colors.primary} !important`};
} }
} }
} }
@ -88,39 +180,62 @@ export const StyledRadio = styled(Radio)`
`; `;
export const StyledIconButton = styled(IconButton)` export const StyledIconButton = styled(IconButton)`
border-radius: ${(props) => props.theme.other.button.borderRadius} !important;
background: ${(props) =>
props.appearance === 'primary'
? `${props.theme.colors.primary}`
: props.appearance === 'subtle' || props.appearance === 'link'
? undefined
: `${props.theme.colors.button.default.background}`};
color: ${(props) =>
props.appearance === 'primary'
? `${props.theme.colors.button.primary.color}`
: props.appearance === 'subtle'
? `${props.theme.colors.button.subtle.color}`
: props.appearance === 'link'
? undefined
: `${props.theme.colors.button.default.color}`};
width: ${(props) => `${props.width}px`};
&:active,
&:focus,
&:hover { &:hover {
color: ${(props) =>
props.appearance === 'primary'
? `${props.theme.colors.button.primary.colorHover}`
: props.appearance !== 'subtle'
? `${props.theme.colors.button.default.colorHover}`
: `${props.theme.colors.button.subtle.colorHover}`};
background: ${(props) => background: ${(props) =>
props.appearance === 'subtle' ? 'transparent !important' : undefined}; props.appearance === 'primary'
color: ${(props) => (props.appearance === 'subtle' ? props.theme.primary.main : undefined)}; ? `${props.theme.colors.button.primary.backgroundHover}`
} : props.appearance === 'subtle'
&:focus { ? `${props.theme.colors.button.subtle.backgroundHover}`
background: ${(props) => : props.appearance === 'link'
props.appearance === 'subtle' ? 'transparent !important' : undefined}; ? undefined
color: ${(props) => (props.appearance === 'subtle' ? props.theme.primary.main : undefined)}; : `${props.theme.colors.button.default.backgroundHover}`};
} }
background-color: ${(props) =>
props.appearance === 'primary' ? props.theme.primary.main : undefined};
width: ${(props) => `${props.width}px`};
`; `;
export const StyledNavItem = styled(Nav.Item)` export const StyledNavItem = styled(Nav.Item)`
a { a {
color: ${(props) => (props.active ? `${props.theme.primary.main} !important;` : undefined)}; color: ${(props) =>
props.active
&:hover { ? `${props.theme.colors.primary} !important`
color: ${(props) => `${props.theme.primary.main} !important;`}; : `${props.theme.colors.nav.color} !important`};
}
} }
`; `;
export const StyledIconToggle = styled(Icon)<{ active: string }>` export const StyledIconToggle = styled(Icon)<{ active: string }>`
cursor: pointer; cursor: pointer;
color: ${(props) => color: ${(props) =>
props.active === 'true' ? props.theme.primary.main : props.theme.primary.text}; props.active === 'true' ? props.theme.colors.primary : props.theme.colors.layout.page.color};
`; `;
export const StyledRate = styled(Rate)` export const StyledRate = styled(Rate)`
color: ${(props) => props.theme.primary.main}; color: ${(props) => props.theme.colors.primary};
`; `;
export const StyledSlider = styled(Slider)` export const StyledSlider = styled(Slider)`
@ -131,20 +246,111 @@ export const StyledSlider = styled(Slider)`
} }
`; `;
export const StyledInputPickerContainer = styled.div`
.rs-picker-menu {
background: ${(props) => props.theme.colors.input.background};
border-radius: ${(props) => props.theme.other.input.borderRadius};
}
.rs-picker-select-menu-item-active {
background: ${(props) => props.theme.colors.input.backgroundActive};
&:hover {
background: ${(props) => props.theme.colors.input.backgroundActive};
}
}
.rs-picker-select-menu-item-focus {
color: ${(props) => props.theme.colors.input.color};
background: ${(props) => props.theme.colors.input.backgroundHover};
}
.rs-picker-select-menu-item,
.rs-picker-select-menu-group-title {
color: ${(props) => props.theme.colors.input.color};
&:hover {
color: ${(props) => props.theme.colors.input.color};
&:hover {
background: ${(props) => props.theme.colors.input.backgroundHover};
}
}
}
.rs-picker-select-menu-group-title {
color: ${(props) => props.theme.colors.input.color};
&:hover {
color: ${(props) => props.theme.colors.input.color};
}
}
.rs-check-item {
background: ${(props) => props.theme.colors.input.background} !important;
border-radius: ${(props) => props.theme.other.input.borderRadius};
&:hover {
color: ${(props) => props.theme.colors.input.color};
background-color: ${(props) => props.theme.colors.input.backgroundHover} !important;
}
}
.rs-check-item-focus {
color: ${(props) => props.theme.colors.input.color};
background: ${(props) => props.theme.colors.input.backgroundActive} !important;
}
.rs-checkbox-checked {
.rs-checkbox-checker {
span {
&:before {
border: ${(props) => `1px solid ${props.theme.colors.primary}`};
}
span {
&:before {
background-color: ${(props) => `${props.theme.colors.primary} !important`};
}
&:after {
border: transparent !important;
}
}
}
}
}
`;
export const StyledInputPicker = styled(InputPicker)<{ width?: number }>` export const StyledInputPicker = styled(InputPicker)<{ width?: number }>`
border: 1px #3c3f43 solid !important;
border-radius: ${(props) => props.theme.other.input.borderRadius} !important;
&:hover,
&:active,
&:focus {
border-color: ${(props) => props.theme.colors.primary} !important;
}
.rs-picker-toggle-value { .rs-picker-toggle-value {
color: ${(props) => `${props.theme.primary.text} !important`}; color: ${(props) => `${props.theme.colors.layout.page.color} !important`};
}
.rs-picker-toggle {
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.primary.inputBackground} !important`}; background: ${(props) => `${props.theme.colors.input.background} !important`};
} }
width: ${(props) => `${props.width}px`}; width: ${(props) => `${props.width}px`};
border-radius: ${(props) => props.theme.other.input.borderRadius};
`; `;
export const StyledIcon = styled(Icon)` export const StyledIcon = styled(Icon)`
color: ${(props) => `${props.theme.primary.main} !important`}; color: ${(props) => `${props.theme.colors.primary} !important`};
`; `;
export const ContextMenuWindow = styled.div<{ export const ContextMenuWindow = styled.div<{
@ -155,6 +361,7 @@ export const ContextMenuWindow = styled.div<{
width: number; width: number;
hasTitle: boolean; hasTitle: boolean;
}>` }>`
background: ${(props) => props.theme.colors.contextMenu.background};
position: absolute; position: absolute;
top: ${(props) => `${props.yPos}px`}; top: ${(props) => `${props.yPos}px`};
left: ${(props) => `${props.xPos}px`}; left: ${(props) => `${props.xPos}px`};
@ -166,12 +373,22 @@ export const ContextMenuWindow = styled.div<{
overflow: hidden; overflow: hidden;
overflow-x: hidden; overflow-x: hidden;
font-size: smaller; font-size: smaller;
background: ${(props) => props.theme.primary.sideBar};
border: 1px #3c4043 solid; border: 1px #3c4043 solid;
z-index: 2000; z-index: 2000;
`; `;
export const StyledContextMenuButton = styled(Button)` export const StyledContextMenuButton = styled(Button)`
color: ${(props) =>
props.disabled
? props.theme.colors.contextMenu.colorDisabled
: props.theme.colors.contextMenu.color} !important;
&:hover,
&:active,
&:focus {
color: ${(props) => props.theme.colors.contextMenu.color};
background: ${(props) => props.theme.colors.contextMenu.backgroundHover};
}
text-align: left; text-align: left;
margin: 0px !important; margin: 0px !important;
border-radius: 0px !important; border-radius: 0px !important;
@ -182,14 +399,23 @@ export const ContextMenuDivider = styled.hr`
`; `;
export const ContextMenuTitle = styled.div` export const ContextMenuTitle = styled.div`
color: ${(props) => props.theme.primary.text}; color: ${(props) => props.theme.colors.layout.page.color};
margin: 5px 0 5px 5px; margin: 5px 0 5px 5px;
user-select: none; user-select: none;
`; `;
export const ContextMenuPopover = styled(Popover)`
color: ${(props) => props.theme.colors.contextMenu.color} !important;
background: ${(props) => props.theme.colors.contextMenu.background};
position: absolute;
z-index: 1000;
`;
export const StyledPopover = styled(Popover)` export const StyledPopover = styled(Popover)`
color: ${(props) => props.theme.colors.popover.color};
background: ${(props) => props.theme.colors.popover.background};
position: absolute; position: absolute;
z-index: 2; z-index: 1000;
`; `;
export const SectionTitleWrapper = styled.div` export const SectionTitleWrapper = styled.div`
@ -199,26 +425,62 @@ export const SectionTitleWrapper = styled.div`
export const SectionTitle = styled.a` export const SectionTitle = styled.a`
font-size: 20px; font-size: 20px;
color: ${(props) => props.theme.primary.text}; color: ${(props) => props.theme.colors.layout.page.color};
cursor: ${(props) => (props.onClick ? 'pointer' : 'default')};
&:hover { &:hover {
cursor: ${(props) => (props.onClick ? 'pointer' : 'default')};
text-decoration: none; text-decoration: none;
color: ${(props) => (!props.onClick ? props.theme.primary.text : undefined)}; color: ${(props) =>
!props.onClick ? props.theme.colors.layout.page.color : props.theme.colors.primary};
} }
&:active, &:active,
&:focus { &:focus {
text-decoration: none; text-decoration: none;
color: ${(props) => (!props.onClick ? props.theme.primary.text : undefined)}; color: ${(props) =>
!props.onClick ? props.theme.colors.layout.page.color : props.theme.colors.primary};
} }
`; `;
export const StyledLink = styled.a` export const StyledLink = styled.a`
color: ${(props) => props.theme.primary.text}; color: ${(props) => props.theme.colors.layout.page.color};
cursor: pointer; cursor: pointer;
&:hover {
text-decoration: none;
color: ${(props) => props.theme.colors.button.link.colorHover};
}
`; `;
export const StyledPanel = styled(Panel)` export const StyledPanel = styled(Panel)`
color: ${(props) => props.theme.primary.text}; color: ${(props) => props.theme.colors.layout.page.color};
border-radius: ${(props) => props.theme.other.panel.borderRadius};
`;
export const StyledTagPicker = styled(TagPicker)`
border: 1px #3c3f43 solid !important;
border-radius: ${(props) => props.theme.other.input.borderRadius} !important;
&:hover,
&:active,
&:focus {
border-color: ${(props) => props.theme.colors.primary} !important;
}
.rs-picker-input {
&:hover {
border-color: ${(props) => props.theme.colors.primary};
}
}
.rs-tag {
color: ${(props) => props.theme.colors.tag.text};
background: ${(props) => props.theme.colors.tag.background};
border-radius: ${(props) => props.theme.other.tag.borderRadius};
}
`;
export const StyledTag = styled(Tag)`
color: ${(props) => props.theme.colors.tag.text};
background: ${(props) => props.theme.colors.tag.background};
border-radius: ${(props) => props.theme.other.tag.borderRadius};
`; `;

4
src/components/viewtypes/ListViewTable.tsx

@ -45,13 +45,13 @@ import { setActive } from '../../redux/albumSlice';
const StyledTable = styled(Table)<{ rowHeight: number; $isDragging: boolean }>` const StyledTable = styled(Table)<{ rowHeight: number; $isDragging: boolean }>`
.rs-table-row.selected { .rs-table-row.selected {
background: ${(props) => props.theme.primary.rowSelected} !important; background: ${(props) => props.theme.colors.table.selectedRow} !important;
// Resolve bug from rsuite-table where certain scrollpoints show a horizontal border // Resolve bug from rsuite-table where certain scrollpoints show a horizontal border
height: ${(props) => `${props.rowHeight + 1}px !important`}; height: ${(props) => `${props.rowHeight + 1}px !important`};
} }
.rs-table-row.dragover { .rs-table-row.dragover {
box-shadow: ${(props) => `inset 0px 5px 0px -3px ${props.theme.primary.main}`}; box-shadow: ${(props) => `inset 0px 5px 0px -3px ${props.theme.colors.primary}`};
} }
.rs-table-row, .rs-table-row,

18
src/components/viewtypes/styled.tsx

@ -12,14 +12,16 @@ export const RsuiteLinkButton = styled(Button)<{
overflow: hidden; overflow: hidden;
color: ${(props) => color: ${(props) =>
props.playing === 'true' props.playing === 'true'
? props.theme.primary.main ? props.theme.colors.primary
: props.subtitle === 'true' : props.subtitle === 'true'
? props.theme.secondary.text ? props.theme.colors.layout.page.colorSecondary
: props.theme.primary.text}; : props.theme.colors.layout.page.color};
&:hover { &:hover,
&:active,
&:focus {
text-decoration: underline; text-decoration: underline;
color: ${(props) => props.theme.primary.text}; color: ${(props) => props.theme.colors.layout.page.color} !important;
cursor: pointer; cursor: pointer;
} }
`; `;
@ -30,14 +32,14 @@ export const TableCellWrapper = styled.div<{
dragover?: string; dragover?: string;
dragfield?: string; dragfield?: string;
}>` }>`
color: ${(props) => (props.playing === 'true' ? props.theme.primary.main : undefined)}; color: ${(props) => (props.playing === 'true' ? props.theme.colors.primary : undefined)};
line-height: ${(props) => (props.height ? `${props.height}px` : undefined)}; line-height: ${(props) => (props.height ? `${props.height}px` : undefined)};
cursor: ${(props) => cursor: ${(props) =>
props.dragover === 'true' ? 'grabbing' : props.dragfield === 'true' ? 'grab' : 'default'}; props.dragover === 'true' ? 'grabbing' : props.dragfield === 'true' ? 'grab' : 'default'};
`; `;
export const CombinedTitleTextWrapper = styled.span<{ playing: string }>` export const CombinedTitleTextWrapper = styled.span<{ playing: string }>`
color: ${(props) => (props.playing === 'true' ? props.theme.primary.main : undefined)}; color: ${(props) => (props.playing === 'true' ? props.theme.colors.primary : undefined)};
`; `;
export const StyledTableHeaderCell = styled(Table.HeaderCell)` export const StyledTableHeaderCell = styled(Table.HeaderCell)`
@ -55,6 +57,6 @@ export const StyledTableHeaderCell = styled(Table.HeaderCell)`
.rs-table-cell-header-icon-sort-desc::after, .rs-table-cell-header-icon-sort-desc::after,
.rs-table-cell-header-icon-sort-asc::after { .rs-table-cell-header-icon-sort-asc::after {
color: ${(props) => `${props.theme.primary.main} !important`}; color: ${(props) => `${props.theme.colors.primary} !important`};
} }
`; `;

6
src/styles/custom-theme.less

@ -60,3 +60,9 @@
// Tooltip // Tooltip
@tooltip-bg: transparent; @tooltip-bg: transparent;
@tooltip-color: transparent; @tooltip-color: transparent;
// Popover
@popover-arrow-width: 0px;
@input-border-focus: #3c3f43;
@input-border: undefined;

323
src/styles/styledTheme.ts

@ -1,61 +1,286 @@
export const defaultDark = { export const defaultDark = {
all: { label: 'Default Dark',
fonts: { value: 'defaultDark',
pageTitleFontSize: '30px', fonts: {
pageFontSize: '14px', size: {
panelHeaderFontSize: '20px', page: '14px',
pageTitle: '40px',
panelTitle: '20px',
button: '14px',
}, },
}, },
primary: { colors: {
main: '#2196F3', primary: '#2196F3',
background: '#181a1f', layout: {
titleBar: '#101010', page: {
titleText: '#FFFFFF', color: '#D8D8D8',
playerBar: '#101010', colorSecondary: '#888e94',
sideBar: '#101010', background: '#181A1F',
text: '#D8D8D8', },
rowSelected: '#4D5156', playerBar: {
playerBarText: '#e9ebf0', color: '#D8D8D8',
playerBarButtons: 'rgba(240, 240, 240, 0.8)', colorSecondary: '#888e94',
playerBarButtonsHover: '#ffffff', background: '#101010',
inputBackground: '#1A1D24', button: {
spinner: '#FFFFFF', color: 'rgba(240, 240, 240, 0.8)',
spinnerBackground: 'rgba(233, 235, 240, 0.3)', colorHover: '#FFFFFF',
sliderBackground: '#888E94', },
coverArtShadow: '#000000', },
sideBar: {
background: '#101010',
button: {
color: '#D8D8D8',
colorHover: '#FFFFFF',
},
},
titleBar: {
color: '#FFFFFF',
background: '#101010',
},
},
button: {
default: {
color: '#D8D8D8',
colorHover: '#FFFFFF',
background: '#292D33',
backgroundHover: '#3C3F43',
},
primary: {
color: '#FFFFFF',
colorHover: '#FFFFFF',
backgroundHover: '#3B89EC',
},
subtle: {
color: '#D8D8D8',
colorHover: '#D8D8D8',
backgroundHover: 'transparent',
},
link: {
color: '#2196F3',
colorHover: '#3B89EC',
},
},
card: {
overlayButton: {
color: '#FFFFFF',
background: '#000000',
opacity: 0.8,
},
},
contextMenu: {
color: '#D8D8D8',
colorDisabled: '#6A6F76',
background: '#1E2125',
backgroundHover: '#292D33',
},
input: {
color: '#D8D8D8',
background: '#25292E',
backgroundHover: '#353A45',
backgroundActive: 'rgba(240, 240, 240, .2)',
},
nav: {
color: '#D8D8D8',
},
popover: {
color: '#D8D8D8',
background: '#1E2125',
},
slider: {
background: '#3C3F43',
progressBar: '#888E94',
},
spinner: {
background: 'rgba(233, 235, 240, 0.3)',
foreground: '#2196F3',
},
table: {
selectedRow: '#4D5156',
},
tag: {
background: '#3C3F43',
text: '#E2E4E9',
},
tooltip: {
color: '#D8D8D8',
background: '#1E2125',
},
}, },
secondary: { other: {
main: '#292D33', button: {
text: '#888e94', borderRadius: '0px',
playerBarText: '#888e94', },
coverArtFilter: 'drop-shadow(0px 3px 5px #000000)',
card: {
borderRadius: '0px',
},
input: {
borderRadius: '0px',
},
miniPlayer: {
height: '450px',
opacity: 0.95,
},
panel: {
borderRadius: '0px',
},
playerBar: {
borderTop: '1px solid rgba(240, 240, 240, .15)',
borderRight: 'none',
borderBottom: 'none',
borderLeft: 'none',
filter: 'none',
},
tag: {
borderRadius: '0px',
},
tooltip: {
border: '1px #3c3f43 solid',
borderRadius: '0px',
},
}, },
}; };
export const defaultLight = { export const defaultLight = {
all: { label: 'Default Light',
...defaultDark.all, value: 'defaultLight',
fonts: {
size: {
page: '14px',
pageTitle: '30px',
panelTitle: '20px',
},
}, },
primary: { colors: {
main: '#285DA0', primary: '#285DA0',
background: '#EBEEF5', layout: {
titleBar: '#21252B', page: {
titleText: '#FFFFFF', color: '#000000',
playerBar: '#21252B', colorSecondary: '#888e94',
sideBar: '#21252B', background: '#FFFFFF',
text: '#000000', },
rowSelected: '#BABCC2', playerBar: {
playerBarText: '#EBEEF5', color: '#FFFFFF',
playerBarButtons: 'rgba(240, 240, 240, 0.8)', colorSecondary: '#888e94',
playerBarButtonsHover: '#FFFFFF', background: '#161B22',
inputBackground: '#F4F7FF', button: {
spinner: '#000000', color: 'rgba(240, 240, 240, 0.8)',
spinnerBackground: 'rgba(0, 0, 0, 0.3)', colorHover: '#FFFFFF',
sliderBackground: '#888e94', },
coverArtShadow: '#000000', },
sideBar: {
background: '#161B22',
button: {
color: '#D8D8D8',
colorHover: '#FFFFFF',
},
},
titleBar: {
color: '#FFFFFF',
background: '#161B22',
},
},
button: {
default: {
color: '#575757',
colorHover: '#000000',
background: '#DFDFE2',
backgroundHover: '#D2D2D6',
},
primary: {
color: '#FFFFFF',
colorHover: '#FFFFFF',
backgroundHover: '#347AD3',
},
subtle: {
color: '#575757',
colorHover: '#575757',
backgroundHover: 'transparent',
},
link: {
color: '#575757',
colorHover: '#575757',
},
},
card: {
overlayButton: {
color: '#FFFFFF',
colorHover: '#FFFFFF',
background: '#000000',
backgroundHover: '#285DA0',
opacity: 0.8,
},
},
contextMenu: {
color: '#575757',
colorDisabled: '#BABABA',
background: '#FFFFFF',
backgroundHover: '#D2D2D6',
},
input: {
color: '#000000',
background: '#FFFFFF',
backgroundHover: '#E5E5EA',
backgroundActive: 'rgba(0, 0, 0, .2)',
},
nav: {
color: '#000000',
},
popover: {
color: '#000000',
background: '#FFFFFF',
},
slider: {
background: '#3C3F43',
progressBar: '#888E94',
},
spinner: {
background: 'rgba(0, 0, 0, 0.3)',
foreground: '#285DA0',
},
table: {
selectedRow: '#CCCCCC',
},
tag: {
background: '#DFDFE2',
text: '#000000',
},
tooltip: {
color: '#000000',
background: '#FFFFFF',
},
}, },
secondary: { other: {
main: '#292D33', button: {
text: '#888e94', borderRadius: '0px',
playerBarText: '#888e94', },
coverArtFilter: 'drop-shadow(0px 3px 5px #000000)',
card: {
borderRadius: '0px',
},
input: {
borderRadius: '0px',
},
miniPlayer: {
height: '450px',
opacity: 0.95,
},
panel: {
borderRadius: '0px',
},
playerBar: {
borderTop: '1px solid rgba(240, 240, 240, .15)',
borderRight: 'none',
borderBottom: 'none',
borderLeft: 'none',
filter: 'none',
},
tag: {
borderRadius: '0px',
},
tooltip: {
border: '1px #3c3f43 solid',
borderRadius: '0px',
},
}, },
}; };

Loading…
Cancel
Save