7.5 KiB
		
	
	
	
	
	
	
	
			
		
		
	
	
			7.5 KiB
		
	
	
	
	
	
	
	
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 
CdkAccordionModuled'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-expandedsur les headers - ✅ 
aria-controlsetaria-labelledbypour les panels - ✅ Navigation clavier complète (Tab, Enter, Espace)
 - ✅ Focus visible avec outline personnalisé
 - ✅ Rôle 
regionsur 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.OnPushsur 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-accordionparov-graph-settings-accordion - Suppression des imports PrimeNG
 - Nettoyage des styles deep selector
 - Ajout des méthodes 
onConfigChange()etonResetAll() 
Fichiers conservés (inchangés)
src/app/graph/ui/sections/filters-section.component.tssrc/app/graph/ui/sections/groups-section.component.tssrc/app/graph/ui/sections/display-section.component.tssrc/app/graph/ui/sections/forces-section.component.tssrc/app/graph/graph-settings.service.tssrc/app/graph/graph-settings.types.ts
🎨 Détails de styling
Structure des items
.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
.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
.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
<ov-graph-settings-accordion
  [config]="config()"
  [showCollapseToggle]="true"
  (configChange)="onConfigChange($event)"
  (animateRequested)="animateRequested.emit()">
</ov-graph-settings-accordion>
Inputs
config: GraphConfig(required) - Configuration du graphshowCollapseToggle: boolean(optional, default: false) - Afficher le toggle global
Outputs
configChange: Partial<GraphConfig>- Changement de configurationanimateRequested: void- Demande d'animation (section Display)
🧪 Tests effectués
✅ Fonctionnels
- Ouverture/fermeture de chaque section
 - Ouverture multiple simultanée
 - Persistance après rechargement
 - Toggle "Collapse all / Expand all"
 - Rendu conditionnel du contenu
 
✅ Accessibilité
- Navigation au clavier (Tab)
 - Activation avec Enter/Espace
 - Focus visible sur les headers
 - Attributs ARIA corrects
 - Lecteur d'écran compatible
 
✅ Visuel
- Animation fluide sans jank
 - Dark mode cohérent
 - Light mode cohérent
 - Responsive mobile (max-width: 768px)
 - Hover states corrects
 
✅ Performance
- Pas de re-render inutile
 - OnPush fonctionne correctement
 - Contenu lazy-loaded
 - Build production OK
 
🚀 Améliorations futures possibles
- Analytics : Émettre un event 
settingsPanelOpened(sectionId)pour tracking - Tests unitaires : Ajouter des specs pour la persistance
 - Animation avancée : Transition de hauteur avec 
@angular/animations - Drag & Drop : Réorganiser l'ordre des sections (CDK Drag Drop)
 - 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() :
this.settingsService.save({
  [collapseKey]: !expanded  // Inversion ici
});
Effect avec allowSignalWrites
Nécessaire pour mettre à jour openSectionsSet dans un effect :
effect(() => {
  // ... lecture de config()
  this.openSectionsSet.set(openSet);
}, { allowSignalWrites: true });
🔗 Références
- Angular CDK Accordion
 - CSS Grid Animation Technique
 - ARIA Accordion Pattern
 - ObsiViewer Graph Settings Service
 
Date de création : 2025-10-02
Auteur : Cascade AI
Version Angular : 20.3.x
Version CDK : 20.3.2