@sentropic/design-system-vue 0.36.34 → 0.36.36

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.
@@ -0,0 +1,88 @@
1
+ export type AnomalySwimLaneTone = "category1" | "category2" | "category3" | "category4" | "category5" | "category6" | "category7" | "category8";
2
+ export type AnomalySwimLaneBucket = {
3
+ at: number;
4
+ score: number;
5
+ };
6
+ export type AnomalySwimLaneSeries = {
7
+ job: string;
8
+ buckets: AnomalySwimLaneBucket[];
9
+ };
10
+ export type AnomalySwimLaneChartProps = {
11
+ data: AnomalySwimLaneSeries[];
12
+ max?: number;
13
+ label?: string;
14
+ width?: number;
15
+ height?: number;
16
+ size?: number;
17
+ class?: string;
18
+ };
19
+ export declare const AnomalySwimLaneChart: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
20
+ data: {
21
+ type: () => AnomalySwimLaneSeries[];
22
+ default: () => never[];
23
+ };
24
+ max: {
25
+ type: NumberConstructor;
26
+ default: undefined;
27
+ };
28
+ label: {
29
+ type: StringConstructor;
30
+ default: undefined;
31
+ };
32
+ width: {
33
+ type: NumberConstructor;
34
+ default: undefined;
35
+ };
36
+ height: {
37
+ type: NumberConstructor;
38
+ default: number;
39
+ };
40
+ size: {
41
+ type: NumberConstructor;
42
+ default: undefined;
43
+ };
44
+ class: {
45
+ type: StringConstructor;
46
+ default: undefined;
47
+ };
48
+ }>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
49
+ [key: string]: any;
50
+ }>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
51
+ data: {
52
+ type: () => AnomalySwimLaneSeries[];
53
+ default: () => never[];
54
+ };
55
+ max: {
56
+ type: NumberConstructor;
57
+ default: undefined;
58
+ };
59
+ label: {
60
+ type: StringConstructor;
61
+ default: undefined;
62
+ };
63
+ width: {
64
+ type: NumberConstructor;
65
+ default: undefined;
66
+ };
67
+ height: {
68
+ type: NumberConstructor;
69
+ default: number;
70
+ };
71
+ size: {
72
+ type: NumberConstructor;
73
+ default: undefined;
74
+ };
75
+ class: {
76
+ type: StringConstructor;
77
+ default: undefined;
78
+ };
79
+ }>> & Readonly<{}>, {
80
+ size: number;
81
+ class: string;
82
+ data: AnomalySwimLaneSeries[];
83
+ label: string;
84
+ height: number;
85
+ max: number;
86
+ width: number;
87
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
88
+ //# sourceMappingURL=AnomalySwimLaneChart.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AnomalySwimLaneChart.d.ts","sourceRoot":"","sources":["../src/AnomalySwimLaneChart.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,mBAAmB,GAC3B,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,CAAC;AAEhB,MAAM,MAAM,qBAAqB,GAAG;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,qBAAqB,EAAE,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,IAAI,EAAE,qBAAqB,EAAE,CAAC;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAoCF,eAAO,MAAM,oBAAoB;;cAGN,MAAM,qBAAqB,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAA7B,MAAM,qBAAqB,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4EA0QtD,CAAC"}
@@ -0,0 +1,227 @@
1
+ import { defineComponent, h, ref } from "vue";
2
+ import { classNames } from "./classNames.js";
3
+ import { chartDataList } from "./chartScale.js";
4
+ const MARGIN = { top: 28, right: 18, bottom: 44, left: 132 };
5
+ const TONES = [
6
+ "category1",
7
+ "category2",
8
+ "category3",
9
+ "category4",
10
+ "category5",
11
+ "category6",
12
+ "category7",
13
+ "category8",
14
+ ];
15
+ // Continuous scale: score intensity normalised 0..max → category1..8 (shared
16
+ // with HeatmapChart). max ≤ 0 or non-finite score → category1 (floor intensity).
17
+ function toneForScore(score, scoreMax) {
18
+ if (!Number.isFinite(score) || scoreMax <= 0)
19
+ return "category1";
20
+ const ratio = Math.max(0, Math.min(1, score / scoreMax));
21
+ const index = Math.max(0, Math.min(TONES.length - 1, Math.floor(ratio * TONES.length)));
22
+ return TONES[index];
23
+ }
24
+ // Truncate a label to the left margin width (approx. by char count).
25
+ function ellipsize(text, maxChars) {
26
+ if (text.length <= maxChars)
27
+ return text;
28
+ if (maxChars <= 1)
29
+ return "…";
30
+ return `${text.slice(0, maxChars - 1)}…`;
31
+ }
32
+ function formatTick(v) {
33
+ if (Math.abs(v) >= 1000)
34
+ return `${(v / 1000).toFixed(v % 1000 === 0 ? 0 : 1)}k`;
35
+ if (Number.isInteger(v))
36
+ return String(v);
37
+ return v.toFixed(1);
38
+ }
39
+ export const AnomalySwimLaneChart = defineComponent({
40
+ name: "AnomalySwimLaneChart",
41
+ props: {
42
+ data: { type: Array, default: () => [] },
43
+ max: { type: Number, default: undefined },
44
+ label: { type: String, default: undefined },
45
+ width: { type: Number, default: undefined },
46
+ height: { type: Number, default: 300 },
47
+ size: { type: Number, default: undefined },
48
+ class: { type: String, default: undefined },
49
+ },
50
+ setup(props, { attrs }) {
51
+ const hoveredKey = ref(null);
52
+ function handleLeave() {
53
+ hoveredKey.value = null;
54
+ }
55
+ function handlePointerMove(event) {
56
+ const target = event.target;
57
+ if (!(target instanceof Element)) {
58
+ hoveredKey.value = null;
59
+ return;
60
+ }
61
+ hoveredKey.value = target.getAttribute("data-chart-key");
62
+ }
63
+ return () => {
64
+ const data = props.data ?? [];
65
+ const label = props.label;
66
+ const height = props.height ?? 300;
67
+ const resolvedWidth = props.width ?? props.size ?? 520;
68
+ const plotWidth = Math.max(resolvedWidth - MARGIN.left - MARGIN.right, 1);
69
+ const plotHeight = Math.max(height - MARGIN.top - MARGIN.bottom, 1);
70
+ // Normalise: drop unlabeled jobs and non-finite buckets.
71
+ const validData = data
72
+ .filter((d) => typeof d.job === "string" && d.job.length > 0)
73
+ .map((d) => ({
74
+ job: d.job,
75
+ buckets: (d.buckets ?? [])
76
+ .filter((b) => Number.isFinite(b.at))
77
+ .map((b) => ({ at: b.at, score: b.score })),
78
+ }));
79
+ // Distinct time columns (by `at`, ascending).
80
+ const columnOrder = [];
81
+ for (const d of validData) {
82
+ for (const b of d.buckets) {
83
+ if (!columnOrder.includes(b.at))
84
+ columnOrder.push(b.at);
85
+ }
86
+ }
87
+ columnOrder.sort((a, b) => a - b);
88
+ // Effective max score: `max` prop when finite and > 0, else from data.
89
+ const scoreMax = (() => {
90
+ if (typeof props.max === "number" && Number.isFinite(props.max) && props.max > 0)
91
+ return props.max;
92
+ const scores = validData
93
+ .flatMap((d) => d.buckets.map((b) => b.score))
94
+ .filter(Number.isFinite);
95
+ return scores.length > 0 ? Math.max(...scores) : 1;
96
+ })();
97
+ let rows = [];
98
+ if (validData.length > 0 && columnOrder.length > 0) {
99
+ const band = plotHeight / validData.length;
100
+ const rowHeight = Math.min(band * 0.78, 34);
101
+ const colWidth = plotWidth / columnOrder.length;
102
+ rows = validData.map((d, i) => {
103
+ const y = MARGIN.top + band * i + (band - rowHeight) / 2;
104
+ const cells = d.buckets.map((b, j) => {
105
+ const colIndex = Math.max(0, columnOrder.indexOf(b.at));
106
+ const x = MARGIN.left + colIndex * colWidth;
107
+ const w = Math.max(colWidth - 2, 1);
108
+ return {
109
+ key: `${i}-${j}`,
110
+ datum: b,
111
+ x,
112
+ width: w,
113
+ cx: x + w / 2,
114
+ tone: toneForScore(b.score, scoreMax),
115
+ };
116
+ });
117
+ return {
118
+ datum: d,
119
+ index: i,
120
+ y,
121
+ height: rowHeight,
122
+ rowCenterY: MARGIN.top + band * (i + 0.5),
123
+ cells,
124
+ };
125
+ });
126
+ }
127
+ const columns = columnOrder.length === 0
128
+ ? []
129
+ : columnOrder.map((at, index) => ({
130
+ at,
131
+ cx: MARGIN.left + (index + 0.5) * (plotWidth / columnOrder.length),
132
+ }));
133
+ const dataValueItems = validData.map((d) => `${d.job}: ${d.buckets.map((b) => `${b.at} = ${b.score}`).join(", ")}`);
134
+ const legendItems = TONES.map((tone) => ({ tone }));
135
+ const hasLegend = validData.length > 0 && columnOrder.length > 0;
136
+ const svgChildren = [];
137
+ // tick labels (time axis)
138
+ for (const column of columns) {
139
+ svgChildren.push(h("text", {
140
+ key: `t${column.at}`,
141
+ class: "st-anomalySwimLaneChart__tickLabel",
142
+ x: column.cx,
143
+ y: height - MARGIN.bottom + 16,
144
+ "text-anchor": "middle",
145
+ }, formatTick(column.at)));
146
+ }
147
+ // axes
148
+ svgChildren.push(h("line", { class: "st-anomalySwimLaneChart__axis", x1: MARGIN.left, x2: MARGIN.left, y1: MARGIN.top, y2: height - MARGIN.bottom }));
149
+ svgChildren.push(h("line", { class: "st-anomalySwimLaneChart__axis", x1: MARGIN.left, x2: resolvedWidth - MARGIN.right, y1: height - MARGIN.bottom, y2: height - MARGIN.bottom }));
150
+ // one row per job + left label + score cells per bucket
151
+ for (const row of rows) {
152
+ const i = row.index;
153
+ svgChildren.push(h("text", {
154
+ key: `lbl${i}`,
155
+ class: "st-anomalySwimLaneChart__jobLabel",
156
+ x: MARGIN.left - 8,
157
+ y: row.rowCenterY,
158
+ "text-anchor": "end",
159
+ "dominant-baseline": "middle",
160
+ }, ellipsize(row.datum.job, 18)));
161
+ for (const cell of row.cells) {
162
+ const isDim = hoveredKey.value !== null && hoveredKey.value !== cell.key;
163
+ svgChildren.push(h("rect", {
164
+ key: `cell${cell.key}`,
165
+ class: classNames("st-anomalySwimLaneChart__cell", `st-anomalySwimLaneChart__cell--${cell.tone}`, isDim ? "st-anomalySwimLaneChart__cell--dim" : undefined),
166
+ x: cell.x,
167
+ y: row.y,
168
+ width: cell.width,
169
+ height: row.height,
170
+ rx: 2,
171
+ "data-chart-key": cell.key,
172
+ }));
173
+ }
174
+ }
175
+ let hovered = null;
176
+ if (hoveredKey.value !== null) {
177
+ for (const row of rows) {
178
+ for (const cell of row.cells) {
179
+ if (cell.key === hoveredKey.value)
180
+ hovered = { row, cell };
181
+ }
182
+ }
183
+ }
184
+ const children = [
185
+ h("div", {
186
+ class: "st-anomalySwimLaneChart__visual",
187
+ role: "img",
188
+ "aria-label": label,
189
+ onPointermove: handlePointerMove,
190
+ onPointerleave: handleLeave,
191
+ }, [
192
+ h("svg", {
193
+ viewBox: `0 0 ${resolvedWidth} ${height}`,
194
+ preserveAspectRatio: "xMidYMid meet",
195
+ width: "100%",
196
+ height: "100%",
197
+ focusable: "false",
198
+ "aria-hidden": "true",
199
+ }, svgChildren),
200
+ ]),
201
+ ];
202
+ if (hasLegend) {
203
+ children.push(h("div", { class: "st-anomalySwimLaneChart__legend", "aria-hidden": "true" }, [
204
+ h("span", { class: "st-anomalySwimLaneChart__legendText" }, "Low"),
205
+ h("span", { class: "st-anomalySwimLaneChart__legendRamp" }, legendItems.map((item) => h("span", {
206
+ key: item.tone,
207
+ class: `st-anomalySwimLaneChart__legendSwatch st-anomalySwimLaneChart__legendSwatch--${item.tone}`,
208
+ }))),
209
+ h("span", { class: "st-anomalySwimLaneChart__legendText" }, "High"),
210
+ ]));
211
+ }
212
+ children.push(chartDataList(label ?? "anomaly swim lane", dataValueItems));
213
+ if (hovered) {
214
+ children.push(h("div", {
215
+ class: "st-anomalySwimLaneChart__tooltip",
216
+ role: "presentation",
217
+ style: `left:${(hovered.cell.cx / resolvedWidth) * 100}%;top:${(hovered.row.rowCenterY / height) * 100}%`,
218
+ }, [
219
+ h("span", { class: "st-anomalySwimLaneChart__tooltipLabel" }, `${hovered.row.datum.job} · ${hovered.cell.datum.at}`),
220
+ h("span", { class: "st-anomalySwimLaneChart__tooltipValue" }, `${hovered.cell.datum.score}`),
221
+ ]));
222
+ }
223
+ return h("div", { ...attrs, class: classNames("st-anomalySwimLaneChart", props.class) }, children);
224
+ };
225
+ },
226
+ });
227
+ //# sourceMappingURL=AnomalySwimLaneChart.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AnomalySwimLaneChart.js","sourceRoot":"","sources":["../src/AnomalySwimLaneChart.ts"],"names":[],"mappings":"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"}
@@ -65,8 +65,8 @@ export declare const CopyButton: import("vue").DefineComponent<import("vue").Ext
65
65
  size: CopyButtonSize;
66
66
  class: string;
67
67
  label: string;
68
- value: string;
69
68
  text: string;
69
+ value: string;
70
70
  copiedLabel: string;
71
71
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
72
72
  //# sourceMappingURL=CopyButton.d.ts.map
@@ -0,0 +1,76 @@
1
+ export type RibbonChartTone = "category1" | "category2" | "category3" | "category4" | "category5" | "category6" | "category7" | "category8";
2
+ export type RibbonChartDatum = {
3
+ category: string;
4
+ period: string | number;
5
+ value: number;
6
+ tone?: RibbonChartTone;
7
+ };
8
+ export type RibbonChartProps = {
9
+ data: RibbonChartDatum[];
10
+ label?: string;
11
+ width?: number;
12
+ height?: number;
13
+ size?: number;
14
+ class?: string;
15
+ };
16
+ export declare const RibbonChart: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
17
+ data: {
18
+ type: () => RibbonChartDatum[];
19
+ default: () => never[];
20
+ };
21
+ label: {
22
+ type: StringConstructor;
23
+ default: undefined;
24
+ };
25
+ width: {
26
+ type: NumberConstructor;
27
+ default: undefined;
28
+ };
29
+ height: {
30
+ type: NumberConstructor;
31
+ default: number;
32
+ };
33
+ size: {
34
+ type: NumberConstructor;
35
+ default: undefined;
36
+ };
37
+ class: {
38
+ type: StringConstructor;
39
+ default: undefined;
40
+ };
41
+ }>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
42
+ [key: string]: any;
43
+ }>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
44
+ data: {
45
+ type: () => RibbonChartDatum[];
46
+ default: () => never[];
47
+ };
48
+ label: {
49
+ type: StringConstructor;
50
+ default: undefined;
51
+ };
52
+ width: {
53
+ type: NumberConstructor;
54
+ default: undefined;
55
+ };
56
+ height: {
57
+ type: NumberConstructor;
58
+ default: number;
59
+ };
60
+ size: {
61
+ type: NumberConstructor;
62
+ default: undefined;
63
+ };
64
+ class: {
65
+ type: StringConstructor;
66
+ default: undefined;
67
+ };
68
+ }>> & Readonly<{}>, {
69
+ size: number;
70
+ class: string;
71
+ data: RibbonChartDatum[];
72
+ label: string;
73
+ height: number;
74
+ width: number;
75
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
76
+ //# sourceMappingURL=RibbonChart.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RibbonChart.d.ts","sourceRoot":"","sources":["../src/RibbonChart.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,eAAe,GACvB,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,CAAC;AAEhB,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,eAAe,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,gBAAgB,EAAE,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAsCF,eAAO,MAAM,WAAW;;cAGG,MAAM,gBAAgB,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;cAAxB,MAAM,gBAAgB,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4EA0QjD,CAAC"}
@@ -0,0 +1,223 @@
1
+ import { defineComponent, h, ref } from "vue";
2
+ import { classNames } from "./classNames.js";
3
+ import { chartDataList } from "./chartScale.js";
4
+ const MARGIN = { top: 16, right: 16, bottom: 32, left: 16 };
5
+ const RIBBON_SMOOTH = 0.4;
6
+ const TONES = [
7
+ "category1",
8
+ "category2",
9
+ "category3",
10
+ "category4",
11
+ "category5",
12
+ "category6",
13
+ "category7",
14
+ "category8",
15
+ ];
16
+ export const RibbonChart = defineComponent({
17
+ name: "RibbonChart",
18
+ props: {
19
+ data: { type: Array, default: () => [] },
20
+ label: { type: String, default: undefined },
21
+ width: { type: Number, default: undefined },
22
+ height: { type: Number, default: 300 },
23
+ size: { type: Number, default: undefined },
24
+ class: { type: String, default: undefined },
25
+ },
26
+ setup(props, { attrs }) {
27
+ const hoveredKey = ref(null);
28
+ function handleLeave() {
29
+ hoveredKey.value = null;
30
+ }
31
+ function handlePointerMove(event) {
32
+ const target = event.target;
33
+ if (!(target instanceof Element)) {
34
+ hoveredKey.value = null;
35
+ return;
36
+ }
37
+ hoveredKey.value = target.getAttribute("data-chart-key");
38
+ }
39
+ return () => {
40
+ const data = props.data ?? [];
41
+ const label = props.label;
42
+ const height = props.height ?? 300;
43
+ const resolvedWidth = props.width ?? props.size ?? 520;
44
+ const plotWidth = Math.max(resolvedWidth - MARGIN.left - MARGIN.right, 1);
45
+ const plotHeight = Math.max(height - MARGIN.top - MARGIN.bottom, 1);
46
+ // Normalise: drop unlabeled categories and non-finite / negative values.
47
+ const validData = data.filter((d) => typeof d.category === "string" &&
48
+ d.category.length > 0 &&
49
+ Number.isFinite(d.value) &&
50
+ d.value >= 0);
51
+ // Distinct periods (first-seen order), stacked columns left to right.
52
+ const periodOrder = [];
53
+ for (const d of validData) {
54
+ if (!periodOrder.includes(d.period))
55
+ periodOrder.push(d.period);
56
+ }
57
+ // Distinct categories (first-seen order) → categoryN index (1..8, cycled)
58
+ // when no explicit `tone`. An explicit `tone` on a datum wins.
59
+ const categoryOrder = [];
60
+ const explicitToneByCategory = new Map();
61
+ for (const d of validData) {
62
+ if (!categoryOrder.includes(d.category))
63
+ categoryOrder.push(d.category);
64
+ if (d.tone)
65
+ explicitToneByCategory.set(d.category, d.tone);
66
+ }
67
+ const toneOf = (category) => {
68
+ const explicit = explicitToneByCategory.get(category);
69
+ if (explicit)
70
+ return explicit;
71
+ const idx = categoryOrder.indexOf(category);
72
+ return `category${((idx < 0 ? 0 : idx) % 8) + 1}`;
73
+ };
74
+ // Per-period stacking: segments SORTED by descending value (rank), the
75
+ // largest at the foot. Each segment keeps top/bottom y for ribbon paths.
76
+ let columns = [];
77
+ if (validData.length > 0 && periodOrder.length > 0) {
78
+ const band = plotWidth / periodOrder.length;
79
+ const barWidth = Math.min(band * 0.5, 72);
80
+ const totals = periodOrder.map((p) => validData.filter((d) => d.period === p).reduce((s, d) => s + Math.max(d.value, 0), 0));
81
+ const domainMax = Math.max(1, ...totals);
82
+ columns = periodOrder.map((period, pi) => {
83
+ const x = MARGIN.left + band * pi + (band - barWidth) / 2;
84
+ const rows = validData
85
+ .filter((d) => d.period === period)
86
+ .map((d) => ({ category: d.category, value: Math.max(d.value, 0) }))
87
+ .sort((a, b) => b.value - a.value);
88
+ let acc = 0;
89
+ const segments = rows.map((row, ri) => {
90
+ const seg = (row.value / domainMax) * plotHeight;
91
+ const yBottom = MARGIN.top + plotHeight - acc;
92
+ const yTop = yBottom - seg;
93
+ acc += seg;
94
+ return {
95
+ key: `${pi}-${ri}-${row.category}`,
96
+ category: row.category,
97
+ value: row.value,
98
+ tone: toneOf(row.category),
99
+ x,
100
+ width: barWidth,
101
+ yTop,
102
+ yBottom,
103
+ cx: x + barWidth / 2,
104
+ cy: yTop + (yBottom - yTop) / 2,
105
+ };
106
+ });
107
+ return { period, index: pi, cx: MARGIN.left + band * (pi + 0.5), segments };
108
+ });
109
+ }
110
+ // Ribbons: for each category present in two consecutive periods, a smoothed
111
+ // band connecting the left segment (right edge) to the right (left edge).
112
+ const ribbons = [];
113
+ for (let ci = 0; ci < columns.length - 1; ci++) {
114
+ const left = columns[ci];
115
+ const right = columns[ci + 1];
116
+ for (const ls of left.segments) {
117
+ const rs = right.segments.find((s) => s.category === ls.category);
118
+ if (!rs)
119
+ continue;
120
+ const x0 = ls.x + ls.width;
121
+ const x1 = rs.x;
122
+ const mid = x0 + (x1 - x0) * RIBBON_SMOOTH;
123
+ const mid2 = x1 - (x1 - x0) * RIBBON_SMOOTH;
124
+ const d = `M${x0.toFixed(2)},${ls.yTop.toFixed(2)} ` +
125
+ `C${mid.toFixed(2)},${ls.yTop.toFixed(2)} ${mid2.toFixed(2)},${rs.yTop.toFixed(2)} ${x1.toFixed(2)},${rs.yTop.toFixed(2)} ` +
126
+ `L${x1.toFixed(2)},${rs.yBottom.toFixed(2)} ` +
127
+ `C${mid2.toFixed(2)},${rs.yBottom.toFixed(2)} ${mid.toFixed(2)},${ls.yBottom.toFixed(2)} ${x0.toFixed(2)},${ls.yBottom.toFixed(2)} Z`;
128
+ ribbons.push({ key: `${ci}-${ls.category}`, category: ls.category, tone: ls.tone, d });
129
+ }
130
+ }
131
+ const legendItems = categoryOrder.map((category) => ({ category, tone: toneOf(category) }));
132
+ const hasLegend = categoryOrder.length > 0;
133
+ const dataValueItems = categoryOrder.map((category) => `${category}: ${periodOrder
134
+ .map((p) => {
135
+ const found = validData.find((d) => d.category === category && d.period === p);
136
+ return `${p} = ${found ? found.value : 0}`;
137
+ })
138
+ .join(", ")}`);
139
+ let hoveredSegment = null;
140
+ if (hoveredKey.value !== null) {
141
+ for (const col of columns) {
142
+ for (const seg of col.segments) {
143
+ if (seg.key === hoveredKey.value)
144
+ hoveredSegment = seg;
145
+ }
146
+ }
147
+ }
148
+ const svgChildren = [];
149
+ // inter-period ribbons (beneath the segments)
150
+ for (const ribbon of ribbons) {
151
+ const isDim = hoveredSegment !== null && hoveredSegment.category !== ribbon.category;
152
+ svgChildren.push(h("path", {
153
+ key: `r${ribbon.key}`,
154
+ class: classNames("st-ribbonChart__ribbon", `st-ribbonChart__ribbon--${ribbon.tone}`, isDim ? "st-ribbonChart__ribbon--dim" : undefined),
155
+ d: ribbon.d,
156
+ }));
157
+ }
158
+ // per-period stacked segments
159
+ for (const col of columns) {
160
+ svgChildren.push(h("text", {
161
+ key: `p${col.index}`,
162
+ class: "st-ribbonChart__periodLabel",
163
+ x: col.cx,
164
+ y: height - MARGIN.bottom + 16,
165
+ "text-anchor": "middle",
166
+ }, String(col.period)));
167
+ for (const seg of col.segments) {
168
+ const isDim = hoveredSegment !== null && hoveredSegment.category !== seg.category;
169
+ svgChildren.push(h("rect", {
170
+ key: `s${seg.key}`,
171
+ class: classNames("st-ribbonChart__seg", `st-ribbonChart__seg--${seg.tone}`, isDim ? "st-ribbonChart__seg--dim" : undefined),
172
+ x: seg.x,
173
+ y: seg.yTop,
174
+ width: seg.width,
175
+ height: Math.max(seg.yBottom - seg.yTop, 0),
176
+ rx: 2,
177
+ "data-chart-key": seg.key,
178
+ }));
179
+ }
180
+ }
181
+ const children = [
182
+ h("div", {
183
+ class: "st-ribbonChart__visual",
184
+ role: "img",
185
+ "aria-label": label,
186
+ onPointermove: handlePointerMove,
187
+ onPointerleave: handleLeave,
188
+ }, [
189
+ h("svg", {
190
+ viewBox: `0 0 ${resolvedWidth} ${height}`,
191
+ preserveAspectRatio: "xMidYMid meet",
192
+ width: "100%",
193
+ height: "100%",
194
+ focusable: "false",
195
+ "aria-hidden": "true",
196
+ }, svgChildren),
197
+ ]),
198
+ ];
199
+ if (hasLegend) {
200
+ children.push(h("ul", { class: "st-ribbonChart__legend", "aria-label": `Catégories de ${label ?? "ribbon"}` }, legendItems.map((item) => h("li", { key: item.category, class: "st-ribbonChart__legendItem" }, [
201
+ h("span", {
202
+ class: `st-ribbonChart__legendSwatch st-ribbonChart__legendSwatch--${item.tone}`,
203
+ "aria-hidden": "true",
204
+ }),
205
+ item.category,
206
+ ]))));
207
+ }
208
+ children.push(chartDataList(label ?? "ribbon", dataValueItems));
209
+ if (hoveredSegment) {
210
+ children.push(h("div", {
211
+ class: "st-ribbonChart__tooltip",
212
+ role: "presentation",
213
+ style: `left:${(hoveredSegment.cx / resolvedWidth) * 100}%;top:${(hoveredSegment.cy / height) * 100}%`,
214
+ }, [
215
+ h("span", { class: "st-ribbonChart__tooltipLabel" }, hoveredSegment.category),
216
+ h("span", { class: "st-ribbonChart__tooltipValue" }, `${hoveredSegment.value}`),
217
+ ]));
218
+ }
219
+ return h("div", { ...attrs, class: classNames("st-ribbonChart", props.class) }, children);
220
+ };
221
+ },
222
+ });
223
+ //# sourceMappingURL=RibbonChart.js.map