# Graph Settings Accordion - Implementation CDK ## 📋 Vue d'ensemble Remplacement de l'accordéon PrimeNG par un accordéon headless basé sur `@angular/cdk/accordion` avec styling Tailwind pour le panneau Graph settings d'ObsiViewer. ## ✅ Fonctionnalités implémentées ### 1. **Accordéon CDK Headless** - ✅ Utilisation de `CdkAccordionModule` d'Angular CDK - ✅ 4 sections indépendantes : Filters, Groups, Display, Forces - ✅ Ouverture/fermeture multiple (multi-accordion) - ✅ Aucune dépendance PrimeNG ou Material visuel ### 2. **Animation fluide** - ✅ Technique CSS Grid avec transition `grid-template-rows: 0fr → 1fr` - ✅ Durée : 200ms avec easing `ease-out` - ✅ Pas de jank, animation performante - ✅ Chevron rotatif (180deg) synchronisé avec l'état ### 3. **Accessibilité (A11y)** - ✅ `aria-expanded` sur les headers - ✅ `aria-controls` et `aria-labelledby` pour les panels - ✅ Navigation clavier complète (Tab, Enter, Espace) - ✅ Focus visible avec outline personnalisé - ✅ Rôle `region` sur les panels ### 4. **Persistance de l'état** - ✅ État ouvert/fermé persisté via `GraphSettingsService` - ✅ Utilisation des clés existantes : `collapse-filter`, `collapse-color-groups`, etc. - ✅ Synchronisation bidirectionnelle avec `.obsidian/graph.json` - ✅ État initial : section "Filters" ouverte par défaut - ✅ Restauration automatique au rechargement ### 5. **Dark/Light Mode** - ✅ Support complet du mode sombre via classe `.dark` - ✅ Couleurs adaptatives pour borders, backgrounds, text - ✅ Gradients et shadows ajustés selon le thème - ✅ Cohérence avec le design system d'ObsiViewer ### 6. **Performance** - ✅ Rendu conditionnel : contenu monté uniquement si panel ouvert (`@if (accordionItem.expanded)`) - ✅ `ChangeDetectionStrategy.OnPush` sur tous les composants - ✅ Signals pour la réactivité optimale - ✅ Pas de re-render massif lors des interactions ### 7. **Bonus : Toggle Collapse All / Expand All** - ✅ Bouton optionnel via input `[showCollapseToggle]="true"` - ✅ Détection automatique de l'état (tous ouverts/fermés) - ✅ Persistance de l'action sur toutes les sections ## 📁 Fichiers créés/modifiés ### Nouveau fichier ``` src/components/graph-settings/graph-settings-accordion.component.ts ``` - Composant standalone avec CDK accordion - 4 sections dynamiques avec icônes SVG - Logique de persistance intégrée - Styles Tailwind inline ### Fichier modifié ``` src/app/graph/ui/settings-panel.component.ts ``` - Remplacement de `p-accordion` par `ov-graph-settings-accordion` - Suppression des imports PrimeNG - Nettoyage des styles deep selector - Ajout des méthodes `onConfigChange()` et `onResetAll()` ### Fichiers conservés (inchangés) - `src/app/graph/ui/sections/filters-section.component.ts` - `src/app/graph/ui/sections/groups-section.component.ts` - `src/app/graph/ui/sections/display-section.component.ts` - `src/app/graph/ui/sections/forces-section.component.ts` - `src/app/graph/graph-settings.service.ts` - `src/app/graph/graph-settings.types.ts` ## 🎨 Détails de styling ### Structure des items ```css .accordion-item { border-radius: 1rem; border: 1px solid rgba(113, 113, 122, 0.3); background-color: rgba(255, 255, 255, 0.5); } .accordion-item-expanded { border-color: rgba(59, 130, 246, 0.45); box-shadow: 0 12px 24px -16px rgba(15, 23, 42, 0.35); } ``` ### Header ```css .accordion-header { padding: 0.95rem 1.2rem; background: linear-gradient(180deg, rgba(248, 250, 252, 0.9) 0%, rgba(248, 250, 252, 0.6) 100%); font-size: 0.9rem; font-weight: 600; } ``` ### Animation Grid ```css .accordion-panel { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 0.2s ease-out; } .accordion-panel-expanded { grid-template-rows: 1fr; } .accordion-panel-inner { min-height: 0; /* Critical pour le clip */ } ``` ## 🔧 Utilisation ### Dans le template ```html ``` ### Inputs - `config: GraphConfig` (required) - Configuration du graph - `showCollapseToggle: boolean` (optional, default: false) - Afficher le toggle global ### Outputs - `configChange: Partial` - Changement de configuration - `animateRequested: void` - Demande d'animation (section Display) ## 🧪 Tests effectués ### ✅ Fonctionnels - [x] Ouverture/fermeture de chaque section - [x] Ouverture multiple simultanée - [x] Persistance après rechargement - [x] Toggle "Collapse all / Expand all" - [x] Rendu conditionnel du contenu ### ✅ Accessibilité - [x] Navigation au clavier (Tab) - [x] Activation avec Enter/Espace - [x] Focus visible sur les headers - [x] Attributs ARIA corrects - [x] Lecteur d'écran compatible ### ✅ Visuel - [x] Animation fluide sans jank - [x] Dark mode cohérent - [x] Light mode cohérent - [x] Responsive mobile (max-width: 768px) - [x] Hover states corrects ### ✅ Performance - [x] Pas de re-render inutile - [x] OnPush fonctionne correctement - [x] Contenu lazy-loaded - [x] Build production OK ## 🚀 Améliorations futures possibles 1. **Analytics** : Émettre un event `settingsPanelOpened(sectionId)` pour tracking 2. **Tests unitaires** : Ajouter des specs pour la persistance 3. **Animation avancée** : Transition de hauteur avec `@angular/animations` 4. **Drag & Drop** : Réorganiser l'ordre des sections (CDK Drag Drop) 5. **Keyboard shortcuts** : Ctrl+1/2/3/4 pour ouvrir les sections ## 📊 Métriques - **Lignes de code** : ~400 lignes (accordion component) - **Dépendances ajoutées** : 0 (CDK déjà présent) - **Dépendances retirées** : PrimeNG accordion (si non utilisé ailleurs) - **Bundle size impact** : -X KB (à mesurer) - **Performance** : Aucune régression détectée ## 🎯 Critères d'acceptation | Critère | Status | |---------|--------| | 4 sections rendues dans accordéon CDK | ✅ | | Styling Tailwind sans Material/PrimeNG | ✅ | | Animation fluide (grid 0fr→1fr) | ✅ | | État persisté et restauré | ✅ | | A11y complet (aria, keyboard) | ✅ | | Dark/Light cohérent | ✅ | | Pas de jank/reflow lourd | ✅ | | Standalone + OnPush | ✅ | | Rendu conditionnel | ✅ | ## 📝 Notes techniques ### Pourquoi CSS Grid au lieu de max-height ? - **Grid 0fr→1fr** : Hauteur automatique, pas besoin de connaître la hauteur exacte - **max-height** : Nécessite une valeur arbitraire (ex: 1000px), moins propre - **Performance** : Grid est optimisé par les navigateurs modernes ### Logique de persistance inversée Les clés dans `graph.json` sont nommées `collapse-*` (true = fermé). Notre accordéon utilise la logique inverse (true = ouvert). Mapping dans `persistState()` : ```typescript this.settingsService.save({ [collapseKey]: !expanded // Inversion ici }); ``` ### Effect avec allowSignalWrites Nécessaire pour mettre à jour `openSectionsSet` dans un effect : ```typescript effect(() => { // ... lecture de config() this.openSectionsSet.set(openSet); }, { allowSignalWrites: true }); ``` ## 🔗 Références - [Angular CDK Accordion](https://material.angular.io/cdk/accordion/overview) - [CSS Grid Animation Technique](https://css-tricks.com/css-grid-can-do-auto-height-transitions/) - [ARIA Accordion Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/accordion/) - [ObsiViewer Graph Settings Service](../src/app/graph/graph-settings.service.ts) --- **Date de création** : 2025-10-02 **Auteur** : Cascade AI **Version Angular** : 20.3.x **Version CDK** : 20.3.2