@@ -24,7 +24,7 @@ export function ColorSelector({ type, defaultValue }: ColorControlProps) {
|
||||
const [color, setColor] = useState(defaultValue);
|
||||
const [popoverOpened, popover] = useDisclosure(false);
|
||||
const { setPrimaryColor, setSecondaryColor } = useColorTheme();
|
||||
const { name: configName } = useConfigContext();
|
||||
const { config, name: configName } = useConfigContext();
|
||||
const updateConfig = useConfigStore((x) => x.updateConfig);
|
||||
|
||||
const theme = useMantineTheme();
|
||||
|
||||
@@ -1,44 +1,105 @@
|
||||
import { Textarea } from '@mantine/core';
|
||||
import {
|
||||
Box,
|
||||
createStyles,
|
||||
Group,
|
||||
Loader,
|
||||
ScrollArea,
|
||||
Stack,
|
||||
Text,
|
||||
useMantineTheme,
|
||||
} from '@mantine/core';
|
||||
import { useDebouncedValue } from '@mantine/hooks';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { ChangeEventHandler, useState } from 'react';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { ChangeEvent, useEffect, useState } from 'react';
|
||||
import { useConfigContext } from '../../../../config/provider';
|
||||
import { useConfigStore } from '../../../../config/store';
|
||||
|
||||
interface CustomCssChangerProps {
|
||||
defaultValue: string | undefined;
|
||||
}
|
||||
const CodeEditor = dynamic(
|
||||
() => import('@uiw/react-textarea-code-editor').then((mod) => mod.default),
|
||||
{ ssr: false }
|
||||
);
|
||||
|
||||
export const CustomCssChanger = ({ defaultValue }: CustomCssChangerProps) => {
|
||||
export const CustomCssChanger = () => {
|
||||
const { t } = useTranslation('settings/customization/page-appearance');
|
||||
const updateConfig = useConfigStore((x) => x.updateConfig);
|
||||
const { name: configName } = useConfigContext();
|
||||
const [customCss, setCustomCss] = useState(defaultValue);
|
||||
const { colorScheme, colors } = useMantineTheme();
|
||||
const { config, name: configName } = useConfigContext();
|
||||
const [nonDebouncedCustomCSS, setNonDebouncedCustomCSS] = useState(
|
||||
config?.settings.customization.customCss ?? ''
|
||||
);
|
||||
const [debouncedCustomCSS] = useDebouncedValue(nonDebouncedCustomCSS, 696);
|
||||
const { classes } = useStyles();
|
||||
|
||||
if (!configName) return null;
|
||||
|
||||
const handleChange: ChangeEventHandler<HTMLTextAreaElement> = (ev) => {
|
||||
const { value } = ev.currentTarget;
|
||||
const customCss = value.trim().length === 0 ? undefined : value;
|
||||
setCustomCss(customCss);
|
||||
useEffect(() => {
|
||||
updateConfig(configName, (prev) => ({
|
||||
...prev,
|
||||
settings: {
|
||||
...prev.settings,
|
||||
customization: {
|
||||
...prev.settings.customization,
|
||||
customCss,
|
||||
customCss: debouncedCustomCSS,
|
||||
},
|
||||
},
|
||||
}));
|
||||
};
|
||||
}, [debouncedCustomCSS]);
|
||||
|
||||
const codeIsDirty = nonDebouncedCustomCSS !== debouncedCustomCSS;
|
||||
const codeEditorHeight = codeIsDirty ? 250 - 42 : 250;
|
||||
|
||||
return (
|
||||
<Textarea
|
||||
minRows={5}
|
||||
label={t('customCSS.label')}
|
||||
placeholder={t('customCSS.placeholder')}
|
||||
value={customCss}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<Stack spacing={4} mt="xl">
|
||||
<Text>{t('customCSS.label')}</Text>
|
||||
<Text color="dimmed" size="xs">
|
||||
{t('customCSS.description')}
|
||||
</Text>
|
||||
<div className={classes.codeEditorRoot}>
|
||||
<ScrollArea style={{ height: codeEditorHeight }}>
|
||||
<CodeEditor
|
||||
className={classes.codeEditor}
|
||||
placeholder={t('customCSS.placeholder')}
|
||||
value={nonDebouncedCustomCSS}
|
||||
onChange={(event: ChangeEvent<HTMLTextAreaElement>) =>
|
||||
setNonDebouncedCustomCSS(event.target.value.trim())
|
||||
}
|
||||
language="css"
|
||||
data-color-mode={colorScheme}
|
||||
minHeight={codeEditorHeight}
|
||||
/>
|
||||
</ScrollArea>
|
||||
{codeIsDirty && (
|
||||
<Box className={classes.codeEditorFooter}>
|
||||
<Group p="xs" spacing="xs">
|
||||
<Loader color={colors.gray[0]} size={18} />
|
||||
<Text>{t('customCSS.applying')}</Text>
|
||||
</Group>
|
||||
</Box>
|
||||
)}
|
||||
</div>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
const useStyles = createStyles(({ colors, colorScheme, radius }) => ({
|
||||
codeEditorFooter: {
|
||||
borderBottomLeftRadius: radius.sm,
|
||||
borderBottomRightRadius: radius.sm,
|
||||
backgroundColor: colorScheme === 'dark' ? colors.dark[7] : undefined,
|
||||
},
|
||||
codeEditorRoot: {
|
||||
borderColor: colorScheme === 'dark' ? colors.dark[4] : colors.gray[4],
|
||||
borderWidth: 1,
|
||||
borderStyle: 'solid',
|
||||
borderRadius: radius.sm,
|
||||
},
|
||||
codeEditor: {
|
||||
backgroundColor: colorScheme === 'dark' ? colors.dark[6] : 'white',
|
||||
fontSize: 12,
|
||||
|
||||
'& ::placeholder': {
|
||||
color: colorScheme === 'dark' ? colors.dark[3] : colors.gray[5],
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -4,15 +4,11 @@ import { useState } from 'react';
|
||||
import { useConfigContext } from '../../../../config/provider';
|
||||
import { useConfigStore } from '../../../../config/store';
|
||||
|
||||
interface OpacitySelectorProps {
|
||||
defaultValue: number | undefined;
|
||||
}
|
||||
|
||||
export function OpacitySelector({ defaultValue }: OpacitySelectorProps) {
|
||||
const [opacity, setOpacity] = useState(defaultValue || 100);
|
||||
export function DashboardTilesOpacitySelector() {
|
||||
const { config, name: configName } = useConfigContext();
|
||||
const [opacity, setOpacity] = useState(config?.settings.customization.appOpacity || 100);
|
||||
const { t } = useTranslation('settings/customization/opacity-selector');
|
||||
|
||||
const { name: configName } = useConfigContext();
|
||||
const updateConfig = useConfigStore((x) => x.updateConfig);
|
||||
|
||||
if (!configName) return null;
|
||||
|
||||
@@ -15,17 +15,13 @@ import { useConfigContext } from '../../../../config/provider';
|
||||
import { useConfigStore } from '../../../../config/store';
|
||||
import { useColorTheme } from '../../../../tools/color';
|
||||
|
||||
interface ShadeSelectorProps {
|
||||
defaultValue: MantineTheme['primaryShade'] | undefined;
|
||||
}
|
||||
|
||||
export function ShadeSelector({ defaultValue }: ShadeSelectorProps) {
|
||||
export function ShadeSelector() {
|
||||
const { t } = useTranslation('settings/customization/shade-selector');
|
||||
const [shade, setShade] = useState(defaultValue);
|
||||
const { config, name: configName } = useConfigContext();
|
||||
const [shade, setShade] = useState(config?.settings.customization.colors.shade);
|
||||
const [popoverOpened, popover] = useDisclosure(false);
|
||||
const { primaryColor, setPrimaryShade } = useColorTheme();
|
||||
|
||||
const { name: configName } = useConfigContext();
|
||||
const updateConfig = useConfigStore((x) => x.updateConfig);
|
||||
|
||||
const theme = useMantineTheme();
|
||||
|
||||
Reference in New Issue
Block a user