Initial commit

This commit is contained in:
Olivier CROGUENNEC
2026-06-13 14:57:15 +02:00
commit 48ed7fe65e
209 changed files with 49979 additions and 0 deletions
+152
View File
@@ -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>
</>
);
}