@synertech/ui 0.40.1 → 0.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -156,6 +156,16 @@ import { cn } from "@synertech/ui";
156
156
  - `ScatterChart` - Corrélation entre deux variables numériques (3 séries max)
157
157
  - `StatCard` / `StatCardGroup` - Valeur unique avec variation et sparkline
158
158
  - `Sparkline` - Courbe sans axe, pour une cellule ou une carte
159
+ - `Heatmap` - Une grandeur sur une grille (jour × heure, cohortes, matrice)
160
+ - `BulletChart` - Réalisé, objectif et plages qualitatives sur une seule ligne
161
+ - `ChartFacets` - Petits multiples : un même graphique répété, à échelle partagée
162
+ - `ChartScaleLegend` - La légende d'une rampe de couleur
163
+ - `WaterfallChart` - Comment on passe de A à B : chaque contribution, positive ou négative
164
+ - `FunnelChart` - Des étapes ordonnées et leur déperdition (rampe ordinale)
165
+ - `DumbbellChart` - Avant → après par élément, ou une plage min–max
166
+ - `BoxPlot` - Médiane, quartiles, moustaches et valeurs aberrantes
167
+ - `CandlestickChart` - Ouverture, haut, bas, clôture (couleurs de statut)
168
+ - `GanttChart` - Des tâches dans le temps, avec dépendances et jalons
159
169
 
160
170
  ### Feedback & Overlays
161
171
 
@@ -586,6 +596,26 @@ const data = [
586
596
  showValues
587
597
  />
588
598
 
599
+ // Barres divergentes : l'écart à une référence
600
+ <BarChart
601
+ data={gaps}
602
+ x={{ key: "region" }}
603
+ series={[{ key: "gap" }]}
604
+ orientation="horizontal"
605
+ diverging // true | un nombre (le centre) | une clé de série
606
+ y={{ label: "Écart à la cible (%)" }}
607
+ />
608
+
609
+ // Échelle d'accord : la série nommée est à cheval sur le centre
610
+ <BarChart
611
+ data={survey}
612
+ x={{ key: "question" }}
613
+ series={[{ key: "awful" }, { key: "bad" }, { key: "ok" }, { key: "good" }, { key: "great" }]}
614
+ orientation="horizontal"
615
+ stack="stacked"
616
+ diverging="ok"
617
+ />
618
+
589
619
  // Camembert ou anneau
590
620
  <PieChart
591
621
  data={sources}
@@ -604,6 +634,118 @@ const data = [
604
634
  bubble={{ key: "clients", range: [4, 18] }}
605
635
  trendLine
606
636
  />
637
+
638
+ // Histogramme — `bins` lit x.key comme une mesure, la range et la compte
639
+ <BarChart
640
+ data={sessions} // [{ duration: 42 }, { duration: 118 }, ...]
641
+ x={{ key: "duration", label: "Durée (s)" }}
642
+ series={[{ key: "sessions", label: "Sessions" }]} // nomme le COMPTE
643
+ bins={12} // ou true (auto), ou [0, 15, 30, 60, 120]
644
+ />
645
+
646
+ // Barres + ligne — une série typée, un seul axe
647
+ <BarChart
648
+ data={monthly}
649
+ x={{ key: "month" }}
650
+ series={[
651
+ { key: "commandes", label: "Commandes" },
652
+ { key: "retours", label: "Retours", type: "line" },
653
+ ]}
654
+ curve="monotone"
655
+ />
656
+ ```
657
+
658
+ ```tsx
659
+ // Carte de chaleur — une ligne de données par case
660
+ <Heatmap
661
+ data={load} // [{ day, hour, sessions }, ...]
662
+ x={{ key: "hour", label: "Heure" }}
663
+ y={{ key: "day", label: "Jour" }}
664
+ value="sessions"
665
+ scale="sequential" // "sequential" | "diverging" (+ center)
666
+ steps={undefined} // la rampe reste continue tant qu'on ne la découpe pas
667
+ legendLabel="Sessions" // la légende d'échelle est affichée par défaut
668
+ showValues
669
+ tableView="toggle" // la grille pivotée : une ligne par catégorie de rangée
670
+ />
671
+
672
+ // Graphique à puce — réalisé, objectif, plages
673
+ <BulletChart
674
+ data={[{ measure: "Revenu", value: 38400, target: 42000, bands: [20000, 34000, 50000] }]}
675
+ x={{ key: "measure" }}
676
+ value="value"
677
+ target="target"
678
+ ranges="bands" // bornes croissantes, peintes en gris derrière la barre
679
+ normalize="row" // "row" (par défaut) | "shared" → ramène l'axe des valeurs
680
+ />
681
+
682
+ // Cascade — chaque pas flotte entre le cumul avant et après lui
683
+ <WaterfallChart
684
+ data={budget} // [{ poste, montant, jalon? }, ...]
685
+ x={{ key: "poste" }}
686
+ value="montant"
687
+ total="jalon" // ligne de contrôle dessinée depuis zéro
688
+ start={0} // cumul d'ouverture
689
+ />
690
+
691
+ // Tunnel — étapes ORDONNÉES, donc rampe ordinale (jamais les créneaux)
692
+ <FunnelChart
693
+ data={stages} // [{ etape, utilisateurs }, ...]
694
+ x={{ key: "etape" }}
695
+ value="utilisateurs"
696
+ align="center" // "center" (silhouette) | "left" (comparaison)
697
+ showConversion // le ratio d'un pas au suivant : le constat
698
+ />
699
+
700
+ // Haltère — avant → après, ou une plage. L'écart EST la marque
701
+ <DumbbellChart
702
+ data={satisfaction}
703
+ x={{ key: "service" }}
704
+ series={[{ key: "avant", label: "2025" }, { key: "apres", label: "2026" }]}
705
+ sort="gap" // les plus gros mouvements en tête
706
+ />
707
+
708
+ // Boîte à moustaches — `values` (observations) ou `quantiles` (résumé)
709
+ <BoxPlot
710
+ data={teams} // [{ equipe, durees: [12, 14, ...] }, ...]
711
+ x={{ key: "equipe" }}
712
+ values="durees" // moustaches de Tukey + aberrations calculées
713
+ showMean // le losange : l'écart moyenne/médiane = la queue
714
+ />
715
+
716
+ // Chandeliers — couleurs de statut + corps creux/plein, jamais la teinte seule
717
+ <CandlestickChart
718
+ data={sessions}
719
+ x={{ key: "jour" }}
720
+ open="ouverture" high="haut" low="bas" close="cloture"
721
+ />
722
+
723
+ // Planning — dates, dépendances, jalons. Ce n'est pas un `Calendar`
724
+ <GanttChart
725
+ data={project}
726
+ x={{ key: "tache" }}
727
+ start="debut" end="fin"
728
+ id="id" dependencies="depend" // les flèches : ce qui bloque quoi
729
+ progress="avancement" // 0 à 1
730
+ milestone="jalon"
731
+ group="lot" // un créneau de palette par lot
732
+ today="2026-04-14"
733
+ />
734
+
735
+ // Petits multiples — le domaine est calculé sur l'ensemble et imposé à chaque panneau
736
+ <ChartFacets data={rows} by="region" values={["revenue"]} columns={3}>
737
+ {({ data, domain, label }) => (
738
+ <LineChart
739
+ data={data}
740
+ series={[{ key: "revenue" }]}
741
+ x={{ key: "month" }}
742
+ y={{ domain }} // ← toujours le passer, c'est tout l'intérêt
743
+ plotLabel={label}
744
+ height={130}
745
+ legend={false}
746
+ />
747
+ )}
748
+ </ChartFacets>
607
749
  ```
608
750
 
609
751
  **Ce que les composants refusent de faire, et pourquoi :**
@@ -617,6 +759,33 @@ const data = [
617
759
  Un nuage de points s'arrête à 3 séries.
618
760
  - **`PieChart` n'est pas fait pour comparer des valeurs proches** — l'œil compare
619
761
  mal des angles. Deux parts, c'est une `StatCard`.
762
+ - **La palette catégorielle ne peint pas une grandeur.** Les huit créneaux
763
+ répondent à « lequel » ; une rampe répond à « combien ». Dès qu'une couleur
764
+ encode une grandeur, la légende d'échelle est obligatoire.
765
+ - **Une `Heatmap` ne se découpe pas au-delà de 7 classes de couleur** — les cases
766
+ voisines cessent d'être distinguables. Sans `steps`, la rampe reste continue.
767
+ - **Les panneaux d'un `ChartFacets` partagent leur domaine.** Des échelles libres
768
+ ont l'air comparables et ne le sont pas : passez toujours le `domain` reçu.
769
+ - **La rampe divergente dit un sens, pas un jugement.** « Au-dessus » n'est pas
770
+ « bon » : un délai qui glisse est au-dessus. Le jugement appartient aux
771
+ couleurs de statut, toujours accompagnées d'un libellé.
772
+ - **Un `BarChart` combiné garde un seul axe.** Une série `type: "line"` partage
773
+ l'échelle des barres. Deux axes laissent deux mesures étrangères se croiser
774
+ là où le cadrage les place, ce qui se lit comme une corrélation inventée.
775
+ - **Un histogramme ne se trie pas.** `sort`, `stack` et `diverging` sont ignorés
776
+ avec `bins` : les seaux ont un ordre, et le réordonner détruit la forme.
777
+ - **Un tunnel encode une longueur, pas une surface.** Le trapèze classique
778
+ demande de comparer deux surfaces, ce que l'œil ne sait pas faire.
779
+ - **Un `BoxPlot` nourri d'un résumé ne dessine aucune valeur aberrante.** Les
780
+ bornes de Tukey ont besoin des observations ; le composant préfère ne rien
781
+ affirmer plutôt qu'inventer.
782
+ - **`DumbbellChart` et `BoxPlot` ne s'ancrent pas à zéro.** Ils se lisent à un
783
+ écart et à un étalement, pas à la longueur d'une marque depuis une base.
784
+ - **`CandlestickChart` porte le sens deux fois** : couleur de statut ET corps
785
+ creux (hausse) contre plein (baisse). Vert contre rouge est la pire paire
786
+ sous déficience de vision des couleurs.
787
+ - **`GanttChart` n'est pas un `Calendar`.** Le calendrier sélectionne une date
788
+ et la renvoie ; le planning se lit. Une ligne sans largeur est un jalon.
620
789
 
621
790
  ### Cartes KPI
622
791
 
@@ -653,9 +822,18 @@ tout le kit sur une charte :
653
822
  --color-chart-1: #0f766e;
654
823
  --color-chart-2: #b45309;
655
824
  /* ... jusqu'à 8 */
825
+
826
+ /* Les trois rampes de grandeur, si le kit en affiche */
827
+ --color-chart-seq-1: #e0f2f1; /* ... jusqu'à 6, du clair au foncé */
828
+ --color-chart-ord-1: #4db6ac; /* ... jusqu'à 5, paliers ordonnés */
829
+ --color-chart-div-1: #00695c; /* ... jusqu'à 7, milieu neutre au 4 */
656
830
  }
657
831
  ```
658
832
 
833
+ Redéfinissez-les aussi sous `:root.dark` / `[data-theme="dark"]` : les rampes
834
+ sombres du kit ne sont pas les claires éclaircies, elles courent en sens inverse
835
+ (sur fond sombre, « plus » doit s'éloigner du fond).
836
+
659
837
  ### Card (Composable)
660
838
 
661
839
  ```tsx