diff --git a/src/components/AppShelf/AppShelf.tsx b/src/components/AppShelf/AppShelf.tsx index ede6a307f..8a6d20807 100644 --- a/src/components/AppShelf/AppShelf.tsx +++ b/src/components/AppShelf/AppShelf.tsx @@ -1,5 +1,5 @@ import React, { useState } from 'react'; -import { Accordion, createStyles, Grid, Group } from '@mantine/core'; +import { Accordion, createStyles, Grid, Group, Paper, useMantineColorScheme } from '@mantine/core'; import { closestCenter, DndContext, @@ -42,6 +42,7 @@ const AppShelf = (props: any) => { }); const [activeId, setActiveId] = useState(null); const { config, setConfig } = useConfig(); + const { colorScheme } = useMantineColorScheme(); const sensors = useSensors( useSensor(TouchSensor, { @@ -164,7 +165,16 @@ const AppShelf = (props: any) => { ) : null} - + + + diff --git a/src/components/AppShelf/AppShelfItem.tsx b/src/components/AppShelf/AppShelfItem.tsx index 9af38f6d3..8ad389a19 100644 --- a/src/components/AppShelf/AppShelfItem.tsx +++ b/src/components/AppShelf/AppShelfItem.tsx @@ -1,4 +1,13 @@ -import { Text, Card, Anchor, AspectRatio, Image, Center, createStyles } from '@mantine/core'; +import { + Text, + Card, + Anchor, + AspectRatio, + Image, + Center, + createStyles, + useMantineColorScheme, +} from '@mantine/core'; import { motion } from 'framer-motion'; import { useState } from 'react'; import { useSortable } from '@dnd-kit/sortable'; @@ -6,6 +15,7 @@ import { CSS } from '@dnd-kit/utilities'; import { serviceItem } from '../../tools/types'; import PingComponent from '../modules/ping/PingModule'; import AppShelfMenu from './AppShelfMenu'; +import { useConfig } from '../../tools/state'; const useStyles = createStyles((theme) => ({ item: { @@ -41,6 +51,8 @@ export function SortableAppShelfItem(props: any) { export function AppShelfItem(props: any) { const { service }: { service: serviceItem } = props; const [hovering, setHovering] = useState(false); + const { config } = useConfig(); + const { colorScheme } = useMantineColorScheme(); const { classes } = useStyles(); return ( - + ({ root: { @@ -29,6 +30,7 @@ const useStyles = createStyles((theme) => ({ })); export function ColorSchemeSwitch() { + const { config } = useConfig(); const { colorScheme, toggleColorScheme } = useMantineColorScheme(); const { classes, cx } = useStyles(); diff --git a/src/components/Settings/AdvancedSettings.tsx b/src/components/Settings/AdvancedSettings.tsx index 7b678f10f..b00a11933 100644 --- a/src/components/Settings/AdvancedSettings.tsx +++ b/src/components/Settings/AdvancedSettings.tsx @@ -1,6 +1,9 @@ import { TextInput, Group, Button } from '@mantine/core'; import { useForm } from '@mantine/form'; import { useConfig } from '../../tools/state'; +import { ColorSelector } from './ColorSelector'; +import { OpacitySelector } from './OpacitySelector'; +import { ShadeSelector } from './ShadeSelector'; export default function TitleChanger() { const { config, setConfig } = useConfig(); @@ -10,10 +13,16 @@ export default function TitleChanger() { title: config.settings.title, logo: config.settings.logo, favicon: config.settings.favicon, + background: config.settings.background, }, }); - const saveChanges = (values: { title?: string; logo?: string; favicon?: string }) => { + const saveChanges = (values: { + title?: string; + logo?: string; + favicon?: string; + background?: string; + }) => { setConfig({ ...config, settings: { @@ -21,6 +30,7 @@ export default function TitleChanger() { title: values.title, logo: values.logo, favicon: values.favicon, + background: values.background, }, }); }; @@ -36,9 +46,18 @@ export default function TitleChanger() { placeholder="/favicon.svg" {...form.getInputProps('favicon')} /> + + + + + ); } diff --git a/src/components/Settings/ColorSelector.tsx b/src/components/Settings/ColorSelector.tsx new file mode 100644 index 000000000..e7f175b3d --- /dev/null +++ b/src/components/Settings/ColorSelector.tsx @@ -0,0 +1,96 @@ +import React, { useState } from 'react'; +import { ColorSwatch, Group, Popover, Text, useMantineTheme } from '@mantine/core'; +import { useConfig } from '../../tools/state'; +import { useColorTheme } from '../../tools/color'; + +interface ColorControlProps { + type: string; +} + +export function ColorSelector({ type }: ColorControlProps) { + const { config, setConfig } = useConfig(); + const [opened, setOpened] = useState(false); + + const { primaryColor, secondaryColor, setPrimaryColor, setSecondaryColor } = useColorTheme(); + + const theme = useMantineTheme(); + const colors = Object.keys(theme.colors).map((color) => ({ + swatch: theme.colors[color][6], + color, + })); + + const configColor = type === 'primary' ? primaryColor : secondaryColor; + + const setConfigColor = (color: string) => { + if (type === 'primary') { + setPrimaryColor(color); + setConfig({ + ...config, + settings: { + ...config.settings, + primaryColor: color, + }, + }); + } else { + setSecondaryColor(color); + setConfig({ + ...config, + settings: { + ...config.settings, + secondaryColor: color, + }, + }); + } + }; + + const swatches = colors.map(({ color, swatch }) => ( + setConfigColor(color)} + key={color} + color={swatch} + size={22} + style={{ color: theme.white, cursor: 'pointer' }} + /> + )); + + return ( + + setOpened(false)} + transitionDuration={0} + target={ + setOpened((o) => !o)} + size={22} + style={{ display: 'block', cursor: 'pointer' }} + /> + } + styles={{ + root: { + marginRight: theme.spacing.xs, + }, + body: { + width: 152, + backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[8] : theme.white, + }, + arrow: { + backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[8] : theme.white, + }, + }} + position="bottom" + placement="end" + withArrow + arrowSize={3} + > + {swatches} + + {type[0].toUpperCase() + type.slice(1)} color + + ); +} diff --git a/src/components/Settings/CommonSettings.tsx b/src/components/Settings/CommonSettings.tsx index c61b6d605..ee482aa43 100644 --- a/src/components/Settings/CommonSettings.tsx +++ b/src/components/Settings/CommonSettings.tsx @@ -67,8 +67,9 @@ export default function CommonSettings(args: any) { /> )} - + + module); return ( - {modules.map((module) => ( - { - setConfig({ - ...config, - modules: { - ...config.modules, - [module.title]: { - ...config.modules?.[module.title], - enabled: e.currentTarget.checked, + Module enabler + + {modules.map((module) => ( + { + setConfig({ + ...config, + modules: { + ...config.modules, + [module.title]: { + ...config.modules?.[module.title], + enabled: e.currentTarget.checked, + }, }, - }, - }); - }} - /> - ))} + }); + }} + /> + ))} + ); } diff --git a/src/components/Settings/OpacitySelector.tsx b/src/components/Settings/OpacitySelector.tsx new file mode 100644 index 000000000..f94225cd8 --- /dev/null +++ b/src/components/Settings/OpacitySelector.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { Group, Text, Slider } from '@mantine/core'; +import { useConfig } from '../../tools/state'; + +export function OpacitySelector() { + const { config, setConfig } = useConfig(); + + const MARKS = [ + { value: 10, label: '10' }, + { value: 20, label: '20' }, + { value: 30, label: '30' }, + { value: 40, label: '40' }, + { value: 50, label: '50' }, + { value: 60, label: '60' }, + { value: 70, label: '70' }, + { value: 80, label: '80' }, + { value: 90, label: '90' }, + { value: 100, label: '100' }, + ]; + + const setConfigOpacity = (opacity: number) => { + setConfig({ + ...config, + settings: { + ...config.settings, + appOpacity: opacity, + }, + }); + }; + + return ( + + App Opacity + setConfigOpacity(value)} + /> + + ); +} diff --git a/src/components/Settings/SettingsMenu.tsx b/src/components/Settings/SettingsMenu.tsx index ea6e9b4a9..78b968634 100644 --- a/src/components/Settings/SettingsMenu.tsx +++ b/src/components/Settings/SettingsMenu.tsx @@ -11,7 +11,7 @@ function SettingsMenu(props: any) { - + diff --git a/src/components/Settings/ShadeSelector.tsx b/src/components/Settings/ShadeSelector.tsx new file mode 100644 index 000000000..ebd55e84d --- /dev/null +++ b/src/components/Settings/ShadeSelector.tsx @@ -0,0 +1,97 @@ +import React, { useState } from 'react'; +import { ColorSwatch, Group, Popover, Text, useMantineTheme, MantineTheme } from '@mantine/core'; +import { useConfig } from '../../tools/state'; +import { useColorTheme } from '../../tools/color'; + +export function ShadeSelector() { + const { config, setConfig } = useConfig(); + const [opened, setOpened] = useState(false); + + const { primaryColor, secondaryColor, primaryShade, setPrimaryShade } = useColorTheme(); + + const theme = useMantineTheme(); + const primaryShades = theme.colors[primaryColor].map((s, i) => ({ + swatch: theme.colors[primaryColor][i], + shade: i as MantineTheme['primaryShade'], + })); + const secondaryShades = theme.colors[secondaryColor].map((s, i) => ({ + swatch: theme.colors[secondaryColor][i], + shade: i as MantineTheme['primaryShade'], + })); + + const setConfigShade = (shade: MantineTheme['primaryShade']) => { + setPrimaryShade(shade); + setConfig({ + ...config, + settings: { + ...config.settings, + primaryShade: shade, + }, + }); + }; + + const primarySwatches = primaryShades.map(({ swatch, shade }) => ( + setConfigShade(shade)} + key={Number(shade)} + color={swatch} + size={22} + style={{ color: theme.white, cursor: 'pointer' }} + /> + )); + + const secondarySwatches = secondaryShades.map(({ swatch, shade }) => ( + setConfigShade(shade)} + key={Number(shade)} + color={swatch} + size={22} + style={{ color: theme.white, cursor: 'pointer' }} + /> + )); + + return ( + + setOpened(false)} + transitionDuration={0} + target={ + setOpened((o) => !o)} + size={22} + style={{ display: 'block', cursor: 'pointer' }} + /> + } + styles={{ + root: { + marginRight: theme.spacing.xs, + }, + body: { + backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[8] : theme.white, + }, + arrow: { + backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[8] : theme.white, + }, + }} + position="bottom" + placement="end" + withArrow + arrowSize={3} + > + + {primarySwatches} + {secondarySwatches} + + + Shade + + ); +} diff --git a/src/components/layout/Aside.tsx b/src/components/layout/Aside.tsx index 8fafc5708..8ed088c4b 100644 --- a/src/components/layout/Aside.tsx +++ b/src/components/layout/Aside.tsx @@ -28,19 +28,22 @@ export default function Aside(props: any) { className={cx(classes.hide)} style={{ border: 'none', + background: 'none', }} width={{ base: 'auto', }} > - {matches && ( - - - - - - - )} + <> + {matches && ( + + + + + + + )} + ); } diff --git a/src/components/layout/Background.tsx b/src/components/layout/Background.tsx new file mode 100644 index 000000000..741bf9389 --- /dev/null +++ b/src/components/layout/Background.tsx @@ -0,0 +1,20 @@ +import { Global } from '@mantine/core'; +import { useConfig } from '../../tools/state'; + +export function Background() { + const { config } = useConfig(); + + return ( + + ); +} diff --git a/src/components/layout/Header.tsx b/src/components/layout/Header.tsx index 1cfb218a5..20374f609 100644 --- a/src/components/layout/Header.tsx +++ b/src/components/layout/Header.tsx @@ -41,7 +41,7 @@ export function Header(props: any) { return ( - + diff --git a/src/components/layout/Layout.tsx b/src/components/layout/Layout.tsx index d9ccbccb7..975e51776 100644 --- a/src/components/layout/Layout.tsx +++ b/src/components/layout/Layout.tsx @@ -3,6 +3,7 @@ import { Header } from './Header'; import { Footer } from './Footer'; import Aside from './Aside'; import { HeaderConfig } from './HeaderConfig'; +import { Background } from './Background'; const useStyles = createStyles((theme) => ({ main: {}, @@ -13,6 +14,7 @@ export default function Layout({ children, style }: any) { return ( } header={
} footer={