266 lines
10 KiB
TypeScript
Executable File
266 lines
10 KiB
TypeScript
Executable File
'use client';
|
|
|
|
import React, { useState, useContext, useEffect } from 'react';
|
|
import { Panel } from 'primereact/panel';
|
|
import { InputSwitch } from 'primereact/inputswitch';
|
|
import { Slider } from 'primereact/slider';
|
|
import { Dropdown } from 'primereact/dropdown';
|
|
import { Button } from 'primereact/button';
|
|
import { Message } from 'primereact/message';
|
|
import { LayoutContext } from '../../layout/context/layoutcontext';
|
|
|
|
interface AtlantisAccessibilityControlsProps {
|
|
className?: string;
|
|
}
|
|
|
|
interface AccessibilitySettings {
|
|
fontSize: number;
|
|
highContrast: boolean;
|
|
reduceMotion: boolean;
|
|
screenReader: boolean;
|
|
keyboardNav: boolean;
|
|
}
|
|
|
|
const AtlantisAccessibilityControls: React.FC<AtlantisAccessibilityControlsProps> = ({
|
|
className = ''
|
|
}) => {
|
|
const { layoutConfig, setLayoutConfig } = useContext(LayoutContext);
|
|
const [settings, setSettings] = useState<AccessibilitySettings>({
|
|
fontSize: 14,
|
|
highContrast: false,
|
|
reduceMotion: false,
|
|
screenReader: false,
|
|
keyboardNav: true
|
|
});
|
|
|
|
// Options de thème pour l'accessibilité
|
|
const contrastThemes = [
|
|
{ label: 'Thème normal', value: 'magenta' },
|
|
{ label: 'Contraste élevé - Bleu', value: 'blue' },
|
|
{ label: 'Contraste élevé - Sombre', value: 'dark' }
|
|
];
|
|
|
|
// Appliquer les paramètres d'accessibilité
|
|
useEffect(() => {
|
|
// Appliquer la taille de police via scale Atlantis
|
|
setLayoutConfig(prev => ({
|
|
...prev,
|
|
scale: settings.fontSize
|
|
}));
|
|
|
|
// Appliquer le thème de contraste
|
|
if (settings.highContrast) {
|
|
setLayoutConfig(prev => ({
|
|
...prev,
|
|
theme: 'blue', // Thème avec meilleur contraste
|
|
colorScheme: 'dark'
|
|
}));
|
|
}
|
|
|
|
// Classes CSS pour les animations
|
|
const rootElement = document.documentElement;
|
|
if (settings.reduceMotion) {
|
|
rootElement.style.setProperty('--transition-duration', '0ms');
|
|
} else {
|
|
rootElement.style.removeProperty('--transition-duration');
|
|
}
|
|
|
|
// Annoncer les changements pour les lecteurs d'écran
|
|
if (settings.screenReader) {
|
|
announceChange('Paramètres d\'accessibilité mis à jour');
|
|
}
|
|
|
|
}, [settings, setLayoutConfig]);
|
|
|
|
// Fonction d'annonce pour lecteurs d'écran
|
|
const announceChange = (message: string) => {
|
|
const announcement = document.createElement('div');
|
|
announcement.setAttribute('aria-live', 'polite');
|
|
announcement.setAttribute('aria-atomic', 'true');
|
|
announcement.className = 'sr-only';
|
|
announcement.textContent = message;
|
|
document.body.appendChild(announcement);
|
|
setTimeout(() => document.body.removeChild(announcement), 1000);
|
|
};
|
|
|
|
const updateSetting = (key: keyof AccessibilitySettings, value: any) => {
|
|
setSettings(prev => ({ ...prev, [key]: value }));
|
|
};
|
|
|
|
const resetToDefaults = () => {
|
|
setSettings({
|
|
fontSize: 14,
|
|
highContrast: false,
|
|
reduceMotion: false,
|
|
screenReader: false,
|
|
keyboardNav: true
|
|
});
|
|
|
|
setLayoutConfig(prev => ({
|
|
...prev,
|
|
scale: 14,
|
|
theme: 'magenta',
|
|
colorScheme: 'dark'
|
|
}));
|
|
|
|
announceChange('Paramètres d\'accessibilité réinitialisés');
|
|
};
|
|
|
|
return (
|
|
<Panel
|
|
header="Paramètres d'accessibilité"
|
|
toggleable
|
|
collapsed
|
|
className={`card ${className}`}
|
|
pt={{
|
|
header: { className: 'surface-100' },
|
|
content: { className: 'surface-50' }
|
|
}}
|
|
>
|
|
<div className="grid">
|
|
{/* Taille de police */}
|
|
<div className="col-12 md:col-6">
|
|
<div className="field">
|
|
<label htmlFor="fontSize" className="font-semibold text-color">
|
|
Taille de police: {settings.fontSize}px
|
|
</label>
|
|
<Slider
|
|
id="fontSize"
|
|
value={settings.fontSize}
|
|
onChange={(e) => updateSetting('fontSize', e.value)}
|
|
min={12}
|
|
max={20}
|
|
step={1}
|
|
className="w-full mt-2"
|
|
/>
|
|
<div className="flex justify-content-between text-xs text-color-secondary mt-1">
|
|
<span>Petit</span>
|
|
<span>Normal</span>
|
|
<span>Grand</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Contraste élevé */}
|
|
<div className="col-12 md:col-6">
|
|
<div className="field">
|
|
<label htmlFor="highContrast" className="font-semibold text-color">
|
|
Mode contraste élevé
|
|
</label>
|
|
<div className="flex align-items-center gap-2 mt-2">
|
|
<InputSwitch
|
|
id="highContrast"
|
|
checked={settings.highContrast}
|
|
onChange={(e) => updateSetting('highContrast', e.value)}
|
|
/>
|
|
<span className="text-sm text-color-secondary">
|
|
{settings.highContrast ? 'Activé' : 'Désactivé'}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Réduction des animations */}
|
|
<div className="col-12 md:col-6">
|
|
<div className="field">
|
|
<label htmlFor="reduceMotion" className="font-semibold text-color">
|
|
Réduire les animations
|
|
</label>
|
|
<div className="flex align-items-center gap-2 mt-2">
|
|
<InputSwitch
|
|
id="reduceMotion"
|
|
checked={settings.reduceMotion}
|
|
onChange={(e) => updateSetting('reduceMotion', e.value)}
|
|
/>
|
|
<span className="text-sm text-color-secondary">
|
|
Pour les utilisateurs sensibles au mouvement
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Mode lecteur d'écran */}
|
|
<div className="col-12 md:col-6">
|
|
<div className="field">
|
|
<label htmlFor="screenReader" className="font-semibold text-color">
|
|
Mode lecteur d'écran
|
|
</label>
|
|
<div className="flex align-items-center gap-2 mt-2">
|
|
<InputSwitch
|
|
id="screenReader"
|
|
checked={settings.screenReader}
|
|
onChange={(e) => updateSetting('screenReader', e.value)}
|
|
/>
|
|
<span className="text-sm text-color-secondary">
|
|
Annonces vocales activées
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Informations et aide */}
|
|
<div className="mt-4">
|
|
<Message
|
|
severity="info"
|
|
text="Ces paramètres améliorent l'accessibilité selon vos besoins"
|
|
className="w-full"
|
|
/>
|
|
</div>
|
|
|
|
{/* Raccourcis clavier */}
|
|
<div className="card mt-3">
|
|
<h6 className="mt-0 mb-3 text-color">Raccourcis clavier disponibles</h6>
|
|
<div className="grid text-sm">
|
|
<div className="col-12 md:col-6">
|
|
<div className="flex align-items-center gap-2 mb-2">
|
|
<kbd className="bg-surface-200 text-color px-2 py-1 border-round text-xs">Tab</kbd>
|
|
<span className="text-color-secondary">Naviguer entre les éléments</span>
|
|
</div>
|
|
<div className="flex align-items-center gap-2 mb-2">
|
|
<kbd className="bg-surface-200 text-color px-2 py-1 border-round text-xs">Entrée</kbd>
|
|
<span className="text-color-secondary">Activer un élément</span>
|
|
</div>
|
|
</div>
|
|
<div className="col-12 md:col-6">
|
|
<div className="flex align-items-center gap-2 mb-2">
|
|
<kbd className="bg-surface-200 text-color px-2 py-1 border-round text-xs">Échap</kbd>
|
|
<span className="text-color-secondary">Fermer un dialogue</span>
|
|
</div>
|
|
<div className="flex align-items-center gap-2 mb-2">
|
|
<kbd className="bg-surface-200 text-color px-2 py-1 border-round text-xs">↑↓</kbd>
|
|
<span className="text-color-secondary">Naviguer dans les listes</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Actions */}
|
|
<div className="flex justify-content-between align-items-center mt-4">
|
|
<Button
|
|
label="Réinitialiser"
|
|
icon="pi pi-refresh"
|
|
className="p-button-outlined"
|
|
onClick={resetToDefaults}
|
|
/>
|
|
|
|
<div className="flex align-items-center gap-2">
|
|
<i className="pi pi-info-circle text-primary" />
|
|
<span className="text-sm text-color-secondary">
|
|
Paramètres sauvegardés automatiquement
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Région pour les annonces lecteurs d'écran */}
|
|
<div
|
|
aria-live="polite"
|
|
aria-atomic="true"
|
|
className="sr-only"
|
|
id="accessibility-announcements"
|
|
/>
|
|
</Panel>
|
|
);
|
|
};
|
|
|
|
export default AtlantisAccessibilityControls; |