refactor(weather options): New tile-focused approach to options pages

Co-authored-by: David Ralph <me@davidcralph.co.uk>
This commit is contained in:
alexsparkes
2024-08-24 00:13:56 +01:00
parent ae8d9de411
commit f7bbf04070
17 changed files with 618 additions and 358 deletions

View File

@@ -0,0 +1,144 @@
import { memo, useState } from 'react';
import variables from 'config/variables';
import EventBus from 'utils/eventbus';
import { Checkbox, Dropdown, Radio, Slider, Text } from 'components/Form/Settings';
import {
Header,
Section,
Row,
Content,
Action,
PreferencesWrapper,
} from 'components/Layout/Settings';
import { useTab } from 'components/Elements/MainModal/backend/TabContext';
import { AccessibilityOptions, FontOptions } from './sections/';
import { MdAccessibility, MdFormatSize } from 'react-icons/md';
import values from 'utils/data/slider_values.json';
function AppearanceOptions() {
const [accessibility, setAccessibility] = useState(false);
const [currentTheme, setCurrentTheme] = useState(localStorage.getItem('theme') || 'auto');
const { subSection } = useTab();
const WidgetStyle = () => {
return (
<Row final={true}>
<Content
title={variables.getMessage('settings:sections.appearance.style.title')}
subtitle={variables.getMessage('settings:sections.appearance.style.description')}
/>
<Action>
<Radio
name="widgetStyle"
element=".other"
options={[
{
name: variables.getMessage('settings:sections.appearance.style.legacy'),
value: 'legacy',
},
{
name: variables.getMessage('settings:sections.appearance.style.new'),
value: 'new',
},
]}
category="widgets"
/>
</Action>
</Row>
);
};
const ThemeSelector = (currentTheme) => {
const themes = [
{
example: 'light.jpg',
name: variables.getMessage('settings:sections.appearance.theme.light'),
value: 'light',
},
{
example: 'dark.jpg',
name: variables.getMessage('settings:sections.appearance.theme.dark'),
value: 'dark',
},
{
example: 'light.jpg',
name: variables.getMessage('settings:sections.appearance.theme.auto'),
value: 'auto',
},
];
const ThemeCard = ({ example, name, value }) => {
const handleClick = () => {
localStorage.setItem('theme', value);
setCurrentTheme(value);
EventBus.emit('refresh', 'other');
};
return (
<div
className={
'flex flex-col items-center rounded p-2 hover:bg-background-light hover:dark:bg-background-dark cursor-pointer' +
(currentTheme === value ? ' border-2 border-neutral-100' : '')
}
onClick={handleClick}
>
<img src={`theme-examples/${example}`} alt="Light theme" className="rounded-t" />
<div className="py-4">{name}</div>
</div>
);
};
return (
<div className="bg-modal-content-light dark:bg-modal-content-dark p-10 rounded divide-gray-500 flex flex-col gap-5">
<h1 className="text-3xl tracking-tight font-semibold">
{variables.getMessage('settings:sections.appearance.theme.title')}
</h1>
<p className="text-neutral-800 dark:text-neutral-300">
{variables.getMessage('settings:sections.appearance.theme.description')}
</p>
<div className="grid grid-cols-3 items-center gap-28">
{themes.map((theme) => (
<ThemeCard key={theme.value} {...theme} />
))}
</div>
</div>
);
};
return (
<>
{subSection === 'font' && <FontOptions />}
{subSection === 'accessibility' && <AccessibilityOptions />}
{subSection === '' && (
<>
<Section
id="accessibility"
title={variables.getMessage('settings:sections.appearance.accessibility.title')}
subtitle={variables.getMessage(
'settings:sections.appearance.accessibility.description',
)}
icon={<MdAccessibility />}
/>
<Section
id="font"
title={variables.getMessage('settings:sections.appearance.font.title')}
subtitle={variables.getMessage('settings:sections.appearance.font.description')}
icon={<MdFormatSize />}
/>
{ThemeSelector(currentTheme)}
<PreferencesWrapper>
<WidgetStyle />
</PreferencesWrapper>
</>
)}
</>
);
}
const MemoizedAppearanceOptions = memo(AppearanceOptions);
export { MemoizedAppearanceOptions as default, MemoizedAppearanceOptions as AppearanceOptions };

View File

@@ -0,0 +1 @@
export * from './Appearance';

View File

@@ -0,0 +1,63 @@
import variables from 'config/variables';
import { Checkbox, Dropdown, Slider } from 'components/Form/Settings';
import values from 'utils/data/slider_values.json';
import { Row, Content, Action } from 'components/Layout/Settings';
const AccessibilityOptions = () => {
return (
<Row final={true}>
<Content
title={variables.getMessage('settings:sections.appearance.accessibility.title')}
subtitle={variables.getMessage('settings:sections.appearance.accessibility.description')}
/>
<Action>
<Dropdown
label={variables.getMessage(
'settings:sections.appearance.accessibility.text_shadow.title',
)}
name="textBorder"
category="other"
items={[
{
value: 'new',
text: variables.getMessage(
'settings:sections.appearance.accessibility.text_shadow.new',
),
},
{
value: 'true',
text: variables.getMessage(
'settings:sections.appearance.accessibility.text_shadow.old',
),
},
{
value: 'none',
text: variables.getMessage(
'settings:sections.appearance.accessibility.text_shadow.none',
),
},
]}
/>
<Checkbox
text={variables.getMessage('settings:sections.appearance.accessibility.animations')}
name="animations"
category="other"
/>
<Slider
title={variables.getMessage('settings:sections.appearance.accessibility.toast_duration')}
name="toastDisplayTime"
default="2500"
step="100"
min="500"
max="5000"
marks={values.toast}
display={
' ' + variables.getMessage('settings:sections.appearance.accessibility.milliseconds')
}
/>
</Action>
</Row>
);
};
export { AccessibilityOptions as default, AccessibilityOptions };

View File

@@ -0,0 +1,113 @@
import variables from 'config/variables';
import { Checkbox, Dropdown, Text } from 'components/Form/Settings';
import { Row, Content, Action, PreferencesWrapper } from 'components/Layout/Settings';
const FontOptions = () => {
const fontWeight = 'settings:sections.appearance.font.weight';
return (
<>
<PreferencesWrapper>
<Row>
<Content
title={variables.getMessage('settings:sections.appearance.font.title')}
subtitle={variables.getMessage('settings:sections.appearance.font.description')}
/>
<Action>
<Checkbox
name="fontGoogle"
text={variables.getMessage('settings:sections.appearance.font.google')}
category="other"
/>
<Text
title={variables.getMessage('settings:sections.appearance.font.custom')}
name="font"
upperCaseFirst={true}
category="other"
/>
</Action>
</Row>
</PreferencesWrapper>
<PreferencesWrapper>
<Row>
<Content
title={variables.getMessage('settings:sections.appearance.font.title')}
subtitle={variables.getMessage('settings:sections.appearance.font.description')}
/>
<Action>
{/* names are taken from https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight */}
<Dropdown
label={variables.getMessage('settings:sections.appearance.font.weight.title')}
name="fontweight"
category="other"
items={[
{
value: '100',
text: variables.getMessage(fontWeight + '.thin'),
},
{
value: '200',
text: variables.getMessage(fontWeight + '.extra_light'),
},
{
value: '300',
text: variables.getMessage(fontWeight + '.light'),
},
{
value: '400',
text: variables.getMessage(fontWeight + '.normal'),
},
{
value: '500',
text: variables.getMessage(fontWeight + '.medium'),
},
{
value: '600',
text: variables.getMessage(fontWeight + '.semi_bold'),
},
{
value: '700',
text: variables.getMessage(fontWeight + '.bold'),
},
{
value: '800',
text: variables.getMessage(fontWeight + '.extra_bold'),
},
]}
/>
</Action>
</Row>
</PreferencesWrapper>
<PreferencesWrapper>
<Row>
<Content
title={variables.getMessage('settings:sections.appearance.font.title')}
subtitle={variables.getMessage('settings:sections.appearance.font.description')}
/>
<Action>
<Dropdown
label={variables.getMessage('settings:sections.appearance.font.style.title')}
name="fontstyle"
category="other"
items={[
{
value: 'normal',
text: variables.getMessage('settings:sections.appearance.font.style.normal'),
},
{
value: 'italic',
text: variables.getMessage('settings:sections.appearance.font.style.italic'),
},
{
value: 'oblique',
text: variables.getMessage('settings:sections.appearance.font.style.oblique'),
},
]}
/>
</Action>
</Row>
</PreferencesWrapper>
</>
);
};
export { FontOptions as default, FontOptions };

View File

@@ -0,0 +1,2 @@
export * from './Accessibility';
export * from './FontOptions';

View File

@@ -1,327 +0,0 @@
import { memo, useState } from 'react';
import variables from 'config/variables';
import { Checkbox, Dropdown, Radio, Slider, Text } from 'components/Form/Settings';
import {
Header,
Section,
Row,
Content,
Action,
PreferencesWrapper,
} from 'components/Layout/Settings';
import { useTab } from 'components/Elements/MainModal/backend/TabContext';
import { MdAccessibility } from 'react-icons/md';
import values from 'utils/data/slider_values.json';
function AppearanceOptions() {
const [accessibility, setAccessibility] = useState(false);
const [currentTheme, setCurrentTheme] = useState(localStorage.getItem('theme') || 'auto');
const { subSection } = useTab();
const ThemeSelection = () => {
return (
<Row>
<Content
title={variables.getMessage('settings:sections.appearance.theme.title')}
subtitle={variables.getMessage('settings:sections.appearance.theme.description')}
/>
<Action>
<Radio
name="theme"
options={[
{
name: variables.getMessage('settings:sections.appearance.theme.auto'),
value: 'auto',
},
{
name: variables.getMessage('settings:sections.appearance.theme.light'),
value: 'light',
},
{
name: variables.getMessage('settings:sections.appearance.theme.dark'),
value: 'dark',
},
]}
category="other"
/>
</Action>
</Row>
);
};
const FontOptions = () => {
const fontWeight = 'settings:sections.appearance.font.weight';
return (
<Row>
<Content
title={variables.getMessage('settings:sections.appearance.font.title')}
subtitle={variables.getMessage('settings:sections.appearance.font.description')}
/>
<Action>
<Checkbox
name="fontGoogle"
text={variables.getMessage('settings:sections.appearance.font.google')}
category="other"
/>
<Text
title={variables.getMessage('settings:sections.appearance.font.custom')}
name="font"
upperCaseFirst={true}
category="other"
/>
{/* names are taken from https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight */}
<Dropdown
label={variables.getMessage('settings:sections.appearance.font.weight.title')}
name="fontweight"
category="other"
items={[
{
value: '100',
text: variables.getMessage(fontWeight + '.thin'),
},
{
value: '200',
text: variables.getMessage(fontWeight + '.extra_light'),
},
{
value: '300',
text: variables.getMessage(fontWeight + '.light'),
},
{
value: '400',
text: variables.getMessage(fontWeight + '.normal'),
},
{
value: '500',
text: variables.getMessage(fontWeight + '.medium'),
},
{
value: '600',
text: variables.getMessage(fontWeight + '.semi_bold'),
},
{
value: '700',
text: variables.getMessage(fontWeight + '.bold'),
},
{
value: '800',
text: variables.getMessage(fontWeight + '.extra_bold'),
},
]}
/>
<Dropdown
label={variables.getMessage('settings:sections.appearance.font.style.title')}
name="fontstyle"
category="other"
items={[
{
value: 'normal',
text: variables.getMessage('settings:sections.appearance.font.style.normal'),
},
{
value: 'italic',
text: variables.getMessage('settings:sections.appearance.font.style.italic'),
},
{
value: 'oblique',
text: variables.getMessage('settings:sections.appearance.font.style.oblique'),
},
]}
/>
</Action>
</Row>
);
};
const WidgetStyle = () => {
return (
<Row final={true}>
<Content
title={variables.getMessage('settings:sections.appearance.style.title')}
subtitle={variables.getMessage('settings:sections.appearance.style.description')}
/>
<Action>
<Radio
name="widgetStyle"
element=".other"
options={[
{
name: variables.getMessage('settings:sections.appearance.style.legacy'),
value: 'legacy',
},
{
name: variables.getMessage('settings:sections.appearance.style.new'),
value: 'new',
},
]}
category="widgets"
/>
</Action>
</Row>
);
};
const AccessibilityOptions = () => {
return (
<Row final={true}>
<Content
title={variables.getMessage('settings:sections.appearance.accessibility.title')}
subtitle={variables.getMessage('settings:sections.appearance.accessibility.description')}
/>
<Action>
<Dropdown
label={variables.getMessage(
'settings:sections.appearance.accessibility.text_shadow.title',
)}
name="textBorder"
category="other"
items={[
{
value: 'new',
text: variables.getMessage(
'settings:sections.appearance.accessibility.text_shadow.new',
),
},
{
value: 'true',
text: variables.getMessage(
'settings:sections.appearance.accessibility.text_shadow.old',
),
},
{
value: 'none',
text: variables.getMessage(
'settings:sections.appearance.accessibility.text_shadow.none',
),
},
]}
/>
<Checkbox
text={variables.getMessage('settings:sections.appearance.accessibility.animations')}
name="animations"
category="other"
/>
<Slider
title={variables.getMessage(
'settings:sections.appearance.accessibility.toast_duration',
)}
name="toastDisplayTime"
default="2500"
step="100"
min="500"
max="5000"
marks={values.toast}
display={
' ' + variables.getMessage('settings:sections.appearance.accessibility.milliseconds')
}
/>
</Action>
</Row>
);
};
const newThemeSelector = (currentTheme) => {
const themes = [
{
example: 'light.jpg',
name: variables.getMessage('settings:sections.appearance.theme.light'),
value: 'light',
},
{
example: 'dark.jpg',
name: variables.getMessage('settings:sections.appearance.theme.dark'),
value: 'dark',
},
{
example: 'light.jpg',
name: variables.getMessage('settings:sections.appearance.theme.auto'),
value: 'auto',
},
];
console.log(currentTheme);
const ThemeCard = ({ example, name, value }) => {
return (
<div
class={
'flex flex-col items-center rounded p-2 hover:bg-background-light hover:dark:bg-background-dark cursor-pointer' +
(currentTheme === value ? ' border-2 border-neutral-100' : '')
}
>
<img src={`theme-examples/${example}`} alt="Light theme" class="rounded-t" />
<div class="py-4">{name}</div>
</div>
);
};
return (
<div class="bg-modal-content-light dark:bg-modal-content-dark p-10 rounded divide-gray-500 flex flex-col gap-5">
<h1 class="text-3xl tracking-tight font-semibold">
{variables.getMessage('settings:sections.appearance.theme.title')}
</h1>
<p class="text-neutral-300">
{variables.getMessage('settings:sections.appearance.theme.description')}
</p>
<div class="grid grid-cols-3 items-center gap-28">
{themes.map((theme) => (
<ThemeCard key={theme.value} {...theme} />
))}
</div>
</div>
);
};
let header;
if (accessibility) {
header = (
<Header
title={variables.getMessage('settings:sections.appearance.title')}
secondaryTitle={variables.getMessage('settings:sections.appearance.accessibility.title')}
goBack={() => setAccessibility(false)}
report={false}
/>
);
} else {
header = (
<Header title={variables.getMessage('settings:sections.appearance.title')} report={false} />
);
}
return (
<>
{/*{header}*/}
{subSection === 'accessibility' ? (
<AccessibilityOptions />
) : (
<>
<Section
id="accessibility"
title={variables.getMessage('settings:sections.appearance.accessibility.title')}
subtitle={variables.getMessage(
'settings:sections.appearance.accessibility.description',
)}
icon={<MdAccessibility />}
onClick={() => setAccessibility(true)}
/>
{newThemeSelector(currentTheme)}
<PreferencesWrapper>
<ThemeSelection />
</PreferencesWrapper>
<PreferencesWrapper>
<FontOptions />
</PreferencesWrapper>
<PreferencesWrapper>
<WidgetStyle />
</PreferencesWrapper>
</>
)}
</>
);
}
const MemoizedAppearanceOptions = memo(AppearanceOptions);
export { MemoizedAppearanceOptions as default, MemoizedAppearanceOptions as AppearanceOptions };

View File

@@ -1,6 +1,5 @@
export * from './About';
export * from './Advanced';
export * from './Appearance';
export * from './Changelog';
export * from './Experimental';
export * from './Language';

View File

@@ -13,11 +13,11 @@ import { MessageOptions } from 'features/message';
import { BackgroundOptions } from 'features/background';
import { SearchOptions } from 'features/search';
import { WeatherOptions } from 'features/weather';
import { AppearanceOptions } from 'features/appearance';
import { Stats } from 'features/stats';
import {
About,
AdvancedOptions,
AppearanceOptions,
Changelog,
ExperimentalOptions,
LanguageOptions,

View File

@@ -11,7 +11,7 @@ import defaults from './options/default';
import './weather.scss';
const WeatherWidget = () => {
const WeatherWidget = (props) => {
const [location, setLocation] = useState(localStorage.getItem('location') || defaults.location);
const [done, setDone] = useState(false);
const [weatherData, setWeatherData] = useState(null);
@@ -48,7 +48,7 @@ const WeatherWidget = () => {
const weatherType = localStorage.getItem('weatherType') || defaults.weatherType;
if (!done) {
return <WeatherSkeleton weatherType={weatherType} />;
return <WeatherSkeleton weatherType={weatherType} preview={true} />;
}
if (!weatherData) {

View File

@@ -1,8 +1,15 @@
import { memo } from 'react';
function WeatherSkeleton({ weatherType }) {
function WeatherSkeleton({ weatherType, preview }) {
return (
<div className="weather skeleton">
<div
className="weather skeleton"
style={{
position: preview ? 'relative' : '',
right: preview ? '0' : '',
bottom: preview ? '0' : '',
}}
>
<div className="weatherCore">
<div className="iconAndTemps">
<div className="weathericon">

View File

@@ -3,7 +3,9 @@ import { MdAutoAwesome } from 'react-icons/md';
import { Header, Row, Content, Action, PreferencesWrapper } from 'components/Layout/Settings';
import { useLocalStorageState } from 'utils/useLocalStorageState';
import { Radio, Dropdown, Checkbox } from 'components/Form/Settings';
import { Hero, Preview, Controls } from 'components/Layout/Settings/Hero';
import { TextField } from '@mui/material';
import { WeatherWidget } from 'features/weather';
import variables from 'config/variables';
const useWeatherSettings = () => {
@@ -176,26 +178,46 @@ const WeatherOptions = () => {
return (
<>
<Header
{/*<Header
title={variables.getMessage(`${WEATHER_SECTION}.title`)}
setting="weatherEnabled"
category="widgets"
zoomSetting="zoomWeather"
zoomCategory="weather"
visibilityToggle={true}
/>
<PreferencesWrapper
/>*/}
<Hero>
<Preview>
<WeatherWidget />
</Preview>
<Controls
setting="weatherEnabled"
category="widgets"
zoomSetting="zoomWeather"
zoomCategory="weather"
visibilityToggle={true}
/>
</Hero>
{/*<PreferencesWrapper
setting="weatherEnabled"
zoomSetting="zoomWeather"
zoomCategory="weather"
visibilityToggle={true}
>
/>*/}
<h1 className="py-3 uppercase tracking-tight text-neutral-300">options</h1>
<PreferencesWrapper setting="weatherEnabled">
<WidgetType />
{/* https://stackoverflow.com/a/65328486 when using inputs it may defocus so we do the {} instead of <> */}
{LocationSetting()}
<TemperatureFormat />
{weatherType === '4' && <CustomOptions />}
</PreferencesWrapper>
{/* https://stackoverflow.com/a/65328486 when using inputs it may defocus so we do the {} instead of <> */}
<PreferencesWrapper setting="weatherEnabled">{LocationSetting()}</PreferencesWrapper>
<PreferencesWrapper setting="weatherEnabled">
<TemperatureFormat />
</PreferencesWrapper>
{weatherType === '4' && (
<PreferencesWrapper setting="weatherEnabled">
<CustomOptions />
</PreferencesWrapper>
)}
</>
);
};