Initial commit
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
import { useTheme } from '../context/ThemeContext.jsx';
|
||||
import { useUi } from '../context/UiContext.jsx';
|
||||
|
||||
/* ── Theme options ─────────────────────────────────────────── */
|
||||
const THEMES = [
|
||||
{
|
||||
mode: 'light',
|
||||
label: 'Clair',
|
||||
desc: 'Interface lumineuse',
|
||||
preview: (
|
||||
<svg viewBox="0 0 56 36" width="56" height="36" aria-hidden="true">
|
||||
<rect width="56" height="36" rx="5" fill="#f0f4ff"/>
|
||||
<rect x="2" y="2" width="12" height="32" rx="3" fill="#1e3a8a"/>
|
||||
<rect x="16" y="2" width="38" height="8" rx="2" fill="#ffffff" opacity=".9"/>
|
||||
<rect x="16" y="12" width="38" height="5" rx="2" fill="#c7d2e8"/>
|
||||
<rect x="16" y="19" width="28" height="5" rx="2" fill="#c7d2e8"/>
|
||||
<rect x="16" y="26" width="20" height="5" rx="2" fill="#c7d2e8"/>
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
mode: 'dark',
|
||||
label: 'Sombre',
|
||||
desc: 'Interface nocturne',
|
||||
preview: (
|
||||
<svg viewBox="0 0 56 36" width="56" height="36" aria-hidden="true">
|
||||
<rect width="56" height="36" rx="5" fill="#060e1f"/>
|
||||
<rect x="2" y="2" width="12" height="32" rx="3" fill="#0d1629"/>
|
||||
<rect x="16" y="2" width="38" height="8" rx="2" fill="#111c35" opacity=".9"/>
|
||||
<rect x="16" y="12" width="38" height="5" rx="2" fill="#1e3a6a"/>
|
||||
<rect x="16" y="19" width="28" height="5" rx="2" fill="#1e3a6a"/>
|
||||
<rect x="16" y="26" width="20" height="5" rx="2" fill="#1e3a6a"/>
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
mode: 'system',
|
||||
label: 'Système',
|
||||
desc: 'Suit les préférences OS',
|
||||
preview: (
|
||||
<svg viewBox="0 0 56 36" width="56" height="36" aria-hidden="true">
|
||||
<defs>
|
||||
<linearGradient id="split" x1="0" x2="1" y1="0" y2="0">
|
||||
<stop offset="50%" stopColor="#f0f4ff"/>
|
||||
<stop offset="50%" stopColor="#060e1f"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="56" height="36" rx="5" fill="url(#split)"/>
|
||||
<rect x="2" y="2" width="12" height="32" rx="3" fill="#1e3a8a"/>
|
||||
<rect x="16" y="2" width="18" height="8" rx="2" fill="#ffffff" opacity=".9"/>
|
||||
<rect x="36" y="2" width="18" height="8" rx="2" fill="#111c35" opacity=".9"/>
|
||||
<rect x="16" y="12" width="18" height="5" rx="2" fill="#c7d2e8"/>
|
||||
<rect x="36" y="12" width="18" height="5" rx="2" fill="#1e3a6a"/>
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
/* ── Font scale options ────────────────────────────────────── */
|
||||
const FONTS = [
|
||||
{
|
||||
scale: 'compact',
|
||||
label: 'Normal',
|
||||
desc: 'Interface compacte',
|
||||
sizes: { body: 12, table: 11 },
|
||||
},
|
||||
{
|
||||
scale: 'medium',
|
||||
label: 'Moyen',
|
||||
desc: 'Taille intermédiaire',
|
||||
sizes: { body: 13, table: 12 },
|
||||
},
|
||||
{
|
||||
scale: 'large',
|
||||
label: 'Grand',
|
||||
desc: 'Meilleure lisibilité',
|
||||
sizes: { body: 14, table: 13 },
|
||||
},
|
||||
];
|
||||
|
||||
/* ── Component ─────────────────────────────────────────────── */
|
||||
export default function Preferences() {
|
||||
const { mode, setMode } = useTheme();
|
||||
const { fontScale, setFontScale } = useUi();
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="topbar">
|
||||
<h2>Interface</h2>
|
||||
</div>
|
||||
|
||||
{/* ── Thème ──────────────────────────────────────────── */}
|
||||
<div className="card">
|
||||
<h3 style={{ margin: '0 0 4px' }}>Apparence</h3>
|
||||
<p style={{ margin: '0 0 16px', color: 'var(--text-muted)', fontSize: 'var(--fs-sm)' }}>
|
||||
Choisissez le thème visuel de l'application.
|
||||
</p>
|
||||
<div className="pref-options">
|
||||
{THEMES.map((t) => (
|
||||
<button
|
||||
key={t.mode}
|
||||
type="button"
|
||||
className={`pref-option${mode === t.mode ? ' active' : ''}`}
|
||||
onClick={() => setMode(t.mode)}
|
||||
aria-pressed={mode === t.mode}
|
||||
>
|
||||
{t.preview}
|
||||
<span style={{ fontWeight: 700, fontSize: 'var(--fs-sm)', marginTop: 4 }}>{t.label}</span>
|
||||
<span style={{ fontSize: 'var(--fs-xs)', color: 'var(--text-muted)', fontWeight: 400 }}>{t.desc}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ── Police ─────────────────────────────────────────── */}
|
||||
<div className="card">
|
||||
<h3 style={{ margin: '0 0 4px' }}>Taille du texte</h3>
|
||||
<p style={{ margin: '0 0 16px', color: 'var(--text-muted)', fontSize: 'var(--fs-sm)' }}>
|
||||
Le niveau <strong>Grand</strong> est recommandé pour les personnes malvoyantes.
|
||||
Les niveaux inférieurs permettent d'afficher plus de données à l'écran.
|
||||
</p>
|
||||
<div className="pref-options">
|
||||
{FONTS.map((f) => (
|
||||
<button
|
||||
key={f.scale}
|
||||
type="button"
|
||||
className={`pref-option${fontScale === f.scale ? ' active' : ''}`}
|
||||
onClick={() => setFontScale(f.scale)}
|
||||
aria-pressed={fontScale === f.scale}
|
||||
>
|
||||
{/* Live-size text preview */}
|
||||
<div style={{
|
||||
display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 4,
|
||||
width: '100%', padding: '8px 0 4px',
|
||||
borderBottom: '1px solid var(--border)', marginBottom: 4,
|
||||
}}>
|
||||
<span className="font-preview" style={{ fontSize: f.sizes.body }}>
|
||||
Aa — {f.label}
|
||||
</span>
|
||||
<span style={{ fontSize: f.sizes.table, color: 'var(--text-muted)' }}>
|
||||
Tableau {f.sizes.table}px · Corps {f.sizes.body}px
|
||||
</span>
|
||||
</div>
|
||||
<span style={{ fontWeight: 700, fontSize: 'var(--fs-sm)', marginTop: 2 }}>{f.label}</span>
|
||||
<span style={{ fontSize: 'var(--fs-xs)', color: 'var(--text-muted)', fontWeight: 400 }}>{f.desc}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user