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

View File

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

View File

@@ -37,10 +37,9 @@ import { ThemeModalContent } from './ThemeModalContent';
export function ThemeEditor({
backgroundList,
defaultTheme,
selectedTheme,
designerTheme,
setDesignerTheme,
selectedTheme,
setSelectedTheme,
pickerOpen,
openPicker,
closePicker,
@@ -51,10 +50,9 @@ export function ThemeEditor({
}: {
backgroundList: string[];
defaultTheme: Theme;
selectedTheme: Theme;
designerTheme: Theme;
setDesignerTheme: (theme: Theme) => void;
selectedTheme: Theme;
setSelectedTheme: (theme: Theme) => void;
pickerOpen: boolean;
openPicker: () => void;
closePicker: () => void;
@@ -78,27 +76,6 @@ export function ThemeEditor({
const filtered = backgroundList.filter((item: string) =>
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) => {
return (
<UnstyledButton
@@ -110,7 +87,7 @@ export function ThemeEditor({
background:
item !== 'none' && item !== 'None'
? 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})`
: 'repeating-linear-gradient(45deg, #33333366, #33333366 10px, #88888866 10px, #88888866 20px)',
backgroundSize: item === 'none' ? undefined : 'cover',
@@ -173,7 +150,7 @@ export function ThemeEditor({
setColorValue(colorValue || '');
closeBackgroundPicker();
openPicker();
setDesignerTheme(selectedTheme);
setDesignerTheme(designerTheme);
};
const randomColor = (withAlpha?: boolean) => {
@@ -185,6 +162,20 @@ export function ThemeEditor({
return color;
};
useEffect(() => {
if (font) {
setDesignerTheme({
...designerTheme,
themeFont: font,
});
}
}, [font]);
useEffect(() => {
if (selectedTheme) {
setCustomFontEnabled(designerTheme.themeFont !== null);
setFont(designerTheme.themeFont);
}
}, [selectedTheme]);
return (
<>
<Group p={'2rem'} w={'100%'} justify='space-between'>
@@ -197,10 +188,10 @@ export function ThemeEditor({
>
<ColorSwatch
onClick={() => {
handleSwatchClick('primaryColor', selectedTheme.primaryColor);
handleSwatchClick('primaryColor', designerTheme.primaryColor);
}}
color={selectedTheme.primaryColor}
style={{ borderColor: selectedTheme.primaryColor }}
color={designerTheme.primaryColor}
style={{ borderColor: designerTheme.primaryColor }}
className={colorSwatchClasses.themeDesignerSwatch}
classNames={colorSwatchClasses}
top={'11.5rem'}
@@ -214,10 +205,10 @@ export function ThemeEditor({
>
<ColorSwatch
onClick={() => {
handleSwatchClick('accentColor', selectedTheme.accentColor);
handleSwatchClick('accentColor', designerTheme.accentColor);
}}
color={selectedTheme.accentColor}
style={{ borderColor: selectedTheme.primaryColor }}
color={designerTheme.accentColor}
style={{ borderColor: designerTheme.primaryColor }}
className={colorSwatchClasses.themeDesignerSwatch}
classNames={colorSwatchClasses}
bottom={'6rem'}
@@ -231,10 +222,10 @@ export function ThemeEditor({
>
<ColorSwatch
onClick={() => {
handleSwatchClick('backgroundColor', selectedTheme.backgroundColor);
handleSwatchClick('backgroundColor', designerTheme.backgroundColor);
}}
color={selectedTheme.backgroundColor}
style={{ borderColor: selectedTheme.primaryColor }}
color={designerTheme.backgroundColor}
style={{ borderColor: designerTheme.primaryColor }}
className={colorSwatchClasses.themeDesignerSwatch}
classNames={colorSwatchClasses}
bottom={'4rem'}
@@ -248,10 +239,10 @@ export function ThemeEditor({
>
<ColorSwatch
onClick={() => {
handleSwatchClick('borderColor', selectedTheme.borderColor);
handleSwatchClick('borderColor', designerTheme.borderColor);
}}
color={selectedTheme.borderColor}
style={{ borderColor: selectedTheme.primaryColor }}
color={designerTheme.borderColor}
style={{ borderColor: designerTheme.primaryColor }}
className={colorSwatchClasses.themeDesignerSwatch}
classNames={colorSwatchClasses}
top={'4.5rem'}
@@ -265,10 +256,10 @@ export function ThemeEditor({
>
<ColorSwatch
onClick={() => {
handleSwatchClick('searchColor', selectedTheme.searchColor);
handleSwatchClick('searchColor', designerTheme.searchColor);
}}
color={selectedTheme.searchColor}
style={{ borderColor: selectedTheme.primaryColor }}
color={designerTheme.searchColor}
style={{ borderColor: designerTheme.primaryColor }}
className={colorSwatchClasses.themeDesignerSwatch}
classNames={colorSwatchClasses}
top={'3.25rem'}
@@ -284,13 +275,13 @@ export function ThemeEditor({
onClick={() => {
closePicker();
openBackgroundPicker();
setDesignerTheme(selectedTheme);
setDesignerTheme(designerTheme);
}}
pos={'absolute'}
right={0}
style={{
color: selectedTheme.backgroundColor,
backgroundColor: selectedTheme.primaryColor,
color: designerTheme.backgroundColor,
backgroundColor: designerTheme.primaryColor,
borderRadius: 0,
borderBottomLeftRadius: 10,
borderTopRightRadius: 10,
@@ -364,7 +355,7 @@ export function ThemeEditor({
const rc = randomColor(colorId === 'searchColor');
setColorValue(rc);
setDesignerTheme({
...selectedTheme,
...designerTheme,
[colorId]: rc,
});
}}
@@ -374,8 +365,8 @@ export function ThemeEditor({
<Button
color={'green'}
onClick={() => {
setSelectedTheme({
...selectedTheme,
setDesignerTheme({
...designerTheme,
[colorId]: colorValue,
});
@@ -391,7 +382,7 @@ export function ThemeEditor({
closePicker();
setDesignerTheme({
...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)',
},
input: {
color: selectedTheme.primaryColor,
backgroundColor: selectedTheme.backgroundColor,
color: designerTheme.primaryColor,
backgroundColor: designerTheme.backgroundColor,
border: 'none',
borderBottom: '1px solid var(--mantine-color-primary-0)',
},
@@ -478,8 +469,8 @@ export function ThemeEditor({
<Button
color={'green'}
onClick={() => {
setSelectedTheme({
...selectedTheme,
setDesignerTheme({
...designerTheme,
backgroundImage: designerTheme.backgroundImage,
});
closeBackgroundPicker();
@@ -494,7 +485,7 @@ export function ThemeEditor({
closeBackgroundPicker();
setDesignerTheme({
...designerTheme,
backgroundImage: selectedTheme.backgroundImage,
backgroundImage: designerTheme.backgroundImage,
});
}}
>
@@ -509,17 +500,17 @@ export function ThemeEditor({
<Group gap={2} w={'100%'} justify='space-between' align='center'>
<Checkbox.Card
w={'calc(50% - 1px)'}
checked={selectedTheme.textShadowsEnabled === 1}
checked={designerTheme.textShadowsEnabled === 1}
c={'var(--mantine-color-background-0)'}
onChange={(e) => {
setSelectedTheme({
...selectedTheme,
setDesignerTheme({
...designerTheme,
textShadowsEnabled: e ? 1 : 0,
});
}}
style={{
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',
}}
>
@@ -532,15 +523,15 @@ export function ThemeEditor({
w={'100%'}
justify='center'
style={{
backgroundColor: selectedTheme.backgroundColor,
backgroundColor: designerTheme.backgroundColor,
}}
>
<Text
classNames={textClasses}
className={textClasses.sectionTitle}
style={{
color: selectedTheme.accentColor,
textShadow: `2px 2px ${selectedTheme.primaryColor}`,
color: designerTheme.accentColor,
textShadow: `2px 2px ${designerTheme.primaryColor}`,
}}
>
Shadows
@@ -550,17 +541,17 @@ export function ThemeEditor({
</Checkbox.Card>
<Checkbox.Card
w={'calc(50% - 1px)'}
checked={selectedTheme.appShadowsEnabled === 1}
checked={designerTheme.appShadowsEnabled === 1}
c={'var(--mantine-color-background-0)'}
onChange={(e) => {
setSelectedTheme({
...selectedTheme,
setDesignerTheme({
...designerTheme,
appShadowsEnabled: e ? 1 : 0,
});
}}
style={{
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',
}}
>
@@ -574,7 +565,7 @@ export function ThemeEditor({
justify='center'
align='center'
style={{
backgroundColor: selectedTheme.backgroundColor,
backgroundColor: designerTheme.backgroundColor,
overflow: 'clip',
}}
>
@@ -582,10 +573,10 @@ export function ThemeEditor({
style={{
marginTop: '12px',
marginBottom: '7px',
color: selectedTheme.primaryColor,
color: designerTheme.primaryColor,
height: 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>
@@ -603,7 +594,7 @@ export function ThemeEditor({
size={32}
onClick={() => {
setCustomFontEnabled(!customFontEnabled);
setFont(null);
setFont(customFontEnabled ? defaultTheme.themeFont : null);
}}
style={{
borderRadius: '0px',
@@ -627,15 +618,13 @@ export function ThemeEditor({
<FontPicker
autoLoad
defaultValue={
selectedTheme.themeFont === null
? 'Jost'
: selectedTheme.themeFont
designerTheme.themeFont === null
? currentUser.defaultFont
: designerTheme.themeFont
}
value={(font: string) => {
value={(pickerFont: string) => {
if (customFontEnabled) {
setFont(font);
} else {
setFont(null);
setFont(pickerFont);
}
}}
/>
@@ -650,7 +639,7 @@ export function ThemeEditor({
color={'red'}
size={32}
onClick={() => {
setFont(null);
setFont(currentUser.defaultFont);
setCustomFontEnabled(false);
}}
style={{
@@ -662,7 +651,7 @@ export function ThemeEditor({
</Tooltip>
</Group>
</Stack>
{JSON.stringify(defaultTheme) !== JSON.stringify(selectedTheme) && (
{JSON.stringify(defaultTheme) !== JSON.stringify(designerTheme) && (
<Stack
gap={0}
w={'95%'}
@@ -703,276 +692,292 @@ export function ThemeEditor({
Object.keys(getObjectDiff(defaultTheme, designerTheme)).map(
(key: any) => {
return (
<Stack
w={'100%'}
align='center'
justify='center'
gap={0}
key={key}
>
<Group w={'100%'} justify='space-between'>
<Text
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' }}
>
key !== 'id' && (
<Stack
w={'100%'}
align='center'
justify='center'
gap={0}
key={key}
>
<Group w={'100%'} justify='space-between'>
<Text
key={key}
pl={2}
style={{
fontFamily:
(defaultTheme[key as keyof Theme] as string) ??
undefined,
fontSize: 18,
color: 'var(--mantine-color-primary-0)',
backgroundColor:
'var(--mantine-color-background-0)',
}}
>
{defaultTheme[key as keyof Theme] ||
`Default (${currentUser.defaultFont})`}
</Text>
<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}`
{
ThemeProperties[
key as keyof typeof ThemeProperties
]
}
/>
<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' }}
>
</Text>
<Tooltip
label='Copy color code'
label={`Reset ${
ThemeProperties[
key as keyof typeof ThemeProperties
]
}`}
classNames={toolTipClasses}
className={toolTipClasses.default}
>
<ColorSwatch
h={50}
<ActionIcon
variant='subtle'
color='red'
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} />,
setDesignerTheme({
...designerTheme,
[key]: defaultTheme[key as keyof Theme],
});
if (key.includes('Font')) {
setFont(defaultTheme.themeFont);
}
}}
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',
},
}}
/>
>
<FiX />
</ActionIcon>
</Tooltip>
</Group>
)}
</Stack>
{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
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
onClick={() => {
setSelectedTheme({
...selectedTheme,
setDesignerTheme({
...designerTheme,
backgroundColor: randomColor(),
searchColor: randomColor(true),
primaryColor: randomColor(),
@@ -1003,12 +1008,13 @@ export function ThemeEditor({
</Tooltip>
<Button
onClick={() => {
setCustomFontEnabled(defaultTheme.themeFont !== null);
duplicate
? setSelectedTheme({
? setDesignerTheme({
...defaultTheme,
name: `${defaultTheme.name} copy`,
})
: setSelectedTheme({ ...defaultTheme });
: setDesignerTheme({ ...defaultTheme });
}}
>
Reset Changes
@@ -1020,7 +1026,7 @@ export function ThemeEditor({
</Group>
<Group>
<Button
disabled={JSON.stringify(defaultTheme) === JSON.stringify(selectedTheme)}
disabled={JSON.stringify(defaultTheme) === JSON.stringify(designerTheme)}
className={buttonClasses.saveButton}
classNames={buttonClasses}
onClick={handleSaveTheme}