manual improvement 1
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
'use client';
|
||||
/*
|
||||
* The ONLY React subtree subscribed to useColorScheme(). When the user picks a mode:
|
||||
* 1. setMode(...) writes MUI's mode
|
||||
* 2. the resolved scheme lands on <html data-mui-color-scheme>
|
||||
* 3. CSS custom properties resolve to new values → browser repaints
|
||||
* 4. React re-renders ONLY this component (which segment reads as selected)
|
||||
* Nothing above it in the tree is touched.
|
||||
*/
|
||||
import { FunctionComponent } from 'react';
|
||||
import { Stack, ToggleButton, ToggleButtonGroup, Typography } from '@mui/material';
|
||||
import { useColorScheme } from '@mui/material/styles';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import AppIcon from '@/components/common/AppIcon';
|
||||
|
||||
type ThemeMode = 'light' | 'dark' | 'system';
|
||||
|
||||
const MODES: ReadonlyArray<{ mode: ThemeMode; icon: string; key: string }> = [
|
||||
{ mode: 'light', icon: 'light_mode', key: 'theme_light' },
|
||||
{ mode: 'dark', icon: 'dark_mode', key: 'theme_dark' },
|
||||
{ mode: 'system', icon: 'system_mode', key: 'theme_system' },
|
||||
];
|
||||
|
||||
/**
|
||||
* The app's one appearance control: a three-way segmented choice — روشن / تیره / سیستم.
|
||||
*
|
||||
* It replaced an on/off `Switch`, which was wrong twice over. A switch is a **boolean**, so it
|
||||
* could not express "follow my phone" — yet `system` is the app's actual default (`ThemeProvider`
|
||||
* `defaultMode`), which left the control silently lying about the current state on a first visit.
|
||||
* And a lone switch beside a label reads like a feature being enabled rather than a look being
|
||||
* chosen. Three labelled segments say what each option does and which one is live.
|
||||
*
|
||||
* This is the one place `mode` is the right source rather than `colorScheme`: `mode` is the user's
|
||||
* **choice** (and can be `'system'`), `colorScheme` is only the resolved result. The `?? 'system'`
|
||||
* fallback is hydration-safe — MUI reports `mode` as `undefined` until it mounts, and the server,
|
||||
* the first client render, and the pre-mount state all agree on the same default.
|
||||
* @component ThemeModeSetting
|
||||
*/
|
||||
const ThemeModeSetting: FunctionComponent = () => {
|
||||
const { mode, setMode } = useColorScheme();
|
||||
const t = useTranslations('common');
|
||||
|
||||
return (
|
||||
<Stack sx={{ gap: 1.25, px: 2, py: 1.5 }}>
|
||||
<Stack direction="row" sx={{ alignItems: 'center', gap: 1.5 }}>
|
||||
<AppIcon icon="appearance" size={18} color="var(--bal-text-secondary)" aria-hidden="true" />
|
||||
<Typography variant="body2">{t('appearance')}</Typography>
|
||||
</Stack>
|
||||
|
||||
<ToggleButtonGroup
|
||||
exclusive
|
||||
fullWidth
|
||||
size="small"
|
||||
value={mode ?? 'system'}
|
||||
onChange={(_event, next: ThemeMode | null) => {
|
||||
// `exclusive` emits null when the active segment is tapped again — keep the choice.
|
||||
if (next) setMode(next);
|
||||
}}
|
||||
aria-label={t('appearance')}
|
||||
sx={{ '& .MuiToggleButton-root': { gap: 0.75, paddingBlock: 0.75, borderRadius: 'var(--bal-radius-sm)' } }}
|
||||
>
|
||||
{MODES.map((option) => (
|
||||
<ToggleButton key={option.mode} value={option.mode}>
|
||||
<AppIcon icon={option.icon} size={16} aria-hidden="true" />
|
||||
<Typography variant="caption" sx={{ fontWeight: 500 }}>
|
||||
{t(option.key)}
|
||||
</Typography>
|
||||
</ToggleButton>
|
||||
))}
|
||||
</ToggleButtonGroup>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export default ThemeModeSetting;
|
||||
Reference in New Issue
Block a user