@sentropic/design-system-svelte 0.34.0 → 0.34.20
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.
- package/dist/AppHeader.svelte +159 -1
- package/dist/AppHeader.svelte.d.ts +18 -1
- package/dist/AppHeader.svelte.d.ts.map +1 -1
- package/dist/ArcDiagramChart.svelte +380 -0
- package/dist/ArcDiagramChart.svelte.d.ts +43 -0
- package/dist/ArcDiagramChart.svelte.d.ts.map +1 -0
- package/dist/AreaRangeChart.svelte +487 -0
- package/dist/AreaRangeChart.svelte.d.ts +38 -0
- package/dist/AreaRangeChart.svelte.d.ts.map +1 -0
- package/dist/AreaSplineRangeChart.svelte +478 -0
- package/dist/AreaSplineRangeChart.svelte.d.ts +37 -0
- package/dist/AreaSplineRangeChart.svelte.d.ts.map +1 -0
- package/dist/BellCurveChart.svelte +487 -0
- package/dist/BellCurveChart.svelte.d.ts +40 -0
- package/dist/BellCurveChart.svelte.d.ts.map +1 -0
- package/dist/Calendar.svelte +11 -0
- package/dist/ChatThread.svelte +32 -1
- package/dist/ChatThread.svelte.d.ts +14 -0
- package/dist/ChatThread.svelte.d.ts.map +1 -1
- package/dist/ColumnPyramidChart.svelte +332 -0
- package/dist/ColumnPyramidChart.svelte.d.ts +35 -0
- package/dist/ColumnPyramidChart.svelte.d.ts.map +1 -0
- package/dist/ColumnRangeChart.svelte +432 -0
- package/dist/ColumnRangeChart.svelte.d.ts +42 -0
- package/dist/ColumnRangeChart.svelte.d.ts.map +1 -0
- package/dist/Combobox.svelte +3 -0
- package/dist/ContentSwitcher.svelte +1 -1
- package/dist/DataTable.svelte.d.ts +1 -1
- package/dist/DatePicker.svelte +3 -0
- package/dist/DependencyWheelChart.svelte +413 -0
- package/dist/DependencyWheelChart.svelte.d.ts +42 -0
- package/dist/DependencyWheelChart.svelte.d.ts.map +1 -0
- package/dist/DumbbellChart.svelte +403 -0
- package/dist/DumbbellChart.svelte.d.ts +44 -0
- package/dist/DumbbellChart.svelte.d.ts.map +1 -0
- package/dist/ErrorBarChart.svelte +428 -0
- package/dist/ErrorBarChart.svelte.d.ts +40 -0
- package/dist/ErrorBarChart.svelte.d.ts.map +1 -0
- package/dist/GanttChart.svelte +410 -0
- package/dist/GanttChart.svelte.d.ts +39 -0
- package/dist/GanttChart.svelte.d.ts.map +1 -0
- package/dist/HLCChart.svelte +330 -0
- package/dist/HLCChart.svelte.d.ts +32 -0
- package/dist/HLCChart.svelte.d.ts.map +1 -0
- package/dist/HeikinAshiChart.svelte +365 -0
- package/dist/HeikinAshiChart.svelte.d.ts +37 -0
- package/dist/HeikinAshiChart.svelte.d.ts.map +1 -0
- package/dist/HollowCandlestickChart.svelte +357 -0
- package/dist/HollowCandlestickChart.svelte.d.ts +34 -0
- package/dist/HollowCandlestickChart.svelte.d.ts.map +1 -0
- package/dist/Input.svelte +3 -0
- package/dist/ItemChart.svelte +389 -0
- package/dist/ItemChart.svelte.d.ts +67 -0
- package/dist/ItemChart.svelte.d.ts.map +1 -0
- package/dist/LollipopChart.svelte +1 -1
- package/dist/MultiSelect.svelte +3 -0
- package/dist/NumberInput.svelte +3 -0
- package/dist/OHLCChart.svelte +343 -0
- package/dist/OHLCChart.svelte.d.ts +33 -0
- package/dist/OHLCChart.svelte.d.ts.map +1 -0
- package/dist/OrganizationChart.svelte +284 -0
- package/dist/OrganizationChart.svelte.d.ts +19 -0
- package/dist/OrganizationChart.svelte.d.ts.map +1 -0
- package/dist/PasswordInput.svelte +3 -0
- package/dist/PolygonChart.svelte +189 -0
- package/dist/PolygonChart.svelte.d.ts +17 -0
- package/dist/PolygonChart.svelte.d.ts.map +1 -0
- package/dist/Search.svelte +7 -5
- package/dist/Select.svelte +3 -0
- package/dist/StreamgraphChart.svelte +283 -0
- package/dist/StreamgraphChart.svelte.d.ts +23 -0
- package/dist/StreamgraphChart.svelte.d.ts.map +1 -0
- package/dist/StreamingMessage.svelte +44 -2
- package/dist/StreamingMessage.svelte.d.ts +18 -1
- package/dist/StreamingMessage.svelte.d.ts.map +1 -1
- package/dist/TileMapChart.svelte +314 -0
- package/dist/TileMapChart.svelte.d.ts +45 -0
- package/dist/TileMapChart.svelte.d.ts.map +1 -0
- package/dist/TimePicker.svelte +3 -0
- package/dist/TimelineChart.svelte +362 -0
- package/dist/TimelineChart.svelte.d.ts +22 -0
- package/dist/TimelineChart.svelte.d.ts.map +1 -0
- package/dist/TreegraphChart.svelte +281 -0
- package/dist/TreegraphChart.svelte.d.ts +19 -0
- package/dist/TreegraphChart.svelte.d.ts.map +1 -0
- package/dist/VariablePieChart.svelte +313 -0
- package/dist/VariablePieChart.svelte.d.ts +52 -0
- package/dist/VariablePieChart.svelte.d.ts.map +1 -0
- package/dist/VennChart.svelte +348 -0
- package/dist/VennChart.svelte.d.ts +72 -0
- package/dist/VennChart.svelte.d.ts.map +1 -0
- package/dist/WordCloudChart.svelte +279 -0
- package/dist/WordCloudChart.svelte.d.ts +18 -0
- package/dist/WordCloudChart.svelte.d.ts.map +1 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -0
- package/package.json +5 -3
package/dist/AppHeader.svelte
CHANGED
|
@@ -18,7 +18,24 @@
|
|
|
18
18
|
* Auto-généré et stable si non fourni.
|
|
19
19
|
*/
|
|
20
20
|
drawerId?: string;
|
|
21
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* Marque structurée (décision actée : logo SENT + sous-titre). Rend le bloc
|
|
23
|
+
* canonique « logo carré + nom + sous-titre produit » sans dupliquer de CSS
|
|
24
|
+
* côté consommateur. Si le snippet `logo` est fourni, il a priorité (contrôle
|
|
25
|
+
* total) ; sinon ces props produisent le bloc marque par défaut.
|
|
26
|
+
*/
|
|
27
|
+
brandName?: string;
|
|
28
|
+
/** Sous-titre produit affiché sous le nom (ex. « Design System », « dataviz »). */
|
|
29
|
+
productName?: string;
|
|
30
|
+
/** Source de l'image du logo carré (ex. `/SENT-logo-squared.svg`). */
|
|
31
|
+
logoSrc?: string;
|
|
32
|
+
/** Texte alternatif du logo (décoratif par défaut). */
|
|
33
|
+
logoAlt?: string;
|
|
34
|
+
/** Cible du lien de la marque. Défaut : `/`. */
|
|
35
|
+
brandHref?: string;
|
|
36
|
+
/** aria-label du lien de marque (sinon dérivé de `brandName` + `productName`). */
|
|
37
|
+
brandLabel?: string;
|
|
38
|
+
/** Logo (décision actée : logo SENT + sous-titre). Prioritaire sur `brandName`/`logoSrc`. */
|
|
22
39
|
logo?: Snippet;
|
|
23
40
|
/** Liens de navigation (rendus dans le <nav> desktop). */
|
|
24
41
|
nav?: Snippet;
|
|
@@ -47,6 +64,12 @@
|
|
|
47
64
|
onMenuToggle,
|
|
48
65
|
menuLabel = "Menu",
|
|
49
66
|
drawerId,
|
|
67
|
+
brandName,
|
|
68
|
+
productName,
|
|
69
|
+
logoSrc,
|
|
70
|
+
logoAlt = "",
|
|
71
|
+
brandHref = "/",
|
|
72
|
+
brandLabel,
|
|
50
73
|
logo,
|
|
51
74
|
nav,
|
|
52
75
|
actions,
|
|
@@ -54,6 +77,15 @@
|
|
|
54
77
|
class: className,
|
|
55
78
|
}: AppHeaderProps = $props();
|
|
56
79
|
|
|
80
|
+
// Marque par défaut : rendue ssi aucun snippet `logo` et qu'il y a au moins un
|
|
81
|
+
// nom / logo / produit. Calque le bloc marque canonique du site DS.
|
|
82
|
+
const hasDefaultBrand = $derived(
|
|
83
|
+
!logo && Boolean(brandName || productName || logoSrc),
|
|
84
|
+
);
|
|
85
|
+
const resolvedBrandLabel = $derived(
|
|
86
|
+
brandLabel ?? [brandName, productName].filter(Boolean).join(" "),
|
|
87
|
+
);
|
|
88
|
+
|
|
57
89
|
// Id stable du tiroir : prop fournie sinon compteur module (SSR-safe, sans
|
|
58
90
|
// crypto). Capturé une seule fois (untrack) : un id stable ne doit pas réagir.
|
|
59
91
|
const resolvedDrawerId = untrack(
|
|
@@ -68,6 +100,18 @@
|
|
|
68
100
|
<!-- Logo SENT à GAUCHE (+ sous-titre). -->
|
|
69
101
|
{#if logo}
|
|
70
102
|
<div class="st-appHeader__logo">{@render logo()}</div>
|
|
103
|
+
{:else if hasDefaultBrand}
|
|
104
|
+
<a class="st-appHeader__brand" href={brandHref} aria-label={resolvedBrandLabel || undefined}>
|
|
105
|
+
{#if logoSrc}
|
|
106
|
+
<img class="st-appHeader__brandMark" src={logoSrc} alt={logoAlt} aria-hidden={logoAlt ? undefined : "true"} />
|
|
107
|
+
{/if}
|
|
108
|
+
{#if brandName || productName}
|
|
109
|
+
<span class="st-appHeader__brandCopy">
|
|
110
|
+
{#if brandName}<span class="st-appHeader__brandName">{brandName}</span>{/if}
|
|
111
|
+
{#if productName}<span class="st-appHeader__brandProduct">{productName}</span>{/if}
|
|
112
|
+
</span>
|
|
113
|
+
{/if}
|
|
114
|
+
</a>
|
|
71
115
|
{/if}
|
|
72
116
|
|
|
73
117
|
<!-- Nav desktop (masquée en mode compact). -->
|
|
@@ -185,6 +229,120 @@
|
|
|
185
229
|
flex: 0 0 auto;
|
|
186
230
|
}
|
|
187
231
|
|
|
232
|
+
/* --- Marque canonique (logo carré + nom + sous-titre produit) --- */
|
|
233
|
+
.st-appHeader__brand {
|
|
234
|
+
align-items: center;
|
|
235
|
+
color: var(--st-semantic-text-primary);
|
|
236
|
+
display: inline-flex;
|
|
237
|
+
flex: 0 0 auto;
|
|
238
|
+
gap: var(--st-spacing-3, 0.75rem);
|
|
239
|
+
min-width: 0;
|
|
240
|
+
text-decoration: none;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.st-appHeader__brand:hover,
|
|
244
|
+
.st-appHeader__brand:focus-visible {
|
|
245
|
+
text-decoration: none;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.st-appHeader__brand:focus-visible {
|
|
249
|
+
border-radius: var(--st-radius-sm, 0.375rem);
|
|
250
|
+
box-shadow: 0 0 0 2px var(--st-semantic-border-interactive);
|
|
251
|
+
outline: none;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.st-appHeader__brandMark {
|
|
255
|
+
aspect-ratio: 1;
|
|
256
|
+
display: inline-block;
|
|
257
|
+
flex: 0 0 auto;
|
|
258
|
+
height: 2rem;
|
|
259
|
+
object-fit: contain;
|
|
260
|
+
width: 2rem;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.st-appHeader__brandCopy {
|
|
264
|
+
display: grid;
|
|
265
|
+
gap: 0.08rem;
|
|
266
|
+
line-height: 1;
|
|
267
|
+
min-width: 0;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.st-appHeader__brandName {
|
|
271
|
+
color: var(--st-semantic-text-primary);
|
|
272
|
+
font-size: 1rem;
|
|
273
|
+
font-weight: 760;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.st-appHeader__brandProduct {
|
|
277
|
+
color: var(--st-semantic-text-secondary);
|
|
278
|
+
font-size: 0.75rem;
|
|
279
|
+
font-weight: 650;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/* --- Lien de nav canonique (pill soulignée, état actif) ---
|
|
283
|
+
Classe utilitaire publiée : un consommateur l'applique sur ses <a> de nav
|
|
284
|
+
(ou via le composant Link DS) pour matcher le chrome du site DS sans
|
|
285
|
+
dupliquer de CSS d'application. */
|
|
286
|
+
:global(.st-appHeader__navLink) {
|
|
287
|
+
align-items: center;
|
|
288
|
+
border-bottom: 2px solid transparent;
|
|
289
|
+
border-radius: 0;
|
|
290
|
+
color: var(--st-semantic-text-secondary);
|
|
291
|
+
display: inline-flex;
|
|
292
|
+
font-size: 0.875rem;
|
|
293
|
+
gap: 0.35rem;
|
|
294
|
+
line-height: 1;
|
|
295
|
+
padding: 0.38rem 0.75rem;
|
|
296
|
+
text-decoration: none;
|
|
297
|
+
white-space: nowrap;
|
|
298
|
+
transition: color var(--st-motion-fast, 120ms) ease,
|
|
299
|
+
border-color var(--st-motion-fast, 120ms) ease;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
:global(.st-appHeader__navLink:hover),
|
|
303
|
+
:global(.st-appHeader__navLink:focus-visible) {
|
|
304
|
+
color: var(--st-semantic-text-primary);
|
|
305
|
+
text-decoration: none;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
:global(.st-appHeader__navLink[aria-current="page"]) {
|
|
309
|
+
border-bottom-color: var(--st-semantic-border-interactive);
|
|
310
|
+
color: var(--st-semantic-text-primary);
|
|
311
|
+
font-weight: 650;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/* --- Contrôle utilitaire canonique (pill : thème / langue / icône) --- */
|
|
315
|
+
:global(.st-appHeader__control) {
|
|
316
|
+
align-items: center;
|
|
317
|
+
background: var(--st-semantic-surface-default);
|
|
318
|
+
border: 1px solid var(--st-semantic-border-subtle);
|
|
319
|
+
border-radius: var(--st-radius-sm, 0.375rem);
|
|
320
|
+
color: var(--st-semantic-text-secondary);
|
|
321
|
+
cursor: pointer;
|
|
322
|
+
display: inline-flex;
|
|
323
|
+
font: inherit;
|
|
324
|
+
font-size: 0.75rem;
|
|
325
|
+
font-weight: 650;
|
|
326
|
+
gap: 0.35rem;
|
|
327
|
+
height: 2.25rem;
|
|
328
|
+
line-height: 1;
|
|
329
|
+
padding: 0 0.65rem;
|
|
330
|
+
text-decoration: none;
|
|
331
|
+
white-space: nowrap;
|
|
332
|
+
transition: background-color var(--st-motion-fast, 120ms) ease,
|
|
333
|
+
border-color var(--st-motion-fast, 120ms) ease,
|
|
334
|
+
color var(--st-motion-fast, 120ms) ease;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
:global(.st-appHeader__control:hover),
|
|
338
|
+
:global(.st-appHeader__control:focus-visible),
|
|
339
|
+
:global(.st-appHeader__control[aria-expanded="true"]) {
|
|
340
|
+
background: var(--st-semantic-surface-subtle);
|
|
341
|
+
border-color: var(--st-semantic-border-interactive);
|
|
342
|
+
color: var(--st-semantic-text-primary);
|
|
343
|
+
outline: none;
|
|
344
|
+
}
|
|
345
|
+
|
|
188
346
|
.st-appHeader__actions {
|
|
189
347
|
align-items: center;
|
|
190
348
|
display: flex;
|
|
@@ -16,7 +16,24 @@ export interface AppHeaderProps {
|
|
|
16
16
|
* Auto-généré et stable si non fourni.
|
|
17
17
|
*/
|
|
18
18
|
drawerId?: string;
|
|
19
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* Marque structurée (décision actée : logo SENT + sous-titre). Rend le bloc
|
|
21
|
+
* canonique « logo carré + nom + sous-titre produit » sans dupliquer de CSS
|
|
22
|
+
* côté consommateur. Si le snippet `logo` est fourni, il a priorité (contrôle
|
|
23
|
+
* total) ; sinon ces props produisent le bloc marque par défaut.
|
|
24
|
+
*/
|
|
25
|
+
brandName?: string;
|
|
26
|
+
/** Sous-titre produit affiché sous le nom (ex. « Design System », « dataviz »). */
|
|
27
|
+
productName?: string;
|
|
28
|
+
/** Source de l'image du logo carré (ex. `/SENT-logo-squared.svg`). */
|
|
29
|
+
logoSrc?: string;
|
|
30
|
+
/** Texte alternatif du logo (décoratif par défaut). */
|
|
31
|
+
logoAlt?: string;
|
|
32
|
+
/** Cible du lien de la marque. Défaut : `/`. */
|
|
33
|
+
brandHref?: string;
|
|
34
|
+
/** aria-label du lien de marque (sinon dérivé de `brandName` + `productName`). */
|
|
35
|
+
brandLabel?: string;
|
|
36
|
+
/** Logo (décision actée : logo SENT + sous-titre). Prioritaire sur `brandName`/`logoSrc`. */
|
|
20
37
|
logo?: Snippet;
|
|
21
38
|
/** Liens de navigation (rendus dans le <nav> desktop). */
|
|
22
39
|
nav?: Snippet;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppHeader.svelte.d.ts","sourceRoot":"","sources":["../src/lib/AppHeader.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEtC,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gDAAgD;IAChD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,mCAAmC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,
|
|
1
|
+
{"version":3,"file":"AppHeader.svelte.d.ts","sourceRoot":"","sources":["../src/lib/AppHeader.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEtC,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gDAAgD;IAChD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,mCAAmC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mFAAmF;IACnF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gDAAgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kFAAkF;IAClF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6FAA6F;IAC7F,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,0DAA0D;IAC1D,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,oEAAoE;IACpE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,wEAAwE;IACxE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA+GH,QAAA,MAAM,SAAS,oDAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
/**
|
|
3
|
+
* ArcDiagramChart - API canonique (référence Svelte, React/Vue doivent s'aligner)
|
|
4
|
+
*
|
|
5
|
+
* Représente des liens pondérés entre nœuds alignés sur un axe horizontal (bas).
|
|
6
|
+
* Chaque nœud est un point sur la ligne, dont la taille croît avec son degré
|
|
7
|
+
* pondéré (somme des poids de ses liens) ; chaque lien est un arc en demi-cercle
|
|
8
|
+
* tracé AU-DESSUS de la ligne reliant ses deux nœuds, dont l'épaisseur croît avec
|
|
9
|
+
* le poids et dont la couleur reprend celle du nœud source.
|
|
10
|
+
*
|
|
11
|
+
* Props obligatoires :
|
|
12
|
+
* data ArcDiagramChartLink[] - liste de liens {from, to, weight}
|
|
13
|
+
* from/to = identifiants de nœuds (string) ;
|
|
14
|
+
* un nœud est créé pour chaque identifiant cité,
|
|
15
|
+
* dans l'ordre d'apparition (union from/to).
|
|
16
|
+
* label string - aria-label du graphique
|
|
17
|
+
*
|
|
18
|
+
* Props optionnelles :
|
|
19
|
+
* labels? Record<string,string> - libellés d'affichage par identifiant de nœud
|
|
20
|
+
* width number (défaut 480) - largeur du viewBox en px
|
|
21
|
+
* height number (défaut 240) - hauteur du viewBox en px
|
|
22
|
+
* class string - classe CSS supplémentaire
|
|
23
|
+
*
|
|
24
|
+
* Garde : seuls les liens dont `weight` est fini et > 0 sont pris en compte.
|
|
25
|
+
* Les liens NaN / Infinity / négatifs / nuls sont ignorés silencieusement.
|
|
26
|
+
*/
|
|
27
|
+
export type ArcDiagramChartTone =
|
|
28
|
+
| "category1"
|
|
29
|
+
| "category2"
|
|
30
|
+
| "category3"
|
|
31
|
+
| "category4"
|
|
32
|
+
| "category5"
|
|
33
|
+
| "category6"
|
|
34
|
+
| "category7"
|
|
35
|
+
| "category8";
|
|
36
|
+
|
|
37
|
+
export type ArcDiagramChartLink = {
|
|
38
|
+
from: string;
|
|
39
|
+
to: string;
|
|
40
|
+
weight: number;
|
|
41
|
+
};
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
<script lang="ts">
|
|
45
|
+
import ChartDataList from "./ChartDataList.svelte";
|
|
46
|
+
import GraphLegend from "./GraphLegend.svelte";
|
|
47
|
+
|
|
48
|
+
type ArcDiagramChartProps = {
|
|
49
|
+
data: ArcDiagramChartLink[];
|
|
50
|
+
label: string;
|
|
51
|
+
labels?: Record<string, string>;
|
|
52
|
+
width?: number;
|
|
53
|
+
height?: number;
|
|
54
|
+
class?: string;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
let {
|
|
58
|
+
data,
|
|
59
|
+
label,
|
|
60
|
+
labels,
|
|
61
|
+
width = 480,
|
|
62
|
+
height = 240,
|
|
63
|
+
class: className
|
|
64
|
+
}: ArcDiagramChartProps = $props();
|
|
65
|
+
|
|
66
|
+
const TONES = [
|
|
67
|
+
"category1",
|
|
68
|
+
"category2",
|
|
69
|
+
"category3",
|
|
70
|
+
"category4",
|
|
71
|
+
"category5",
|
|
72
|
+
"category6",
|
|
73
|
+
"category7",
|
|
74
|
+
"category8"
|
|
75
|
+
] as const;
|
|
76
|
+
|
|
77
|
+
const MARGIN_X = 24; // marge horizontale pour les premiers/derniers nœuds
|
|
78
|
+
const BASELINE_PAD = 28; // distance du bas réservée aux marqueurs/libellés
|
|
79
|
+
const MIN_NODE_R = 4;
|
|
80
|
+
const MAX_NODE_R = 9;
|
|
81
|
+
|
|
82
|
+
function magnitude(value: number): number {
|
|
83
|
+
return Number.isFinite(value) && value > 0 ? value : 0;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function displayLabel(id: string): string {
|
|
87
|
+
return labels?.[id] ?? id;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
let hoveredLinkIndex: number | null = $state(null);
|
|
91
|
+
|
|
92
|
+
const layout = $derived.by(() => {
|
|
93
|
+
const baselineY = height - BASELINE_PAD;
|
|
94
|
+
|
|
95
|
+
const links = data
|
|
96
|
+
.map((link, index) => ({ link, index, weight: magnitude(link.weight) }))
|
|
97
|
+
.filter((entry) => entry.weight > 0);
|
|
98
|
+
|
|
99
|
+
const order: string[] = [];
|
|
100
|
+
const degree = new Map<string, number>();
|
|
101
|
+
for (const { link, weight } of links) {
|
|
102
|
+
for (const id of [link.from, link.to]) {
|
|
103
|
+
if (!degree.has(id)) {
|
|
104
|
+
degree.set(id, 0);
|
|
105
|
+
order.push(id);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
degree.set(link.from, (degree.get(link.from) ?? 0) + weight);
|
|
109
|
+
degree.set(link.to, (degree.get(link.to) ?? 0) + weight);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (order.length === 0) {
|
|
113
|
+
return { baselineY, nodes: [], arcs: [], nodeX: new Map<string, number>() };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const usable = Math.max(width - MARGIN_X * 2, 1);
|
|
117
|
+
const step = order.length > 1 ? usable / (order.length - 1) : 0;
|
|
118
|
+
const startX = order.length > 1 ? MARGIN_X : width / 2;
|
|
119
|
+
|
|
120
|
+
const maxDegree = Math.max(1, ...order.map((id) => degree.get(id) ?? 0));
|
|
121
|
+
|
|
122
|
+
type NodeDatum = {
|
|
123
|
+
id: string;
|
|
124
|
+
tone: ArcDiagramChartTone;
|
|
125
|
+
x: number;
|
|
126
|
+
r: number;
|
|
127
|
+
value: number;
|
|
128
|
+
};
|
|
129
|
+
const nodeX = new Map<string, number>();
|
|
130
|
+
const nodeTone = new Map<string, ArcDiagramChartTone>();
|
|
131
|
+
const nodes: NodeDatum[] = order.map((id, index) => {
|
|
132
|
+
const x = startX + step * index;
|
|
133
|
+
const tone = TONES[index % TONES.length];
|
|
134
|
+
const value = degree.get(id) ?? 0;
|
|
135
|
+
const r = MIN_NODE_R + (MAX_NODE_R - MIN_NODE_R) * (value / maxDegree);
|
|
136
|
+
nodeX.set(id, x);
|
|
137
|
+
nodeTone.set(id, tone);
|
|
138
|
+
return { id, tone, x, r, value };
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
const maxWeight = Math.max(1, ...links.map((entry) => entry.weight));
|
|
142
|
+
type ArcDatum = {
|
|
143
|
+
index: number;
|
|
144
|
+
from: string;
|
|
145
|
+
to: string;
|
|
146
|
+
weight: number;
|
|
147
|
+
tone: ArcDiagramChartTone;
|
|
148
|
+
strokeWidth: number;
|
|
149
|
+
path: string;
|
|
150
|
+
midX: number;
|
|
151
|
+
midY: number;
|
|
152
|
+
};
|
|
153
|
+
const arcs: ArcDatum[] = links.map(({ link, weight, index }) => {
|
|
154
|
+
const x1 = nodeX.get(link.from)!;
|
|
155
|
+
const x2 = nodeX.get(link.to)!;
|
|
156
|
+
const left = Math.min(x1, x2);
|
|
157
|
+
const right = Math.max(x1, x2);
|
|
158
|
+
const radius = (right - left) / 2;
|
|
159
|
+
const sweep = x1 <= x2 ? 1 : 0;
|
|
160
|
+
// Demi-cercle au-dessus de la ligne (arc supérieur).
|
|
161
|
+
const path = `M ${x1} ${baselineY} A ${radius} ${radius} 0 0 ${sweep} ${x2} ${baselineY}`;
|
|
162
|
+
const tone = nodeTone.get(link.from)!;
|
|
163
|
+
return {
|
|
164
|
+
index,
|
|
165
|
+
from: link.from,
|
|
166
|
+
to: link.to,
|
|
167
|
+
weight,
|
|
168
|
+
tone,
|
|
169
|
+
strokeWidth: Math.max(1.5, (weight / maxWeight) * 6),
|
|
170
|
+
path,
|
|
171
|
+
midX: (left + right) / 2,
|
|
172
|
+
midY: baselineY - radius
|
|
173
|
+
};
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
return { baselineY, nodes, arcs, nodeX };
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
const legendEntries = $derived(
|
|
180
|
+
layout.nodes.map((node) => ({
|
|
181
|
+
label: displayLabel(node.id),
|
|
182
|
+
shape: "circle" as const,
|
|
183
|
+
tone: node.tone
|
|
184
|
+
}))
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
const dataValueItems = $derived(
|
|
188
|
+
data
|
|
189
|
+
.filter((link) => magnitude(link.weight) > 0)
|
|
190
|
+
.map((link) => `${displayLabel(link.from)} -> ${displayLabel(link.to)}: ${link.weight}`)
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
function handleVisualPointerMove(event: PointerEvent) {
|
|
194
|
+
const target = event.target;
|
|
195
|
+
if (!(target instanceof Element)) {
|
|
196
|
+
hoveredLinkIndex = null;
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const index = Number(target.getAttribute("data-link-index"));
|
|
200
|
+
hoveredLinkIndex = Number.isInteger(index) ? index : null;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const classes = () => ["st-arcDiagramChart", className].filter(Boolean).join(" ");
|
|
204
|
+
</script>
|
|
205
|
+
|
|
206
|
+
<div class={classes()}>
|
|
207
|
+
<div
|
|
208
|
+
class="st-arcDiagramChart__visual"
|
|
209
|
+
role="img"
|
|
210
|
+
aria-label={label}
|
|
211
|
+
onpointermove={handleVisualPointerMove}
|
|
212
|
+
onpointerleave={() => (hoveredLinkIndex = null)}
|
|
213
|
+
>
|
|
214
|
+
<svg
|
|
215
|
+
viewBox="0 0 {width} {height}"
|
|
216
|
+
preserveAspectRatio="xMidYMid meet"
|
|
217
|
+
width="100%"
|
|
218
|
+
height="100%"
|
|
219
|
+
focusable="false"
|
|
220
|
+
aria-hidden="true"
|
|
221
|
+
>
|
|
222
|
+
<line
|
|
223
|
+
class="st-arcDiagramChart__axis"
|
|
224
|
+
x1={MARGIN_X}
|
|
225
|
+
y1={layout.baselineY}
|
|
226
|
+
x2={width - MARGIN_X}
|
|
227
|
+
y2={layout.baselineY}
|
|
228
|
+
/>
|
|
229
|
+
|
|
230
|
+
<g class="st-arcDiagramChart__arcs">
|
|
231
|
+
{#each layout.arcs as arc (arc.index)}
|
|
232
|
+
<path
|
|
233
|
+
class="st-arcDiagramChart__arc st-arcDiagramChart__arc--{arc.tone}"
|
|
234
|
+
class:st-arcDiagramChart__arc--dim={hoveredLinkIndex !== null && hoveredLinkIndex !== arc.index}
|
|
235
|
+
d={arc.path}
|
|
236
|
+
stroke-width={arc.strokeWidth}
|
|
237
|
+
data-link-index={arc.index}
|
|
238
|
+
/>
|
|
239
|
+
{/each}
|
|
240
|
+
</g>
|
|
241
|
+
|
|
242
|
+
<g class="st-arcDiagramChart__nodes">
|
|
243
|
+
{#each layout.nodes as node (node.id)}
|
|
244
|
+
<circle
|
|
245
|
+
class="st-arcDiagramChart__node st-arcDiagramChart__node--{node.tone}"
|
|
246
|
+
cx={node.x}
|
|
247
|
+
cy={layout.baselineY}
|
|
248
|
+
r={node.r}
|
|
249
|
+
/>
|
|
250
|
+
{/each}
|
|
251
|
+
</g>
|
|
252
|
+
</svg>
|
|
253
|
+
|
|
254
|
+
{#if legendEntries.length > 0}
|
|
255
|
+
<GraphLegend class="st-arcDiagramChart__legend" entries={legendEntries} />
|
|
256
|
+
{/if}
|
|
257
|
+
</div>
|
|
258
|
+
|
|
259
|
+
<ChartDataList {label} items={dataValueItems} />
|
|
260
|
+
|
|
261
|
+
{#if hoveredLinkIndex !== null && layout.arcs.find((a) => a.index === hoveredLinkIndex)}
|
|
262
|
+
{@const arc = layout.arcs.find((a) => a.index === hoveredLinkIndex)!}
|
|
263
|
+
<div
|
|
264
|
+
class="st-arcDiagramChart__tooltip"
|
|
265
|
+
role="presentation"
|
|
266
|
+
style="left: {(arc.midX / width) * 100}%; top: {(arc.midY / height) * 100}%"
|
|
267
|
+
>
|
|
268
|
+
<span class="st-arcDiagramChart__tooltipLabel">{displayLabel(arc.from)} -> {displayLabel(arc.to)}</span>
|
|
269
|
+
<span class="st-arcDiagramChart__tooltipValue">{arc.weight}</span>
|
|
270
|
+
</div>
|
|
271
|
+
{/if}
|
|
272
|
+
</div>
|
|
273
|
+
|
|
274
|
+
<style>
|
|
275
|
+
.st-arcDiagramChart {
|
|
276
|
+
color: var(--st-semantic-text-secondary);
|
|
277
|
+
display: block;
|
|
278
|
+
font-family: inherit;
|
|
279
|
+
max-width: 100%;
|
|
280
|
+
position: relative;
|
|
281
|
+
width: 100%;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.st-arcDiagramChart svg,
|
|
285
|
+
.st-arcDiagramChart__visual {
|
|
286
|
+
display: block;
|
|
287
|
+
overflow: visible;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.st-arcDiagramChart__visual {
|
|
291
|
+
position: relative;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.st-arcDiagramChart__legend {
|
|
295
|
+
position: absolute;
|
|
296
|
+
right: 0;
|
|
297
|
+
top: 0;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.st-arcDiagramChart__axis {
|
|
301
|
+
stroke: var(--st-semantic-border-subtle, var(--st-semantic-text-secondary));
|
|
302
|
+
stroke-width: 1;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
.st-arcDiagramChart__arc {
|
|
306
|
+
cursor: pointer;
|
|
307
|
+
fill: none;
|
|
308
|
+
stroke-opacity: 0.6;
|
|
309
|
+
transition: opacity 120ms ease, stroke-opacity 120ms ease;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.st-arcDiagramChart__arc:hover {
|
|
313
|
+
stroke-opacity: 0.85;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
.st-arcDiagramChart__arc--dim {
|
|
317
|
+
opacity: 0.18;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
@media (prefers-reduced-motion: reduce) {
|
|
321
|
+
.st-arcDiagramChart__arc {
|
|
322
|
+
transition: none;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
.st-arcDiagramChart__node {
|
|
327
|
+
stroke: var(--st-semantic-surface-default, Canvas);
|
|
328
|
+
stroke-width: 1.5;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
.st-arcDiagramChart__arc--category1,
|
|
332
|
+
.st-arcDiagramChart__node--category1 { stroke: var(--st-semantic-data-category1); }
|
|
333
|
+
.st-arcDiagramChart__node--category1 { fill: var(--st-semantic-data-category1); }
|
|
334
|
+
.st-arcDiagramChart__arc--category2,
|
|
335
|
+
.st-arcDiagramChart__node--category2 { stroke: var(--st-semantic-data-category2); }
|
|
336
|
+
.st-arcDiagramChart__node--category2 { fill: var(--st-semantic-data-category2); }
|
|
337
|
+
.st-arcDiagramChart__arc--category3,
|
|
338
|
+
.st-arcDiagramChart__node--category3 { stroke: var(--st-semantic-data-category3); }
|
|
339
|
+
.st-arcDiagramChart__node--category3 { fill: var(--st-semantic-data-category3); }
|
|
340
|
+
.st-arcDiagramChart__arc--category4,
|
|
341
|
+
.st-arcDiagramChart__node--category4 { stroke: var(--st-semantic-data-category4); }
|
|
342
|
+
.st-arcDiagramChart__node--category4 { fill: var(--st-semantic-data-category4); }
|
|
343
|
+
.st-arcDiagramChart__arc--category5,
|
|
344
|
+
.st-arcDiagramChart__node--category5 { stroke: var(--st-semantic-data-category5); }
|
|
345
|
+
.st-arcDiagramChart__node--category5 { fill: var(--st-semantic-data-category5); }
|
|
346
|
+
.st-arcDiagramChart__arc--category6,
|
|
347
|
+
.st-arcDiagramChart__node--category6 { stroke: var(--st-semantic-data-category6); }
|
|
348
|
+
.st-arcDiagramChart__node--category6 { fill: var(--st-semantic-data-category6); }
|
|
349
|
+
.st-arcDiagramChart__arc--category7,
|
|
350
|
+
.st-arcDiagramChart__node--category7 { stroke: var(--st-semantic-data-category7); }
|
|
351
|
+
.st-arcDiagramChart__node--category7 { fill: var(--st-semantic-data-category7); }
|
|
352
|
+
.st-arcDiagramChart__arc--category8,
|
|
353
|
+
.st-arcDiagramChart__node--category8 { stroke: var(--st-semantic-data-category8); }
|
|
354
|
+
.st-arcDiagramChart__node--category8 { fill: var(--st-semantic-data-category8); }
|
|
355
|
+
|
|
356
|
+
.st-arcDiagramChart__tooltip {
|
|
357
|
+
background: var(--st-semantic-surface-inverse);
|
|
358
|
+
border-radius: var(--st-radius-sm, 0.25rem);
|
|
359
|
+
color: var(--st-semantic-text-inverse);
|
|
360
|
+
display: inline-flex;
|
|
361
|
+
flex-direction: column;
|
|
362
|
+
font-size: 0.75rem;
|
|
363
|
+
gap: 0.125rem;
|
|
364
|
+
line-height: 1.2;
|
|
365
|
+
padding: 0.375rem 0.5rem;
|
|
366
|
+
pointer-events: none;
|
|
367
|
+
position: absolute;
|
|
368
|
+
transform: translate(-50%, -115%);
|
|
369
|
+
white-space: nowrap;
|
|
370
|
+
z-index: 1;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
.st-arcDiagramChart__tooltipLabel {
|
|
374
|
+
font-weight: 600;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
.st-arcDiagramChart__tooltipValue {
|
|
378
|
+
opacity: 0.85;
|
|
379
|
+
}
|
|
380
|
+
</style>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ArcDiagramChart - API canonique (référence Svelte, React/Vue doivent s'aligner)
|
|
3
|
+
*
|
|
4
|
+
* Représente des liens pondérés entre nœuds alignés sur un axe horizontal (bas).
|
|
5
|
+
* Chaque nœud est un point sur la ligne, dont la taille croît avec son degré
|
|
6
|
+
* pondéré (somme des poids de ses liens) ; chaque lien est un arc en demi-cercle
|
|
7
|
+
* tracé AU-DESSUS de la ligne reliant ses deux nœuds, dont l'épaisseur croît avec
|
|
8
|
+
* le poids et dont la couleur reprend celle du nœud source.
|
|
9
|
+
*
|
|
10
|
+
* Props obligatoires :
|
|
11
|
+
* data ArcDiagramChartLink[] - liste de liens {from, to, weight}
|
|
12
|
+
* from/to = identifiants de nœuds (string) ;
|
|
13
|
+
* un nœud est créé pour chaque identifiant cité,
|
|
14
|
+
* dans l'ordre d'apparition (union from/to).
|
|
15
|
+
* label string - aria-label du graphique
|
|
16
|
+
*
|
|
17
|
+
* Props optionnelles :
|
|
18
|
+
* labels? Record<string,string> - libellés d'affichage par identifiant de nœud
|
|
19
|
+
* width number (défaut 480) - largeur du viewBox en px
|
|
20
|
+
* height number (défaut 240) - hauteur du viewBox en px
|
|
21
|
+
* class string - classe CSS supplémentaire
|
|
22
|
+
*
|
|
23
|
+
* Garde : seuls les liens dont `weight` est fini et > 0 sont pris en compte.
|
|
24
|
+
* Les liens NaN / Infinity / négatifs / nuls sont ignorés silencieusement.
|
|
25
|
+
*/
|
|
26
|
+
export type ArcDiagramChartTone = "category1" | "category2" | "category3" | "category4" | "category5" | "category6" | "category7" | "category8";
|
|
27
|
+
export type ArcDiagramChartLink = {
|
|
28
|
+
from: string;
|
|
29
|
+
to: string;
|
|
30
|
+
weight: number;
|
|
31
|
+
};
|
|
32
|
+
type ArcDiagramChartProps = {
|
|
33
|
+
data: ArcDiagramChartLink[];
|
|
34
|
+
label: string;
|
|
35
|
+
labels?: Record<string, string>;
|
|
36
|
+
width?: number;
|
|
37
|
+
height?: number;
|
|
38
|
+
class?: string;
|
|
39
|
+
};
|
|
40
|
+
declare const ArcDiagramChart: import("svelte").Component<ArcDiagramChartProps, {}, "">;
|
|
41
|
+
type ArcDiagramChart = ReturnType<typeof ArcDiagramChart>;
|
|
42
|
+
export default ArcDiagramChart;
|
|
43
|
+
//# sourceMappingURL=ArcDiagramChart.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ArcDiagramChart.svelte.d.ts","sourceRoot":"","sources":["../src/lib/ArcDiagramChart.svelte.ts"],"names":[],"mappings":"AAGE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,MAAM,mBAAmB,GAC3B,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,CAAC;AAEhB,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAOF,KAAK,oBAAoB,GAAG;IAC1B,IAAI,EAAE,mBAAmB,EAAE,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAmMJ,QAAA,MAAM,eAAe,0DAAwC,CAAC;AAC9D,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1D,eAAe,eAAe,CAAC"}
|