SoapboxConfig: fix theme selector

This commit is contained in:
Alex Gleason 2022-05-05 18:12:08 -05:00
parent 0bd43a3f2c
commit db08a27657
No known key found for this signature in database
GPG key ID: 7211D1F99744FBB7
3 changed files with 85 additions and 57 deletions

View file

@ -10,7 +10,7 @@ import HStack from 'soapbox/components/ui/hstack/hstack';
import Stack from 'soapbox/components/ui/stack/stack'; import Stack from 'soapbox/components/ui/stack/stack';
import Streamfield from 'soapbox/components/ui/streamfield/streamfield'; import Streamfield from 'soapbox/components/ui/streamfield/streamfield';
import { Checkbox } from 'soapbox/features/forms'; import { Checkbox } from 'soapbox/features/forms';
import ThemeToggle from 'soapbox/features/ui/components/theme-toggle'; import ThemeSelector from 'soapbox/features/ui/components/theme-selector';
import { useAppSelector, useAppDispatch } from 'soapbox/hooks'; import { useAppSelector, useAppDispatch } from 'soapbox/hooks';
import { normalizeSoapboxConfig } from 'soapbox/normalizers'; import { normalizeSoapboxConfig } from 'soapbox/normalizers';
@ -48,6 +48,7 @@ type ValueGetter<T = Element> = (e: React.ChangeEvent<T>) => any;
type ColorValueGetter = (color: ColorResult, event: React.ChangeEvent<HTMLInputElement>) => any; type ColorValueGetter = (color: ColorResult, event: React.ChangeEvent<HTMLInputElement>) => any;
type Template = ImmutableMap<string, any>; type Template = ImmutableMap<string, any>;
type ConfigPath = Array<string | number>; type ConfigPath = Array<string | number>;
type ThemeChangeHandler = (theme: string) => void;
const templates: Record<string, Template> = { const templates: Record<string, Template> = {
promoPanelItem: ImmutableMap({ icon: '', text: '', url: '' }), promoPanelItem: ImmutableMap({ icon: '', text: '', url: '' }),
@ -109,6 +110,12 @@ const SoapboxConfig: React.FC = () => {
}; };
}; };
const handleThemeChange = (path: ConfigPath): ThemeChangeHandler => {
return theme => {
setConfig(path, theme);
};
};
const handleColorChange = (path: ConfigPath, getValue: ColorValueGetter): ColorChangeHandler => { const handleColorChange = (path: ConfigPath, getValue: ColorValueGetter): ColorChangeHandler => {
return (color, event) => { return (color, event) => {
setConfig(path, getValue(color, event)); setConfig(path, getValue(color, event));
@ -196,12 +203,10 @@ const SoapboxConfig: React.FC = () => {
<div className='input with_label toggle'> <div className='input with_label toggle'>
<div className='label_input'> <div className='label_input'>
<label><FormattedMessage id='soapbox_config.fields.theme_label' defaultMessage='Default theme' /></label> <label><FormattedMessage id='soapbox_config.fields.theme_label' defaultMessage='Default theme' /></label>
<ThemeToggle /> <ThemeSelector
{/* <ThemeToggle value={soapbox.defaultSettings.get('themeMode')}
onToggle={handleChange(['defaultSettings', 'themeMode'], value => value)} onChange={handleThemeChange(['defaultSettings', 'themeMode'])}
themeMode={soapbox.defaultSettings.get('themeMode')} />
intl={intl}
/> */}
</div> </div>
</div> </div>
</Stack> </Stack>

View file

@ -0,0 +1,63 @@
import React, { useMemo } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { Icon } from 'soapbox/components/ui';
const messages = defineMessages({
light: { id: 'theme_toggle.light', defaultMessage: 'Light' },
dark: { id: 'theme_toggle.dark', defaultMessage: 'Dark' },
system: { id: 'theme_toggle.system', defaultMessage: 'System' },
});
interface IThemeSelector {
value: string,
onChange: (value: string) => void,
}
/** Pure theme selector. */
const ThemeSelector: React.FC<IThemeSelector> = ({ value, onChange }) => {
const intl = useIntl();
const themeIconSrc = useMemo(() => {
switch (value) {
case 'system':
return require('@tabler/icons/icons/device-desktop.svg');
case 'light':
return require('@tabler/icons/icons/sun.svg');
case 'dark':
return require('@tabler/icons/icons/moon.svg');
default:
return null;
}
}, [value]);
const handleChange: React.ChangeEventHandler<HTMLSelectElement> = e => {
onChange(e.target.value);
};
return (
<label>
<div className='relative rounded-md shadow-sm'>
<div className='absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none'>
<Icon src={themeIconSrc} className='h-4 w-4 text-gray-400' />
</div>
<select
onBlur={handleChange}
defaultValue={value}
className='focus:ring-indigo-500 focus:border-indigo-500 dark:bg-slate-800 dark:border-gray-600 block w-full pl-8 pr-12 sm:text-sm border-gray-300 rounded-md'
>
<option value='system'>{intl.formatMessage(messages.system)}</option>
<option value='light'>{intl.formatMessage(messages.light)}</option>
<option value='dark'>{intl.formatMessage(messages.dark)}</option>
</select>
<div className='absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none'>
<Icon src={require('@tabler/icons/icons/chevron-down.svg')} className='h-4 w-4 text-gray-400' />
</div>
</div>
</label>
);
};
export default ThemeSelector;

View file

@ -1,65 +1,25 @@
import React, { useMemo } from 'react'; import React from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { useDispatch } from 'react-redux'; import { useDispatch } from 'react-redux';
import { changeSetting } from 'soapbox/actions/settings'; import { changeSetting } from 'soapbox/actions/settings';
import { Icon } from 'soapbox/components/ui';
import { useSettings } from 'soapbox/hooks'; import { useSettings } from 'soapbox/hooks';
const messages = defineMessages({ import ThemeSelector from './theme-selector';
light: { id: 'theme_toggle.light', defaultMessage: 'Light' },
dark: { id: 'theme_toggle.dark', defaultMessage: 'Dark' },
system: { id: 'theme_toggle.system', defaultMessage: 'System' },
});
interface IThemeToggle { /** Stateful theme selector. */
showLabel?: boolean, const ThemeToggle: React.FC = () => {
}
const ThemeToggle = ({ showLabel }: IThemeToggle) => {
const intl = useIntl();
const dispatch = useDispatch(); const dispatch = useDispatch();
const themeMode = useSettings().get('themeMode'); const themeMode = useSettings().get('themeMode');
const onToggle = (event: React.ChangeEvent<HTMLSelectElement>) => { const handleChange = (themeMode: string) => {
dispatch(changeSetting(['themeMode'], event.target.value)); dispatch(changeSetting(['themeMode'], themeMode));
}; };
const themeIconSrc = useMemo(() => {
switch (themeMode) {
case 'system':
return require('@tabler/icons/icons/device-desktop.svg');
case 'light':
return require('@tabler/icons/icons/sun.svg');
case 'dark':
return require('@tabler/icons/icons/moon.svg');
default:
return null;
}
}, [themeMode]);
return ( return (
<label> <ThemeSelector
<div className='relative rounded-md shadow-sm'> value={themeMode}
<div className='absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none'> onChange={handleChange}
<Icon src={themeIconSrc} className='h-4 w-4 text-gray-400' /> />
</div>
<select
onChange={onToggle}
defaultValue={themeMode}
className='focus:ring-indigo-500 focus:border-indigo-500 dark:bg-slate-800 dark:border-gray-600 block w-full pl-8 pr-12 sm:text-sm border-gray-300 rounded-md'
>
<option value='system'>{intl.formatMessage(messages.system)}</option>
<option value='light'>{intl.formatMessage(messages.light)}</option>
<option value='dark'>{intl.formatMessage(messages.dark)}</option>
</select>
<div className='absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none'>
<Icon src={require('@tabler/icons/icons/chevron-down.svg')} className='h-4 w-4 text-gray-400' />
</div>
</div>
</label>
); );
}; };