Fixes for theme editor logic

This commit is contained in:
Sean Edmunds
2026-02-02 14:13:55 -06:00
parent 5cc3b8bf6a
commit b92ca8a8fe
3 changed files with 381 additions and 363 deletions

View File

@@ -68,14 +68,15 @@ export function ThemeModal({
randomEnabled: 1, randomEnabled: 1,
textShadowsEnabled: 0, textShadowsEnabled: 0,
appShadowsEnabled: 0, appShadowsEnabled: 0,
themeFont: null,
}); });
}; };
const handleSaveTheme = () => { const handleSaveTheme = () => {
mutateAsync({ mutateAsync({
method: selectedTheme?.id === undefined ? 'POST' : 'PATCH', method: designerTheme?.id === undefined ? 'POST' : 'PATCH',
endpoint: themesEndpoint, endpoint: themesEndpoint,
body: selectedTheme, body: designerTheme,
}); });
handleClose(); handleClose();
}; };
@@ -86,6 +87,7 @@ export function ThemeModal({
setDefaultTheme(selectedTheme); setDefaultTheme(selectedTheme);
} }
}, [selectedTheme]); }, [selectedTheme]);
return ( return (
<Modal.Root <Modal.Root
size={'75%'} size={'75%'}
@@ -109,17 +111,17 @@ export function ThemeModal({
<TextInput <TextInput
className={textInputClasses.default} className={textInputClasses.default}
classNames={textInputClasses} classNames={textInputClasses}
value={selectedTheme.name} value={designerTheme.name}
onChange={(e) => onChange={(e) =>
setSelectedTheme({ ...selectedTheme, name: e.target.value }) setDesignerTheme({ ...designerTheme, name: e.target.value })
} }
styles={{ styles={{
label: { label: {
color: 'var(--mantine-color-primary-0)', color: 'var(--mantine-color-primary-0)',
}, },
input: { input: {
color: selectedTheme.primaryColor, color: designerTheme.primaryColor,
backgroundColor: selectedTheme.backgroundColor, backgroundColor: designerTheme.backgroundColor,
border: 'none', border: 'none',
borderBottom: '1px solid var(--mantine-color-primary-0)', borderBottom: '1px solid var(--mantine-color-primary-0)',
}, },
@@ -217,22 +219,23 @@ export function ThemeModal({
setIsNew={setIsNew} setIsNew={setIsNew}
duplicate={duplicate} duplicate={duplicate}
setDuplicate={setDuplicate} setDuplicate={setDuplicate}
selectedTheme={selectedTheme} designerTheme={designerTheme}
setDesignerTheme={setDesignerTheme}
setSelectedTheme={setSelectedTheme} setSelectedTheme={setSelectedTheme}
setDefaultTheme={setDefaultTheme}
/> />
) : ( ) : (
<ThemeEditor <ThemeEditor
designerTheme={designerTheme}
setDesignerTheme={setDesignerTheme}
selectedTheme={selectedTheme}
closePicker={closePicker}
handleSaveTheme={handleSaveTheme}
backgroundList={backgroundList} backgroundList={backgroundList}
defaultTheme={defaultTheme} defaultTheme={defaultTheme}
selectedTheme={selectedTheme}
designerTheme={designerTheme}
setDesignerTheme={setDesignerTheme}
openPicker={openPicker} openPicker={openPicker}
pickerOpen={pickerOpen} pickerOpen={pickerOpen}
setSelectedTheme={setSelectedTheme} closePicker={closePicker}
handleClose={handleClose} handleClose={handleClose}
handleSaveTheme={handleSaveTheme}
duplicate={duplicate} duplicate={duplicate}
setDuplicate={setDuplicate} setDuplicate={setDuplicate}
/> />

View File

@@ -13,15 +13,19 @@ export function NewThemeStarter({
setIsNew, setIsNew,
duplicate, duplicate,
setDuplicate, setDuplicate,
selectedTheme, designerTheme,
setDesignerTheme,
setSelectedTheme, setSelectedTheme,
setDefaultTheme,
}: { }: {
allThemes: Theme[]; allThemes: Theme[];
setIsNew: (isNew: boolean) => void; setIsNew: (isNew: boolean) => void;
duplicate: boolean; duplicate: boolean;
setDuplicate: (duplicate: boolean) => void; setDuplicate: (duplicate: boolean) => void;
selectedTheme: Theme; designerTheme: Theme;
setDesignerTheme: (selectedTheme: Theme) => void;
setSelectedTheme: (selectedTheme: Theme) => void; setSelectedTheme: (selectedTheme: Theme) => void;
setDefaultTheme: (selectedTheme: Theme) => void;
}) { }) {
const [pressed, setPressed] = useState<any>(false); const [pressed, setPressed] = useState<any>(false);
const handlers = useLongPress(() => { const handlers = useLongPress(() => {
@@ -55,7 +59,8 @@ export function NewThemeStarter({
{...handlers} {...handlers}
key={miniTheme.id} key={miniTheme.id}
onClick={() => { onClick={() => {
!pressed && setSelectedTheme(miniTheme); !pressed && setDesignerTheme(miniTheme);
!pressed && setDefaultTheme(miniTheme);
setPressed(false); setPressed(false);
}} }}
> >
@@ -79,13 +84,14 @@ export function NewThemeStarter({
borderRadius: '8px', borderRadius: '8px',
}} }}
> >
<ThemeMiniCard miniTheme={selectedTheme} /> <ThemeMiniCard miniTheme={designerTheme} />
<Button <Button
disabled={selectedTheme.name === 'default'} disabled={designerTheme.name === 'default'}
onClick={() => { onClick={() => {
setSelectedTheme({ setDesignerTheme({
...selectedTheme, ...designerTheme,
name: `${selectedTheme.name} copy`, id: undefined,
name: `${designerTheme.name} copy`,
}); });
setIsNew(false); setIsNew(false);
}} }}
@@ -98,7 +104,7 @@ export function NewThemeStarter({
styles={{ label: { color: 'black' } }} styles={{ label: { color: 'black' } }}
onClick={() => { onClick={() => {
setDuplicate(false); setDuplicate(false);
setSelectedTheme({ setDesignerTheme({
id: undefined, id: undefined,
name: 'Default', name: 'Default',
backgroundImage: 'none', backgroundImage: 'none',
@@ -110,6 +116,7 @@ export function NewThemeStarter({
randomEnabled: 1, randomEnabled: 1,
textShadowsEnabled: 0, textShadowsEnabled: 0,
appShadowsEnabled: 0, appShadowsEnabled: 0,
themeFont: null,
}); });
}} }}
> >
@@ -149,10 +156,11 @@ export function NewThemeStarter({
randomEnabled: 1, randomEnabled: 1,
textShadowsEnabled: 0, textShadowsEnabled: 0,
appShadowsEnabled: 0, appShadowsEnabled: 0,
themeFont: null,
}} }}
allThemes={[]} allThemes={[]}
themePlaylists={[]} themePlaylists={[]}
setSelectedTheme={setSelectedTheme} setSelectedTheme={setDesignerTheme}
openThemeModal={() => {}} openThemeModal={() => {}}
enabled={false} enabled={false}
/> />
@@ -209,7 +217,7 @@ export function NewThemeStarter({
cardTheme={randomTheme} cardTheme={randomTheme}
allThemes={[]} allThemes={[]}
themePlaylists={[]} themePlaylists={[]}
setSelectedTheme={setSelectedTheme} setSelectedTheme={setDesignerTheme}
openThemeModal={() => {}} openThemeModal={() => {}}
enabled={false} enabled={false}
/> />
@@ -261,6 +269,7 @@ export function NewThemeStarter({
}} }}
onClick={() => { onClick={() => {
setDuplicate(true); setDuplicate(true);
setDesignerTheme(randomTheme);
setSelectedTheme(randomTheme); setSelectedTheme(randomTheme);
}} }}
> >

View File

@@ -37,10 +37,9 @@ import { ThemeModalContent } from './ThemeModalContent';
export function ThemeEditor({ export function ThemeEditor({
backgroundList, backgroundList,
defaultTheme, defaultTheme,
selectedTheme,
designerTheme, designerTheme,
setDesignerTheme, setDesignerTheme,
selectedTheme,
setSelectedTheme,
pickerOpen, pickerOpen,
openPicker, openPicker,
closePicker, closePicker,
@@ -51,10 +50,9 @@ export function ThemeEditor({
}: { }: {
backgroundList: string[]; backgroundList: string[];
defaultTheme: Theme; defaultTheme: Theme;
selectedTheme: Theme;
designerTheme: Theme; designerTheme: Theme;
setDesignerTheme: (theme: Theme) => void; setDesignerTheme: (theme: Theme) => void;
selectedTheme: Theme;
setSelectedTheme: (theme: Theme) => void;
pickerOpen: boolean; pickerOpen: boolean;
openPicker: () => void; openPicker: () => void;
closePicker: () => void; closePicker: () => void;
@@ -78,27 +76,6 @@ export function ThemeEditor({
const filtered = backgroundList.filter((item: string) => const filtered = backgroundList.filter((item: string) =>
item.toLowerCase().includes(query.toLowerCase()), item.toLowerCase().includes(query.toLowerCase()),
); );
useEffect(() => {
if (font) {
setCustomFontEnabled(true);
setSelectedTheme({
...selectedTheme,
themeFont: font,
});
} else {
setCustomFontEnabled(false);
setSelectedTheme({
...selectedTheme,
themeFont: null,
});
}
}, [font]);
useEffect(() => {
if (selectedTheme) {
setCustomFontEnabled(selectedTheme.themeFont !== null);
setFont(selectedTheme.themeFont);
}
}, [selectedTheme]);
const items = filtered.map((item: string, index: number) => { const items = filtered.map((item: string, index: number) => {
return ( return (
<UnstyledButton <UnstyledButton
@@ -110,7 +87,7 @@ export function ThemeEditor({
background: background:
item !== 'none' && item !== 'None' item !== 'none' && item !== 'None'
? index === hovered ? index === hovered
? `linear-gradient(${selectedTheme.backgroundColor}88, ${selectedTheme.backgroundColor}88), no-repeat center url(../../api/backgroundImages/thumbs/${item})` ? `linear-gradient(${designerTheme.backgroundColor}88, ${designerTheme.backgroundColor}88), no-repeat center url(../../api/backgroundImages/thumbs/${item})`
: `no-repeat center url(../../api/backgroundImages/thumbs/${item})` : `no-repeat center url(../../api/backgroundImages/thumbs/${item})`
: 'repeating-linear-gradient(45deg, #33333366, #33333366 10px, #88888866 10px, #88888866 20px)', : 'repeating-linear-gradient(45deg, #33333366, #33333366 10px, #88888866 10px, #88888866 20px)',
backgroundSize: item === 'none' ? undefined : 'cover', backgroundSize: item === 'none' ? undefined : 'cover',
@@ -173,7 +150,7 @@ export function ThemeEditor({
setColorValue(colorValue || ''); setColorValue(colorValue || '');
closeBackgroundPicker(); closeBackgroundPicker();
openPicker(); openPicker();
setDesignerTheme(selectedTheme); setDesignerTheme(designerTheme);
}; };
const randomColor = (withAlpha?: boolean) => { const randomColor = (withAlpha?: boolean) => {
@@ -185,6 +162,20 @@ export function ThemeEditor({
return color; return color;
}; };
useEffect(() => {
if (font) {
setDesignerTheme({
...designerTheme,
themeFont: font,
});
}
}, [font]);
useEffect(() => {
if (selectedTheme) {
setCustomFontEnabled(designerTheme.themeFont !== null);
setFont(designerTheme.themeFont);
}
}, [selectedTheme]);
return ( return (
<> <>
<Group p={'2rem'} w={'100%'} justify='space-between'> <Group p={'2rem'} w={'100%'} justify='space-between'>
@@ -197,10 +188,10 @@ export function ThemeEditor({
> >
<ColorSwatch <ColorSwatch
onClick={() => { onClick={() => {
handleSwatchClick('primaryColor', selectedTheme.primaryColor); handleSwatchClick('primaryColor', designerTheme.primaryColor);
}} }}
color={selectedTheme.primaryColor} color={designerTheme.primaryColor}
style={{ borderColor: selectedTheme.primaryColor }} style={{ borderColor: designerTheme.primaryColor }}
className={colorSwatchClasses.themeDesignerSwatch} className={colorSwatchClasses.themeDesignerSwatch}
classNames={colorSwatchClasses} classNames={colorSwatchClasses}
top={'11.5rem'} top={'11.5rem'}
@@ -214,10 +205,10 @@ export function ThemeEditor({
> >
<ColorSwatch <ColorSwatch
onClick={() => { onClick={() => {
handleSwatchClick('accentColor', selectedTheme.accentColor); handleSwatchClick('accentColor', designerTheme.accentColor);
}} }}
color={selectedTheme.accentColor} color={designerTheme.accentColor}
style={{ borderColor: selectedTheme.primaryColor }} style={{ borderColor: designerTheme.primaryColor }}
className={colorSwatchClasses.themeDesignerSwatch} className={colorSwatchClasses.themeDesignerSwatch}
classNames={colorSwatchClasses} classNames={colorSwatchClasses}
bottom={'6rem'} bottom={'6rem'}
@@ -231,10 +222,10 @@ export function ThemeEditor({
> >
<ColorSwatch <ColorSwatch
onClick={() => { onClick={() => {
handleSwatchClick('backgroundColor', selectedTheme.backgroundColor); handleSwatchClick('backgroundColor', designerTheme.backgroundColor);
}} }}
color={selectedTheme.backgroundColor} color={designerTheme.backgroundColor}
style={{ borderColor: selectedTheme.primaryColor }} style={{ borderColor: designerTheme.primaryColor }}
className={colorSwatchClasses.themeDesignerSwatch} className={colorSwatchClasses.themeDesignerSwatch}
classNames={colorSwatchClasses} classNames={colorSwatchClasses}
bottom={'4rem'} bottom={'4rem'}
@@ -248,10 +239,10 @@ export function ThemeEditor({
> >
<ColorSwatch <ColorSwatch
onClick={() => { onClick={() => {
handleSwatchClick('borderColor', selectedTheme.borderColor); handleSwatchClick('borderColor', designerTheme.borderColor);
}} }}
color={selectedTheme.borderColor} color={designerTheme.borderColor}
style={{ borderColor: selectedTheme.primaryColor }} style={{ borderColor: designerTheme.primaryColor }}
className={colorSwatchClasses.themeDesignerSwatch} className={colorSwatchClasses.themeDesignerSwatch}
classNames={colorSwatchClasses} classNames={colorSwatchClasses}
top={'4.5rem'} top={'4.5rem'}
@@ -265,10 +256,10 @@ export function ThemeEditor({
> >
<ColorSwatch <ColorSwatch
onClick={() => { onClick={() => {
handleSwatchClick('searchColor', selectedTheme.searchColor); handleSwatchClick('searchColor', designerTheme.searchColor);
}} }}
color={selectedTheme.searchColor} color={designerTheme.searchColor}
style={{ borderColor: selectedTheme.primaryColor }} style={{ borderColor: designerTheme.primaryColor }}
className={colorSwatchClasses.themeDesignerSwatch} className={colorSwatchClasses.themeDesignerSwatch}
classNames={colorSwatchClasses} classNames={colorSwatchClasses}
top={'3.25rem'} top={'3.25rem'}
@@ -284,13 +275,13 @@ export function ThemeEditor({
onClick={() => { onClick={() => {
closePicker(); closePicker();
openBackgroundPicker(); openBackgroundPicker();
setDesignerTheme(selectedTheme); setDesignerTheme(designerTheme);
}} }}
pos={'absolute'} pos={'absolute'}
right={0} right={0}
style={{ style={{
color: selectedTheme.backgroundColor, color: designerTheme.backgroundColor,
backgroundColor: selectedTheme.primaryColor, backgroundColor: designerTheme.primaryColor,
borderRadius: 0, borderRadius: 0,
borderBottomLeftRadius: 10, borderBottomLeftRadius: 10,
borderTopRightRadius: 10, borderTopRightRadius: 10,
@@ -364,7 +355,7 @@ export function ThemeEditor({
const rc = randomColor(colorId === 'searchColor'); const rc = randomColor(colorId === 'searchColor');
setColorValue(rc); setColorValue(rc);
setDesignerTheme({ setDesignerTheme({
...selectedTheme, ...designerTheme,
[colorId]: rc, [colorId]: rc,
}); });
}} }}
@@ -374,8 +365,8 @@ export function ThemeEditor({
<Button <Button
color={'green'} color={'green'}
onClick={() => { onClick={() => {
setSelectedTheme({ setDesignerTheme({
...selectedTheme, ...designerTheme,
[colorId]: colorValue, [colorId]: colorValue,
}); });
@@ -391,7 +382,7 @@ export function ThemeEditor({
closePicker(); closePicker();
setDesignerTheme({ setDesignerTheme({
...designerTheme, ...designerTheme,
[colorId]: selectedTheme[colorId as keyof Theme], [colorId]: designerTheme[colorId as keyof Theme],
}); });
}} }}
> >
@@ -411,8 +402,8 @@ export function ThemeEditor({
color: 'var(--mantine-color-primary-0)', color: 'var(--mantine-color-primary-0)',
}, },
input: { input: {
color: selectedTheme.primaryColor, color: designerTheme.primaryColor,
backgroundColor: selectedTheme.backgroundColor, backgroundColor: designerTheme.backgroundColor,
border: 'none', border: 'none',
borderBottom: '1px solid var(--mantine-color-primary-0)', borderBottom: '1px solid var(--mantine-color-primary-0)',
}, },
@@ -478,8 +469,8 @@ export function ThemeEditor({
<Button <Button
color={'green'} color={'green'}
onClick={() => { onClick={() => {
setSelectedTheme({ setDesignerTheme({
...selectedTheme, ...designerTheme,
backgroundImage: designerTheme.backgroundImage, backgroundImage: designerTheme.backgroundImage,
}); });
closeBackgroundPicker(); closeBackgroundPicker();
@@ -494,7 +485,7 @@ export function ThemeEditor({
closeBackgroundPicker(); closeBackgroundPicker();
setDesignerTheme({ setDesignerTheme({
...designerTheme, ...designerTheme,
backgroundImage: selectedTheme.backgroundImage, backgroundImage: designerTheme.backgroundImage,
}); });
}} }}
> >
@@ -509,17 +500,17 @@ export function ThemeEditor({
<Group gap={2} w={'100%'} justify='space-between' align='center'> <Group gap={2} w={'100%'} justify='space-between' align='center'>
<Checkbox.Card <Checkbox.Card
w={'calc(50% - 1px)'} w={'calc(50% - 1px)'}
checked={selectedTheme.textShadowsEnabled === 1} checked={designerTheme.textShadowsEnabled === 1}
c={'var(--mantine-color-background-0)'} c={'var(--mantine-color-background-0)'}
onChange={(e) => { onChange={(e) => {
setSelectedTheme({ setDesignerTheme({
...selectedTheme, ...designerTheme,
textShadowsEnabled: e ? 1 : 0, textShadowsEnabled: e ? 1 : 0,
}); });
}} }}
style={{ style={{
border: 'none', border: 'none',
filter: `grayscale(${selectedTheme.textShadowsEnabled === 1 ? 0 : '75%'}) brightness(${selectedTheme.textShadowsEnabled === 1 ? '1' : '1.5'})`, filter: `grayscale(${designerTheme.textShadowsEnabled === 1 ? 0 : '75%'}) brightness(${designerTheme.textShadowsEnabled === 1 ? '1' : '1.5'})`,
transition: 'all 1s ease', transition: 'all 1s ease',
}} }}
> >
@@ -532,15 +523,15 @@ export function ThemeEditor({
w={'100%'} w={'100%'}
justify='center' justify='center'
style={{ style={{
backgroundColor: selectedTheme.backgroundColor, backgroundColor: designerTheme.backgroundColor,
}} }}
> >
<Text <Text
classNames={textClasses} classNames={textClasses}
className={textClasses.sectionTitle} className={textClasses.sectionTitle}
style={{ style={{
color: selectedTheme.accentColor, color: designerTheme.accentColor,
textShadow: `2px 2px ${selectedTheme.primaryColor}`, textShadow: `2px 2px ${designerTheme.primaryColor}`,
}} }}
> >
Shadows Shadows
@@ -550,17 +541,17 @@ export function ThemeEditor({
</Checkbox.Card> </Checkbox.Card>
<Checkbox.Card <Checkbox.Card
w={'calc(50% - 1px)'} w={'calc(50% - 1px)'}
checked={selectedTheme.appShadowsEnabled === 1} checked={designerTheme.appShadowsEnabled === 1}
c={'var(--mantine-color-background-0)'} c={'var(--mantine-color-background-0)'}
onChange={(e) => { onChange={(e) => {
setSelectedTheme({ setDesignerTheme({
...selectedTheme, ...designerTheme,
appShadowsEnabled: e ? 1 : 0, appShadowsEnabled: e ? 1 : 0,
}); });
}} }}
style={{ style={{
border: 'none', border: 'none',
filter: `grayscale(${selectedTheme.appShadowsEnabled === 1 ? 0 : '75%'}) brightness(${selectedTheme.appShadowsEnabled === 1 ? '1' : '1.5'})`, filter: `grayscale(${designerTheme.appShadowsEnabled === 1 ? 0 : '75%'}) brightness(${designerTheme.appShadowsEnabled === 1 ? '1' : '1.5'})`,
transition: 'all 1s ease', transition: 'all 1s ease',
}} }}
> >
@@ -574,7 +565,7 @@ export function ThemeEditor({
justify='center' justify='center'
align='center' align='center'
style={{ style={{
backgroundColor: selectedTheme.backgroundColor, backgroundColor: designerTheme.backgroundColor,
overflow: 'clip', overflow: 'clip',
}} }}
> >
@@ -582,10 +573,10 @@ export function ThemeEditor({
style={{ style={{
marginTop: '12px', marginTop: '12px',
marginBottom: '7px', marginBottom: '7px',
color: selectedTheme.primaryColor, color: designerTheme.primaryColor,
height: 32, height: 32,
width: 32, width: 32,
filter: `drop-shadow(2px 2px color-mix(in srgb, ${selectedTheme.accentColor}, transparent 30%))`, filter: `drop-shadow(2px 2px color-mix(in srgb, ${designerTheme.accentColor}, transparent 30%))`,
}} }}
/> />
</Group> </Group>
@@ -603,7 +594,7 @@ export function ThemeEditor({
size={32} size={32}
onClick={() => { onClick={() => {
setCustomFontEnabled(!customFontEnabled); setCustomFontEnabled(!customFontEnabled);
setFont(null); setFont(customFontEnabled ? defaultTheme.themeFont : null);
}} }}
style={{ style={{
borderRadius: '0px', borderRadius: '0px',
@@ -627,15 +618,13 @@ export function ThemeEditor({
<FontPicker <FontPicker
autoLoad autoLoad
defaultValue={ defaultValue={
selectedTheme.themeFont === null designerTheme.themeFont === null
? 'Jost' ? currentUser.defaultFont
: selectedTheme.themeFont : designerTheme.themeFont
} }
value={(font: string) => { value={(pickerFont: string) => {
if (customFontEnabled) { if (customFontEnabled) {
setFont(font); setFont(pickerFont);
} else {
setFont(null);
} }
}} }}
/> />
@@ -650,7 +639,7 @@ export function ThemeEditor({
color={'red'} color={'red'}
size={32} size={32}
onClick={() => { onClick={() => {
setFont(null); setFont(currentUser.defaultFont);
setCustomFontEnabled(false); setCustomFontEnabled(false);
}} }}
style={{ style={{
@@ -662,7 +651,7 @@ export function ThemeEditor({
</Tooltip> </Tooltip>
</Group> </Group>
</Stack> </Stack>
{JSON.stringify(defaultTheme) !== JSON.stringify(selectedTheme) && ( {JSON.stringify(defaultTheme) !== JSON.stringify(designerTheme) && (
<Stack <Stack
gap={0} gap={0}
w={'95%'} w={'95%'}
@@ -703,276 +692,292 @@ export function ThemeEditor({
Object.keys(getObjectDiff(defaultTheme, designerTheme)).map( Object.keys(getObjectDiff(defaultTheme, designerTheme)).map(
(key: any) => { (key: any) => {
return ( return (
<Stack key !== 'id' && (
w={'100%'} <Stack
align='center' w={'100%'}
justify='center' align='center'
gap={0} justify='center'
key={key} gap={0}
> key={key}
<Group w={'100%'} justify='space-between'> >
<Text <Group w={'100%'} justify='space-between'>
key={key}
pl={2}
style={{
color: 'var(--mantine-color-primary-0)',
backgroundColor:
'var(--mantine-color-background-0)',
}}
>
{ThemeProperties[key as keyof typeof ThemeProperties]}
</Text>
<Tooltip
label={`Reset ${
ThemeProperties[key as keyof typeof ThemeProperties]
}`}
classNames={toolTipClasses}
className={toolTipClasses.default}
>
<ActionIcon
variant='subtle'
color='red'
onClick={() => {
setSelectedTheme({
...designerTheme,
[key]: defaultTheme[key as keyof Theme],
});
if (key.includes('Font')) {
setFont(defaultTheme.themeFont);
}
}}
>
<FiX />
</ActionIcon>
</Tooltip>
</Group>
{key === 'name' && (
<Group
w={'100%'}
justify='center'
bg='var(--mantine-color-accent-0)'
style={{ borderRadius: 8, overflow: 'clip' }}
h={48}
>
<Text>{defaultTheme[key as keyof Theme]}</Text>
<Text>→</Text>
<Text>{designerTheme[key as keyof Theme]}</Text>
</Group>
)}
{key.includes('Shadow') && (
<Group
w={'100%'}
justify='center'
bg='var(--mantine-color-accent-0)'
style={{ borderRadius: 8, overflow: 'clip' }}
h={48}
>
{defaultTheme[key as keyof Theme] === 0 ? (
<Text>Off</Text>
) : (
<Text>On</Text>
)}
<Text>→</Text>
{designerTheme[key as keyof Theme] === 0 ? (
<Text>Off</Text>
) : (
<Text>On</Text>
)}
</Group>
)}
{key.includes('Font') && (
<Group
w={'100%'}
justify='center'
bg='var(--mantine-color-accent-0)'
h={48}
style={{ borderRadius: 8, overflow: 'clip' }}
>
<Text <Text
key={key}
pl={2}
style={{ style={{
fontFamily: color: 'var(--mantine-color-primary-0)',
(defaultTheme[key as keyof Theme] as string) ?? backgroundColor:
undefined, 'var(--mantine-color-background-0)',
fontSize: 18,
}} }}
> >
{defaultTheme[key as keyof Theme] || {
`Default (${currentUser.defaultFont})`} ThemeProperties[
</Text> key as keyof typeof ThemeProperties
<Text>→</Text> ]
<Text
style={{
fontFamily: selectedTheme.themeFont ?? undefined,
fontSize: 18,
}}
>
{designerTheme[key as keyof Theme] ||
`Default (${currentUser.defaultFont})`}
</Text>
</Group>
)}
{key === 'backgroundImage' && (
<Group
w={'100%'}
justify='center'
gap={4}
bg='var(--mantine-color-accent-0)'
style={{ borderRadius: 8, overflow: 'clip' }}
>
<Image
w={'40%'}
src={
(
defaultTheme[key as keyof Theme] as string
).toLocaleLowerCase() === 'none'
? 'https://placehold.co/540x360?text=None'
: `../../api/backgroundImages/thumbs/${defaultTheme[key as keyof Theme] as string}`
} }
/> </Text>
<Text>→</Text>
<Image
w={'40%'}
src={
(
designerTheme[key as keyof Theme] as string
).toLocaleLowerCase() === 'none'
? 'https://placehold.co/540x360?text=None'
: `../../api/backgroundImages/thumbs/${designerTheme[key as keyof Theme] as string}`
}
/>
</Group>
)}
{key.includes('Color') && (
<Group
w={'100%'}
justify='space-between'
bg='var(--mantine-color-accent-0)'
style={{ borderRadius: 8, overflow: 'clip' }}
>
<Tooltip <Tooltip
label='Copy color code' label={`Reset ${
ThemeProperties[
key as keyof typeof ThemeProperties
]
}`}
classNames={toolTipClasses} classNames={toolTipClasses}
className={toolTipClasses.default} className={toolTipClasses.default}
> >
<ColorSwatch <ActionIcon
h={50} variant='subtle'
color='red'
onClick={() => { onClick={() => {
navigator.clipboard.writeText( setDesignerTheme({
defaultTheme[key as keyof Theme] as string, ...designerTheme,
); [key]: defaultTheme[key as keyof Theme],
notifications.show({
title: (
<Text
fw={600}
c={'var(--mantine-color-accent-0)'}
>
Copied{' '}
{
ThemeProperties[
key as keyof typeof ThemeProperties
]
}
</Text>
),
message: (
<Text c={'var(--mantine-color-primary-0)'}>
{
defaultTheme[
key as keyof Theme
] as string
}
</Text>
),
autoClose: true,
withCloseButton: true,
icon: <FaClipboard size={24} />,
}); });
if (key.includes('Font')) {
setFont(defaultTheme.themeFont);
}
}} }}
color={defaultTheme[key as keyof Theme] as string} >
style={{ <FiX />
cursor: 'pointer', </ActionIcon>
}}
styles={{
alphaOverlay: {
borderRadius: '0px',
},
colorOverlay: {
borderRadius: '0px',
},
shadowOverlay: {
borderRadius: '0px',
},
childrenOverlay: {
borderRadius: '0px',
},
}}
/>
</Tooltip>
<Text>→</Text>
<Tooltip
label='Copy color code'
classNames={toolTipClasses}
className={toolTipClasses.default}
>
<ColorSwatch
h={50}
onClick={() => {
navigator.clipboard.writeText(
defaultTheme[key as keyof Theme] as string,
);
notifications.show({
title: (
<Text
fw={600}
c={'var(--mantine-color-accent-0)'}
>
Copied{' '}
{
ThemeProperties[
key as keyof typeof ThemeProperties
]
}
</Text>
),
message: (
<Text c={'var(--mantine-color-primary-0)'}>
{
designerTheme[
key as keyof Theme
] as string
}
</Text>
),
autoClose: true,
withCloseButton: true,
icon: <FaClipboard size={24} />,
});
}}
color={
designerTheme[key as keyof Theme] as string
}
style={{
cursor: 'pointer',
}}
styles={{
alphaOverlay: {
borderRadius: '0px',
},
colorOverlay: {
borderRadius: '0px',
},
shadowOverlay: {
borderRadius: '0px',
},
childrenOverlay: {
borderRadius: '0px',
},
}}
/>
</Tooltip> </Tooltip>
</Group> </Group>
)} {key === 'name' && (
</Stack> <Group
w={'100%'}
justify='center'
bg='var(--mantine-color-accent-0)'
style={{ borderRadius: 8, overflow: 'clip' }}
h={48}
>
<Text>{defaultTheme[key as keyof Theme]}</Text>
<Text>→</Text>
<Text>{designerTheme[key as keyof Theme]}</Text>
</Group>
)}
{key.includes('Shadow') && (
<Group
w={'100%'}
justify='center'
bg='var(--mantine-color-accent-0)'
style={{ borderRadius: 8, overflow: 'clip' }}
h={48}
>
{defaultTheme[key as keyof Theme] === 0 ? (
<Text>Off</Text>
) : (
<Text>On</Text>
)}
<Text>→</Text>
{designerTheme[key as keyof Theme] === 0 ? (
<Text>Off</Text>
) : (
<Text>On</Text>
)}
</Group>
)}
{key.includes('Font') && (
<Group
w={'100%'}
justify='center'
bg='var(--mantine-color-accent-0)'
h={48}
style={{ borderRadius: 8, overflow: 'clip' }}
>
<Text
style={{
fontFamily:
(defaultTheme[
key as keyof Theme
] as string) ?? undefined,
fontSize: 18,
}}
>
{defaultTheme[key as keyof Theme] ||
`Default (${currentUser.defaultFont})`}
</Text>
<Text>→</Text>
<Text
style={{
fontFamily:
designerTheme.themeFont ?? undefined,
fontSize: 18,
}}
>
{designerTheme[key as keyof Theme] ||
`Default (${currentUser.defaultFont})`}
</Text>
</Group>
)}
{key === 'backgroundImage' && (
<Group
w={'100%'}
justify='center'
gap={4}
bg='var(--mantine-color-accent-0)'
style={{ borderRadius: 8, overflow: 'clip' }}
>
<Image
w={'40%'}
src={
(
defaultTheme[key as keyof Theme] as string
).toLocaleLowerCase() === 'none'
? 'https://placehold.co/540x360?text=None'
: `../../api/backgroundImages/thumbs/${defaultTheme[key as keyof Theme] as string}`
}
/>
<Text>→</Text>
<Image
w={'40%'}
src={
(
designerTheme[key as keyof Theme] as string
).toLocaleLowerCase() === 'none'
? 'https://placehold.co/540x360?text=None'
: `../../api/backgroundImages/thumbs/${designerTheme[key as keyof Theme] as string}`
}
/>
</Group>
)}
{key.includes('Color') && (
<Group
w={'100%'}
justify='space-between'
bg='var(--mantine-color-accent-0)'
style={{ borderRadius: 8, overflow: 'clip' }}
>
<Tooltip
label='Copy color code'
classNames={toolTipClasses}
className={toolTipClasses.default}
>
<ColorSwatch
h={50}
onClick={() => {
navigator.clipboard.writeText(
defaultTheme[key as keyof Theme] as string,
);
notifications.show({
title: (
<Text
fw={600}
c={'var(--mantine-color-accent-0)'}
>
Copied{' '}
{
ThemeProperties[
key as keyof typeof ThemeProperties
]
}
</Text>
),
message: (
<Text
c={'var(--mantine-color-primary-0)'}
>
{
defaultTheme[
key as keyof Theme
] as string
}
</Text>
),
autoClose: true,
withCloseButton: true,
icon: <FaClipboard size={24} />,
});
}}
color={
defaultTheme[key as keyof Theme] as string
}
style={{
cursor: 'pointer',
}}
styles={{
alphaOverlay: {
borderRadius: '0px',
},
colorOverlay: {
borderRadius: '0px',
},
shadowOverlay: {
borderRadius: '0px',
},
childrenOverlay: {
borderRadius: '0px',
},
}}
/>
</Tooltip>
<Text>→</Text>
<Tooltip
label='Copy color code'
classNames={toolTipClasses}
className={toolTipClasses.default}
>
<ColorSwatch
h={50}
onClick={() => {
navigator.clipboard.writeText(
defaultTheme[key as keyof Theme] as string,
);
notifications.show({
title: (
<Text
fw={600}
c={'var(--mantine-color-accent-0)'}
>
Copied{' '}
{
ThemeProperties[
key as keyof typeof ThemeProperties
]
}
</Text>
),
message: (
<Text
c={'var(--mantine-color-primary-0)'}
>
{
designerTheme[
key as keyof Theme
] as string
}
</Text>
),
autoClose: true,
withCloseButton: true,
icon: <FaClipboard size={24} />,
});
}}
color={
designerTheme[key as keyof Theme] as string
}
style={{
cursor: 'pointer',
}}
styles={{
alphaOverlay: {
borderRadius: '0px',
},
colorOverlay: {
borderRadius: '0px',
},
shadowOverlay: {
borderRadius: '0px',
},
childrenOverlay: {
borderRadius: '0px',
},
}}
/>
</Tooltip>
</Group>
)}
</Stack>
)
); );
}, },
)} )}
@@ -988,8 +993,8 @@ export function ThemeEditor({
> >
<Button <Button
onClick={() => { onClick={() => {
setSelectedTheme({ setDesignerTheme({
...selectedTheme, ...designerTheme,
backgroundColor: randomColor(), backgroundColor: randomColor(),
searchColor: randomColor(true), searchColor: randomColor(true),
primaryColor: randomColor(), primaryColor: randomColor(),
@@ -1003,12 +1008,13 @@ export function ThemeEditor({
</Tooltip> </Tooltip>
<Button <Button
onClick={() => { onClick={() => {
setCustomFontEnabled(defaultTheme.themeFont !== null);
duplicate duplicate
? setSelectedTheme({ ? setDesignerTheme({
...defaultTheme, ...defaultTheme,
name: `${defaultTheme.name} copy`, name: `${defaultTheme.name} copy`,
}) })
: setSelectedTheme({ ...defaultTheme }); : setDesignerTheme({ ...defaultTheme });
}} }}
> >
Reset Changes Reset Changes
@@ -1020,7 +1026,7 @@ export function ThemeEditor({
</Group> </Group>
<Group> <Group>
<Button <Button
disabled={JSON.stringify(defaultTheme) === JSON.stringify(selectedTheme)} disabled={JSON.stringify(defaultTheme) === JSON.stringify(designerTheme)}
className={buttonClasses.saveButton} className={buttonClasses.saveButton}
classNames={buttonClasses} classNames={buttonClasses}
onClick={handleSaveTheme} onClick={handleSaveTheme}