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.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
.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 graph
- showCollapseToggle: boolean(optional, default: false) - Afficher le toggle global
Outputs
- configChange: Partial<GraphConfig>- Changement de configuration
- animateRequested: 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