@sentropic/design-system-svelte 0.34.28 → 0.34.32
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/CandlestickChart.svelte +286 -11
- package/dist/CandlestickChart.svelte.d.ts +17 -3
- package/dist/CandlestickChart.svelte.d.ts.map +1 -1
- package/dist/CellDecorationIcon.svelte +39 -0
- package/dist/CellDecorationIcon.svelte.d.ts +7 -0
- package/dist/CellDecorationIcon.svelte.d.ts.map +1 -0
- package/dist/ComboChart.svelte +333 -2
- package/dist/ComboChart.svelte.d.ts +34 -0
- package/dist/ComboChart.svelte.d.ts.map +1 -1
- package/dist/DataTable.svelte +91 -2
- package/dist/DataTable.svelte.d.ts +12 -0
- package/dist/DataTable.svelte.d.ts.map +1 -1
- package/dist/KpiCard.svelte +66 -1
- package/dist/KpiCard.svelte.d.ts +7 -0
- package/dist/KpiCard.svelte.d.ts.map +1 -1
- package/dist/OHLCChart.svelte +286 -11
- package/dist/OHLCChart.svelte.d.ts +17 -3
- package/dist/OHLCChart.svelte.d.ts.map +1 -1
- package/dist/ScatterPlot.svelte +260 -6
- package/dist/ScatterPlot.svelte.d.ts +25 -0
- package/dist/ScatterPlot.svelte.d.ts.map +1 -1
- package/dist/cellDecoration.d.ts +36 -0
- package/dist/cellDecoration.d.ts.map +1 -0
- package/dist/cellDecoration.js +71 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/ComboChart.svelte
CHANGED
|
@@ -25,6 +25,15 @@
|
|
|
25
25
|
|
|
26
26
|
<script lang="ts">
|
|
27
27
|
import ChartDataList from "./ChartDataList.svelte";
|
|
28
|
+
import {
|
|
29
|
+
resolveAnnotations,
|
|
30
|
+
annotationDataListItems,
|
|
31
|
+
polygonPoints,
|
|
32
|
+
type ChartAnnotation
|
|
33
|
+
} from "./chartAnnotations.js";
|
|
34
|
+
import { formatDataLabel, normalizeDataLabels, type DataLabelsProp } from "./chartDataLabels.js";
|
|
35
|
+
import { keyForX, resolveActiveIndex } from "./chartCrosshair.js";
|
|
36
|
+
import { datapointAriaLabel, datapointNavAction, rovingTabIndex } from "./chartKeyboardNav.js";
|
|
28
37
|
|
|
29
38
|
type ComboChartProps = {
|
|
30
39
|
categories: string[];
|
|
@@ -42,6 +51,38 @@
|
|
|
42
51
|
hiddenSeries?: string[];
|
|
43
52
|
/** Emitted on click / Enter / Space on a legend item. */
|
|
44
53
|
onToggleSeries?: (seriesId: string) => void;
|
|
54
|
+
/**
|
|
55
|
+
* Annotation overlay in DATA space. The x coordinate is CATEGORICAL — it
|
|
56
|
+
* matches a category by equality (band centre); the y coordinate (and
|
|
57
|
+
* `value`/`from`/`to`) are LEFT (bar) value-axis numbers. Regions render
|
|
58
|
+
* behind the bars, every other kind above. Additive: absent ⇒ unchanged.
|
|
59
|
+
*/
|
|
60
|
+
annotations?: ChartAnnotation[];
|
|
61
|
+
/**
|
|
62
|
+
* Per-datum value labels on BOTH the bars and the line points. `false`/absent
|
|
63
|
+
* (default) → none. `true` → each value with the chart's numeric formatter.
|
|
64
|
+
* Object → `format(value)` and/or a `position` override. Labels are
|
|
65
|
+
* `aria-hidden` — the values already live in the accessible ChartDataList.
|
|
66
|
+
*/
|
|
67
|
+
dataLabels?: DataLabelsProp;
|
|
68
|
+
/**
|
|
69
|
+
* CONTROLLED synchronised hover key (FR-3). The key is the CATEGORY string.
|
|
70
|
+
* When provided (string or null), the crosshair tracks this key instead of
|
|
71
|
+
* the chart's internal pointer hover (null ⇒ nothing shown). Absent keeps
|
|
72
|
+
* the legacy uncontrolled behaviour.
|
|
73
|
+
*/
|
|
74
|
+
hoverKey?: string | null;
|
|
75
|
+
/** Emitted when the user hovers a bar/point (its CATEGORY) or leaves (`null`). */
|
|
76
|
+
onHoverKeyChange?: (key: string | null) => void;
|
|
77
|
+
/**
|
|
78
|
+
* FR-5 — keyboard navigation of the categories (roving tabindex). When `true`
|
|
79
|
+
* (or implied by wiring `onSelectKey`), a focusable overlay of one column per
|
|
80
|
+
* category is rendered: one tab stop, arrows move, Home/End jump, Enter/Space
|
|
81
|
+
* select, Escape leaves. Absent ⇒ no overlay, rendering unchanged.
|
|
82
|
+
*/
|
|
83
|
+
keyboardNav?: boolean;
|
|
84
|
+
/** Emitted on Enter/Space (category) or `null` on Escape. */
|
|
85
|
+
onSelectKey?: (key: string | null) => void;
|
|
45
86
|
width?: number;
|
|
46
87
|
height?: number;
|
|
47
88
|
label: string;
|
|
@@ -57,12 +98,21 @@
|
|
|
57
98
|
legend = true,
|
|
58
99
|
hiddenSeries,
|
|
59
100
|
onToggleSeries,
|
|
101
|
+
annotations,
|
|
102
|
+
dataLabels,
|
|
103
|
+
hoverKey,
|
|
104
|
+
onHoverKeyChange,
|
|
105
|
+
keyboardNav,
|
|
106
|
+
onSelectKey,
|
|
60
107
|
width = 480,
|
|
61
108
|
height = 240,
|
|
62
109
|
label,
|
|
63
110
|
class: className
|
|
64
111
|
}: ComboChartProps = $props();
|
|
65
112
|
|
|
113
|
+
let focusedIndex: number = $state(-1);
|
|
114
|
+
let datapointRefs: Array<SVGRectElement | null> = [];
|
|
115
|
+
|
|
66
116
|
// Interactive legend is active as soon as the parent wires either prop.
|
|
67
117
|
const legendInteractive = $derived(onToggleSeries !== undefined || hiddenSeries !== undefined);
|
|
68
118
|
const hiddenSet = $derived(new Set(hiddenSeries ?? []));
|
|
@@ -263,22 +313,103 @@
|
|
|
263
313
|
.flatMap((s) => categories.map((c, ci) => `${s.label}, ${c}: ${s.data[ci] ?? 0}`)),
|
|
264
314
|
...lines
|
|
265
315
|
.filter((s) => !hiddenSet.has(s.label))
|
|
266
|
-
.flatMap((s) => categories.map((c, ci) => `${s.label}, ${c}: ${s.data[ci] ?? 0}`))
|
|
316
|
+
.flatMap((s) => categories.map((c, ci) => `${s.label}, ${c}: ${s.data[ci] ?? 0}`)),
|
|
317
|
+
...annotationDataListItems(annotations)
|
|
267
318
|
]);
|
|
268
319
|
|
|
320
|
+
// --- Annotation overlay ---------------------------------------------------
|
|
321
|
+
// `xScale` matches a category by equality → its band centre (relative to the
|
|
322
|
+
// plot); `yScale` maps a LEFT (bar) value-axis number. Out-of-domain coords
|
|
323
|
+
// yield null → dropped, so an annotation never escapes the plot.
|
|
324
|
+
const resolvedAnnotations = $derived(
|
|
325
|
+
resolveAnnotations(annotations, {
|
|
326
|
+
xScale: (v: number | string) => {
|
|
327
|
+
const i = categories.indexOf(String(v));
|
|
328
|
+
return i < 0 ? null : bandCenter(i) - MARGIN.left;
|
|
329
|
+
},
|
|
330
|
+
yScale: (v: number) =>
|
|
331
|
+
Number.isFinite(v) ? scaleLinear(v, leftScale.domainMin, leftScale.domainMax, plotHeight, 0) : null,
|
|
332
|
+
plotLeft: MARGIN.left,
|
|
333
|
+
plotTop: MARGIN.top,
|
|
334
|
+
plotWidth,
|
|
335
|
+
plotHeight
|
|
336
|
+
})
|
|
337
|
+
);
|
|
338
|
+
const annotationRegions = $derived(resolvedAnnotations.filter((a) => a.kind === "region"));
|
|
339
|
+
const annotationAbove = $derived(resolvedAnnotations.filter((a) => a.kind !== "region"));
|
|
340
|
+
|
|
341
|
+
// --- Data labels ----------------------------------------------------------
|
|
342
|
+
// One value label per visible bar (outside) + per visible line point (top).
|
|
343
|
+
const dataLabelOpts = $derived(normalizeDataLabels(dataLabels));
|
|
344
|
+
const barDataLabelItems = $derived(
|
|
345
|
+
dataLabelOpts.enabled
|
|
346
|
+
? barGroups.flatMap((group, gi) =>
|
|
347
|
+
group.map((seg, si) => {
|
|
348
|
+
const inside = dataLabelOpts.position === "inside" || dataLabelOpts.position === "center";
|
|
349
|
+
return {
|
|
350
|
+
key: `bar-${gi}-${si}`,
|
|
351
|
+
x: seg.cx,
|
|
352
|
+
y: inside ? seg.y + seg.height / 2 : seg.cy - 6,
|
|
353
|
+
text: formatDataLabel(seg.value, dataLabelOpts, formatTick),
|
|
354
|
+
baseline: (inside ? "middle" : "auto") as "middle" | "auto"
|
|
355
|
+
};
|
|
356
|
+
})
|
|
357
|
+
)
|
|
358
|
+
: []
|
|
359
|
+
);
|
|
360
|
+
const lineDataLabelItems = $derived(
|
|
361
|
+
dataLabelOpts.enabled
|
|
362
|
+
? lineSeries.flatMap((series, li) =>
|
|
363
|
+
series.hidden
|
|
364
|
+
? []
|
|
365
|
+
: series.points.map((p, pi) => {
|
|
366
|
+
const center =
|
|
367
|
+
dataLabelOpts.position === "center" || dataLabelOpts.position === "inside";
|
|
368
|
+
return {
|
|
369
|
+
key: `line-${li}-${pi}`,
|
|
370
|
+
x: p.x,
|
|
371
|
+
y: center ? p.y : p.y - 8,
|
|
372
|
+
text: formatDataLabel(p.value, dataLabelOpts, formatTick),
|
|
373
|
+
baseline: (center ? "middle" : "auto") as "middle" | "auto"
|
|
374
|
+
};
|
|
375
|
+
})
|
|
376
|
+
)
|
|
377
|
+
: []
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
// --- Crosshair + keyboard nav keys (FR-3 / FR-5) --------------------------
|
|
381
|
+
// The shared datum is the CATEGORY: its key is the category string.
|
|
382
|
+
const hoverKeys = $derived(categories.map((c) => keyForX(c)));
|
|
383
|
+
const categorySummary = (ci: number): string =>
|
|
384
|
+
[
|
|
385
|
+
...bars.filter((s) => !hiddenSet.has(s.label)),
|
|
386
|
+
...lines.filter((s) => !hiddenSet.has(s.label))
|
|
387
|
+
]
|
|
388
|
+
.map((s) => {
|
|
389
|
+
const raw = s.data[ci];
|
|
390
|
+
return raw == null || !Number.isFinite(raw) ? null : `${s.label}: ${raw}`;
|
|
391
|
+
})
|
|
392
|
+
.filter((v): v is string => v !== null)
|
|
393
|
+
.join(", ");
|
|
394
|
+
|
|
269
395
|
type Hover =
|
|
270
396
|
| { kind: "bar"; gi: number; si: number }
|
|
271
397
|
| { kind: "line"; li: number; pi: number }
|
|
272
398
|
| null;
|
|
273
|
-
let hovered
|
|
399
|
+
let hovered = $state<Hover>(null);
|
|
274
400
|
|
|
401
|
+
function emitHoverKey(index: number | null) {
|
|
402
|
+
onHoverKeyChange?.(index == null ? null : hoverKeys[index] ?? null);
|
|
403
|
+
}
|
|
275
404
|
function handleLeave() {
|
|
276
405
|
hovered = null;
|
|
406
|
+
emitHoverKey(null);
|
|
277
407
|
}
|
|
278
408
|
function handleVisualPointerMove(event: PointerEvent) {
|
|
279
409
|
const target = event.target;
|
|
280
410
|
if (!(target instanceof Element)) {
|
|
281
411
|
hovered = null;
|
|
412
|
+
emitHoverKey(null);
|
|
282
413
|
return;
|
|
283
414
|
}
|
|
284
415
|
const kind = target.getAttribute("data-chart-kind");
|
|
@@ -286,10 +417,50 @@
|
|
|
286
417
|
const b = Number(target.getAttribute("data-chart-b"));
|
|
287
418
|
if (kind === "bar" && Number.isInteger(a) && Number.isInteger(b)) {
|
|
288
419
|
hovered = { kind: "bar", gi: a, si: b };
|
|
420
|
+
emitHoverKey(a); // gi === category index
|
|
289
421
|
} else if (kind === "line" && Number.isInteger(a) && Number.isInteger(b)) {
|
|
290
422
|
hovered = { kind: "line", li: a, pi: b };
|
|
423
|
+
emitHoverKey(b); // pi === category index
|
|
291
424
|
} else {
|
|
292
425
|
hovered = null;
|
|
426
|
+
emitHoverKey(null);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Category index whose crosshair is DISPLAYED: the controlled `hoverKey` when
|
|
431
|
+
// provided (resolved against the category keys), else the internal pointer
|
|
432
|
+
// category (derived from the hovered bar/line datum).
|
|
433
|
+
const internalCategoryIndex = $derived(
|
|
434
|
+
hovered == null ? null : hovered.kind === "bar" ? hovered.gi : hovered.pi
|
|
435
|
+
);
|
|
436
|
+
const activeCategoryIndex = $derived(
|
|
437
|
+
resolveActiveIndex(hoverKey, internalCategoryIndex, hoverKeys)
|
|
438
|
+
);
|
|
439
|
+
const crosshairX = $derived(activeCategoryIndex >= 0 ? bandCenter(activeCategoryIndex) : null);
|
|
440
|
+
|
|
441
|
+
// --- Keyboard navigation (FR-5) ------------------------------------------
|
|
442
|
+
// One focusable transparent column per category carries the roving tab stop.
|
|
443
|
+
const navEnabled = $derived(
|
|
444
|
+
(keyboardNav === true || onSelectKey !== undefined) && categories.length > 0
|
|
445
|
+
);
|
|
446
|
+
function focusDatum(index: number) {
|
|
447
|
+
focusedIndex = index;
|
|
448
|
+
datapointRefs[index]?.focus();
|
|
449
|
+
emitHoverKey(index);
|
|
450
|
+
}
|
|
451
|
+
function handleDatapointKeyDown(event: KeyboardEvent, index: number) {
|
|
452
|
+
const action = datapointNavAction(event.key, index, categories.length);
|
|
453
|
+
if (!action) return;
|
|
454
|
+
event.preventDefault();
|
|
455
|
+
if (action.kind === "move") {
|
|
456
|
+
focusDatum(action.index);
|
|
457
|
+
} else if (action.kind === "select") {
|
|
458
|
+
onSelectKey?.(hoverKeys[index] ?? null);
|
|
459
|
+
} else {
|
|
460
|
+
focusedIndex = -1;
|
|
461
|
+
emitHoverKey(null);
|
|
462
|
+
onSelectKey?.(null);
|
|
463
|
+
(event.currentTarget as SVGElement).blur();
|
|
293
464
|
}
|
|
294
465
|
}
|
|
295
466
|
|
|
@@ -403,6 +574,20 @@
|
|
|
403
574
|
</text>
|
|
404
575
|
{/each}
|
|
405
576
|
|
|
577
|
+
<!-- Annotation regions sit BEHIND the bars (filled bands). -->
|
|
578
|
+
{#if annotationRegions.length > 0}
|
|
579
|
+
<g class="st-comboChart__annotations st-comboChart__annotations--behind">
|
|
580
|
+
{#each annotationRegions as a (a.key)}
|
|
581
|
+
{#if a.kind === "region"}
|
|
582
|
+
<rect class="st-comboChart__annotationRegion" x={a.x} y={a.y} width={a.width} height={a.height} />
|
|
583
|
+
{#if a.label}
|
|
584
|
+
<text class="st-comboChart__annotationLabel" x={a.x + 4} y={a.y + 11}>{a.label}</text>
|
|
585
|
+
{/if}
|
|
586
|
+
{/if}
|
|
587
|
+
{/each}
|
|
588
|
+
</g>
|
|
589
|
+
{/if}
|
|
590
|
+
|
|
406
591
|
<!-- bars -->
|
|
407
592
|
{#each barGroups as group, gi (gi)}
|
|
408
593
|
{#each group as seg, si (si)}
|
|
@@ -444,7 +629,93 @@
|
|
|
444
629
|
{/each}
|
|
445
630
|
{/if}
|
|
446
631
|
{/each}
|
|
632
|
+
|
|
633
|
+
<!-- Annotations ABOVE the bars/lines: lines, shapes, points, labels. -->
|
|
634
|
+
{#if annotationAbove.length > 0}
|
|
635
|
+
<g class="st-comboChart__annotations st-comboChart__annotations--above">
|
|
636
|
+
{#each annotationAbove as a (a.key)}
|
|
637
|
+
{#if a.kind === "line"}
|
|
638
|
+
<line class="st-comboChart__annotationLine" x1={a.x1} y1={a.y1} x2={a.x2} y2={a.y2} />
|
|
639
|
+
{#if a.label}
|
|
640
|
+
<text
|
|
641
|
+
class="st-comboChart__annotationLabel"
|
|
642
|
+
x={a.axis === "x" ? a.x1 + 4 : MARGIN.left + plotWidth - 4}
|
|
643
|
+
y={a.axis === "x" ? MARGIN.top + 11 : a.y1 - 4}
|
|
644
|
+
text-anchor={a.axis === "x" ? "start" : "end"}
|
|
645
|
+
>
|
|
646
|
+
{a.label}
|
|
647
|
+
</text>
|
|
648
|
+
{/if}
|
|
649
|
+
{:else if a.kind === "shape"}
|
|
650
|
+
<polygon class="st-comboChart__annotationShape" points={polygonPoints(a.points)} />
|
|
651
|
+
{#if a.label}
|
|
652
|
+
<text class="st-comboChart__annotationLabel" x={a.labelX} y={a.labelY} text-anchor="middle">{a.label}</text>
|
|
653
|
+
{/if}
|
|
654
|
+
{:else if a.kind === "point"}
|
|
655
|
+
<circle class="st-comboChart__annotationPoint" cx={a.x} cy={a.y} r="4.5" />
|
|
656
|
+
{#if a.label}
|
|
657
|
+
<text class="st-comboChart__annotationLabel" x={a.x} y={a.y - 8} text-anchor="middle">{a.label}</text>
|
|
658
|
+
{/if}
|
|
659
|
+
{:else if a.kind === "label"}
|
|
660
|
+
<text class="st-comboChart__annotationText" x={a.x} y={a.y} text-anchor={a.anchor}>{a.text}</text>
|
|
661
|
+
{/if}
|
|
662
|
+
{/each}
|
|
663
|
+
</g>
|
|
664
|
+
{/if}
|
|
665
|
+
|
|
666
|
+
<!-- Data labels — one value per bar + per line point, on top. aria-hidden. -->
|
|
667
|
+
{#if barDataLabelItems.length + lineDataLabelItems.length > 0}
|
|
668
|
+
<g class="st-comboChart__dataLabels" aria-hidden="true">
|
|
669
|
+
{#each barDataLabelItems as d (d.key)}
|
|
670
|
+
<text class="st-comboChart__dataLabel" x={d.x} y={d.y} text-anchor="middle" dominant-baseline={d.baseline}>{d.text}</text>
|
|
671
|
+
{/each}
|
|
672
|
+
{#each lineDataLabelItems as d (d.key)}
|
|
673
|
+
<text class="st-comboChart__dataLabel" x={d.x} y={d.y} text-anchor="middle" dominant-baseline={d.baseline}>{d.text}</text>
|
|
674
|
+
{/each}
|
|
675
|
+
</g>
|
|
676
|
+
{/if}
|
|
677
|
+
|
|
678
|
+
<!-- Crosshair (FR-3) — a tokenised dashed line on the CATEGORY axis at the
|
|
679
|
+
active category. Decorative (aria-hidden). -->
|
|
680
|
+
{#if crosshairX !== null}
|
|
681
|
+
<g class="st-comboChart__crosshair" aria-hidden="true">
|
|
682
|
+
<line class="st-comboChart__crosshairLine" x1={crosshairX} x2={crosshairX} y1={MARGIN.top} y2={MARGIN.top + plotHeight} />
|
|
683
|
+
</g>
|
|
684
|
+
{/if}
|
|
447
685
|
</svg>
|
|
686
|
+
|
|
687
|
+
<!-- Keyboard navigation overlay (FR-5) — a focusable, transparent column per
|
|
688
|
+
category. NOT aria-hidden: the accessible roving cursor. -->
|
|
689
|
+
{#if navEnabled}
|
|
690
|
+
<svg
|
|
691
|
+
class="st-comboChart__navLayer"
|
|
692
|
+
viewBox="0 0 {width} {height}"
|
|
693
|
+
preserveAspectRatio="xMidYMid meet"
|
|
694
|
+
width="100%"
|
|
695
|
+
height="100%"
|
|
696
|
+
role="group"
|
|
697
|
+
aria-label="{label} — points de données"
|
|
698
|
+
>
|
|
699
|
+
{#each categories as category, ci (ci)}
|
|
700
|
+
<rect
|
|
701
|
+
bind:this={datapointRefs[ci]}
|
|
702
|
+
class="st-comboChart__navDatum"
|
|
703
|
+
x={MARGIN.left + (plotWidth / Math.max(categories.length, 1)) * ci}
|
|
704
|
+
y={MARGIN.top}
|
|
705
|
+
width={plotWidth / Math.max(categories.length, 1)}
|
|
706
|
+
height={plotHeight}
|
|
707
|
+
role="img"
|
|
708
|
+
tabindex={rovingTabIndex(ci, focusedIndex, categories.length)}
|
|
709
|
+
aria-label={datapointAriaLabel(category, categorySummary(ci))}
|
|
710
|
+
onkeydown={(event) => handleDatapointKeyDown(event, ci)}
|
|
711
|
+
onfocus={() => {
|
|
712
|
+
focusedIndex = ci;
|
|
713
|
+
emitHoverKey(ci);
|
|
714
|
+
}}
|
|
715
|
+
/>
|
|
716
|
+
{/each}
|
|
717
|
+
</svg>
|
|
718
|
+
{/if}
|
|
448
719
|
</div>
|
|
449
720
|
|
|
450
721
|
<ChartDataList {label} items={dataValueItems} />
|
|
@@ -578,6 +849,66 @@
|
|
|
578
849
|
.st-comboChart__dot--category7 { fill: var(--st-semantic-data-category7); }
|
|
579
850
|
.st-comboChart__dot--category8 { fill: var(--st-semantic-data-category8); }
|
|
580
851
|
|
|
852
|
+
/* --- Annotation layer ----------------------------------------------------
|
|
853
|
+
Regions render BEHIND the bars; lines/shapes/points/labels render ABOVE. */
|
|
854
|
+
.st-comboChart__annotationRegion {
|
|
855
|
+
fill: color-mix(in srgb, var(--st-semantic-feedback-info) 12%, transparent);
|
|
856
|
+
stroke: none;
|
|
857
|
+
}
|
|
858
|
+
.st-comboChart__annotationLine {
|
|
859
|
+
stroke: var(--st-semantic-feedback-info);
|
|
860
|
+
stroke-width: 1.5;
|
|
861
|
+
stroke-dasharray: 4 3;
|
|
862
|
+
}
|
|
863
|
+
.st-comboChart__annotationShape {
|
|
864
|
+
fill: color-mix(in srgb, var(--st-semantic-feedback-info) 14%, transparent);
|
|
865
|
+
stroke: var(--st-semantic-feedback-info);
|
|
866
|
+
stroke-width: 1.5;
|
|
867
|
+
}
|
|
868
|
+
.st-comboChart__annotationPoint {
|
|
869
|
+
fill: var(--st-semantic-feedback-info);
|
|
870
|
+
stroke: var(--st-semantic-surface-default);
|
|
871
|
+
stroke-width: 1.5;
|
|
872
|
+
}
|
|
873
|
+
.st-comboChart__annotationLabel,
|
|
874
|
+
.st-comboChart__annotationText {
|
|
875
|
+
fill: var(--st-semantic-text-primary);
|
|
876
|
+
font-size: 0.625rem;
|
|
877
|
+
font-weight: 600;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
/* Data labels — per-bar + per-point value, drawn on top. Token-only colour. */
|
|
881
|
+
.st-comboChart__dataLabel {
|
|
882
|
+
fill: var(--st-semantic-text-primary);
|
|
883
|
+
font-size: 0.6875rem;
|
|
884
|
+
font-weight: 600;
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
/* --- Crosshair layer (FR-3) ----------------------------------------------
|
|
888
|
+
A tokenised dashed line on the CATEGORY axis at the active category. */
|
|
889
|
+
.st-comboChart__crosshairLine {
|
|
890
|
+
stroke: var(--st-semantic-border-strong);
|
|
891
|
+
stroke-width: 1;
|
|
892
|
+
stroke-dasharray: 3 3;
|
|
893
|
+
opacity: 0.7;
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
/* --- Keyboard navigation layer (FR-5) ------------------------------------
|
|
897
|
+
A focusable, transparent overlay of one column per category. */
|
|
898
|
+
.st-comboChart__navLayer {
|
|
899
|
+
inset: 0;
|
|
900
|
+
position: absolute;
|
|
901
|
+
}
|
|
902
|
+
.st-comboChart__navDatum {
|
|
903
|
+
fill: transparent;
|
|
904
|
+
outline: none;
|
|
905
|
+
}
|
|
906
|
+
.st-comboChart__navDatum:focus-visible {
|
|
907
|
+
fill: color-mix(in srgb, var(--st-semantic-border-interactive) 12%, transparent);
|
|
908
|
+
outline: 2px solid var(--st-semantic-border-interactive);
|
|
909
|
+
outline-offset: 1px;
|
|
910
|
+
}
|
|
911
|
+
|
|
581
912
|
@media (prefers-reduced-motion: reduce) {
|
|
582
913
|
.st-comboChart__bar,
|
|
583
914
|
.st-comboChart__dot {
|
|
@@ -10,6 +10,8 @@ export type ComboChartLineSeries = {
|
|
|
10
10
|
tone?: ComboChartTone;
|
|
11
11
|
smooth?: boolean;
|
|
12
12
|
};
|
|
13
|
+
import { type ChartAnnotation } from "./chartAnnotations.js";
|
|
14
|
+
import { type DataLabelsProp } from "./chartDataLabels.js";
|
|
13
15
|
type ComboChartProps = {
|
|
14
16
|
categories: string[];
|
|
15
17
|
bars?: ComboChartBarSeries[];
|
|
@@ -26,6 +28,38 @@ type ComboChartProps = {
|
|
|
26
28
|
hiddenSeries?: string[];
|
|
27
29
|
/** Emitted on click / Enter / Space on a legend item. */
|
|
28
30
|
onToggleSeries?: (seriesId: string) => void;
|
|
31
|
+
/**
|
|
32
|
+
* Annotation overlay in DATA space. The x coordinate is CATEGORICAL — it
|
|
33
|
+
* matches a category by equality (band centre); the y coordinate (and
|
|
34
|
+
* `value`/`from`/`to`) are LEFT (bar) value-axis numbers. Regions render
|
|
35
|
+
* behind the bars, every other kind above. Additive: absent ⇒ unchanged.
|
|
36
|
+
*/
|
|
37
|
+
annotations?: ChartAnnotation[];
|
|
38
|
+
/**
|
|
39
|
+
* Per-datum value labels on BOTH the bars and the line points. `false`/absent
|
|
40
|
+
* (default) → none. `true` → each value with the chart's numeric formatter.
|
|
41
|
+
* Object → `format(value)` and/or a `position` override. Labels are
|
|
42
|
+
* `aria-hidden` — the values already live in the accessible ChartDataList.
|
|
43
|
+
*/
|
|
44
|
+
dataLabels?: DataLabelsProp;
|
|
45
|
+
/**
|
|
46
|
+
* CONTROLLED synchronised hover key (FR-3). The key is the CATEGORY string.
|
|
47
|
+
* When provided (string or null), the crosshair tracks this key instead of
|
|
48
|
+
* the chart's internal pointer hover (null ⇒ nothing shown). Absent keeps
|
|
49
|
+
* the legacy uncontrolled behaviour.
|
|
50
|
+
*/
|
|
51
|
+
hoverKey?: string | null;
|
|
52
|
+
/** Emitted when the user hovers a bar/point (its CATEGORY) or leaves (`null`). */
|
|
53
|
+
onHoverKeyChange?: (key: string | null) => void;
|
|
54
|
+
/**
|
|
55
|
+
* FR-5 — keyboard navigation of the categories (roving tabindex). When `true`
|
|
56
|
+
* (or implied by wiring `onSelectKey`), a focusable overlay of one column per
|
|
57
|
+
* category is rendered: one tab stop, arrows move, Home/End jump, Enter/Space
|
|
58
|
+
* select, Escape leaves. Absent ⇒ no overlay, rendering unchanged.
|
|
59
|
+
*/
|
|
60
|
+
keyboardNav?: boolean;
|
|
61
|
+
/** Emitted on Enter/Space (category) or `null` on Escape. */
|
|
62
|
+
onSelectKey?: (key: string | null) => void;
|
|
29
63
|
width?: number;
|
|
30
64
|
height?: number;
|
|
31
65
|
label: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ComboChart.svelte.d.ts","sourceRoot":"","sources":["../src/lib/ComboChart.svelte.ts"],"names":[],"mappings":"AAGE,MAAM,MAAM,cAAc,GACtB,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,CAAC;AAEhB,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,CAAC,EAAE,cAAc,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;
|
|
1
|
+
{"version":3,"file":"ComboChart.svelte.d.ts","sourceRoot":"","sources":["../src/lib/ComboChart.svelte.ts"],"names":[],"mappings":"AAGE,MAAM,MAAM,cAAc,GACtB,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,CAAC;AAEhB,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,CAAC,EAAE,cAAc,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAIJ,OAAO,EAIH,KAAK,eAAe,EACrB,MAAM,uBAAuB,CAAC;AACjC,OAAO,EAAwC,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAK/F,KAAK,eAAe,GAAG;IACrB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,IAAI,CAAC,EAAE,mBAAmB,EAAE,CAAC;IAC7B,KAAK,CAAC,EAAE,oBAAoB,EAAE,CAAC;IAC/B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,yDAAyD;IACzD,cAAc,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C;;;;;OAKG;IACH,WAAW,CAAC,EAAE,eAAe,EAAE,CAAC;IAChC;;;;;OAKG;IACH,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,kFAAkF;IAClF,gBAAgB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAChD;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,6DAA6D;IAC7D,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AA4jBJ,QAAA,MAAM,UAAU,qDAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
|
package/dist/DataTable.svelte
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
<script lang="ts" module>
|
|
2
2
|
import type { Snippet } from "svelte";
|
|
3
|
+
import type { CellDecoration } from "./cellDecoration.js";
|
|
4
|
+
|
|
5
|
+
export type { CellDecoration, CellDecorationIntent } from "./cellDecoration.js";
|
|
3
6
|
|
|
4
7
|
export interface DataTableColumn<R = DataTableRow> {
|
|
5
8
|
key: string;
|
|
@@ -8,6 +11,11 @@
|
|
|
8
11
|
align?: "start" | "center" | "end";
|
|
9
12
|
width?: string;
|
|
10
13
|
cell?: Snippet<[R, DataTableColumn<R>]>;
|
|
14
|
+
/**
|
|
15
|
+
* Conditional formatting (confort) : décoration sémantique calculée par
|
|
16
|
+
* cellule. Si une `decorations` map est aussi fournie, la map gagne.
|
|
17
|
+
*/
|
|
18
|
+
cellDecoration?: (row: R, value: unknown, colId: string) => CellDecoration | null;
|
|
11
19
|
}
|
|
12
20
|
|
|
13
21
|
export interface DataTableRow {
|
|
@@ -25,10 +33,17 @@
|
|
|
25
33
|
|
|
26
34
|
<script lang="ts">
|
|
27
35
|
import type { HTMLTableAttributes } from "svelte/elements";
|
|
36
|
+
import { cellDecorationClass, cellDecorationLabel } from "./cellDecoration.js";
|
|
37
|
+
import CellDecorationIcon from "./CellDecorationIcon.svelte";
|
|
28
38
|
|
|
29
39
|
type DataTableProps = Omit<HTMLTableAttributes, "class"> & {
|
|
30
40
|
columns: DataTableColumn[];
|
|
31
41
|
rows: DataTableRow[];
|
|
42
|
+
/**
|
|
43
|
+
* Conditional formatting : décorations sémantiques par cellule, indexées
|
|
44
|
+
* `rowId` → `colId` → décoration. Prioritaire sur `column.cellDecoration`.
|
|
45
|
+
*/
|
|
46
|
+
decorations?: Record<string, Record<string, CellDecoration>>;
|
|
32
47
|
caption?: string;
|
|
33
48
|
size?: "sm" | "md" | "lg";
|
|
34
49
|
selectable?: DataTableSelectMode;
|
|
@@ -53,6 +68,7 @@
|
|
|
53
68
|
let {
|
|
54
69
|
columns,
|
|
55
70
|
rows,
|
|
71
|
+
decorations,
|
|
56
72
|
caption,
|
|
57
73
|
size = "md",
|
|
58
74
|
selectable = "none",
|
|
@@ -191,6 +207,28 @@
|
|
|
191
207
|
return String(row[key] ?? "");
|
|
192
208
|
}
|
|
193
209
|
|
|
210
|
+
function resolveDecoration(row: DataTableRow, column: DataTableColumn): CellDecoration | null {
|
|
211
|
+
// La map `decorations` gagne sur le callback `column.cellDecoration`.
|
|
212
|
+
const fromMap = decorations?.[row.id]?.[column.key];
|
|
213
|
+
if (fromMap) return fromMap;
|
|
214
|
+
if (column.cellDecoration) {
|
|
215
|
+
return column.cellDecoration(row, row[column.key], column.key) ?? null;
|
|
216
|
+
}
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function cellClass(column: DataTableColumn, decoration: CellDecoration | null) {
|
|
221
|
+
return (
|
|
222
|
+
[
|
|
223
|
+
alignClass(column.align),
|
|
224
|
+
decoration && "st-cell",
|
|
225
|
+
decoration && cellDecorationClass(decoration.intent),
|
|
226
|
+
]
|
|
227
|
+
.filter(Boolean)
|
|
228
|
+
.join(" ") || undefined
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
|
|
194
232
|
function goToPage(target: number) {
|
|
195
233
|
if (target >= 1 && target <= pageCount && target !== safePage) {
|
|
196
234
|
page = target;
|
|
@@ -302,8 +340,24 @@
|
|
|
302
340
|
</td>
|
|
303
341
|
{/if}
|
|
304
342
|
{#each columns as column (column.key)}
|
|
305
|
-
|
|
306
|
-
|
|
343
|
+
{@const decoration = resolveDecoration(row, column)}
|
|
344
|
+
<td
|
|
345
|
+
class={cellClass(column, decoration)}
|
|
346
|
+
title={decoration ? cellDecorationLabel[decoration.intent] : undefined}
|
|
347
|
+
>
|
|
348
|
+
{#if decoration}
|
|
349
|
+
<span class="st-cell__content">
|
|
350
|
+
<CellDecorationIcon icon={decoration.icon} />
|
|
351
|
+
<span>
|
|
352
|
+
{#if column.cell}
|
|
353
|
+
{@render column.cell(row, column)}
|
|
354
|
+
{:else}
|
|
355
|
+
{cellValue(row, column.key)}
|
|
356
|
+
{/if}
|
|
357
|
+
</span>
|
|
358
|
+
<span class="st-visually-hidden">{cellDecorationLabel[decoration.intent]}</span>
|
|
359
|
+
</span>
|
|
360
|
+
{:else if column.cell}
|
|
307
361
|
{@render column.cell(row, column)}
|
|
308
362
|
{:else}
|
|
309
363
|
{cellValue(row, column.key)}
|
|
@@ -426,6 +480,41 @@
|
|
|
426
480
|
text-align: end;
|
|
427
481
|
}
|
|
428
482
|
|
|
483
|
+
/* Conditional formatting (« classe Power-BI ») — décoration sémantique de
|
|
484
|
+
cellule. Le fond teinté réutilise le pattern accessible de Badge/Tag
|
|
485
|
+
(color-mix 14% sur token feedback) ; le texte garde le token plein. Le fond
|
|
486
|
+
n'est jamais la seule indication : icône + texte SR accompagnent l'intent. */
|
|
487
|
+
.st-cell__content {
|
|
488
|
+
align-items: center;
|
|
489
|
+
display: inline-flex;
|
|
490
|
+
gap: 0.375rem;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
.st-cell--intent-positive {
|
|
494
|
+
background: color-mix(in srgb, var(--st-semantic-feedback-success) 14%, white);
|
|
495
|
+
color: var(--st-semantic-feedback-success);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
.st-cell--intent-negative {
|
|
499
|
+
background: color-mix(in srgb, var(--st-semantic-feedback-error) 14%, white);
|
|
500
|
+
color: var(--st-semantic-feedback-error);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
.st-cell--intent-warning {
|
|
504
|
+
background: color-mix(in srgb, var(--st-semantic-feedback-warning) 14%, white);
|
|
505
|
+
color: var(--st-semantic-feedback-warning);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
.st-cell--intent-info {
|
|
509
|
+
background: color-mix(in srgb, var(--st-semantic-feedback-info) 14%, white);
|
|
510
|
+
color: var(--st-semantic-feedback-info);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
.st-cell--intent-neutral {
|
|
514
|
+
background: var(--st-semantic-surface-subtle);
|
|
515
|
+
color: var(--st-semantic-text-secondary);
|
|
516
|
+
}
|
|
517
|
+
|
|
429
518
|
.st-dataTable__select {
|
|
430
519
|
width: 2.5rem;
|
|
431
520
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { Snippet } from "svelte";
|
|
2
|
+
import type { CellDecoration } from "./cellDecoration.js";
|
|
3
|
+
export type { CellDecoration, CellDecorationIntent } from "./cellDecoration.js";
|
|
2
4
|
export interface DataTableColumn<R = DataTableRow> {
|
|
3
5
|
key: string;
|
|
4
6
|
label: string;
|
|
@@ -6,6 +8,11 @@ export interface DataTableColumn<R = DataTableRow> {
|
|
|
6
8
|
align?: "start" | "center" | "end";
|
|
7
9
|
width?: string;
|
|
8
10
|
cell?: Snippet<[R, DataTableColumn<R>]>;
|
|
11
|
+
/**
|
|
12
|
+
* Conditional formatting (confort) : décoration sémantique calculée par
|
|
13
|
+
* cellule. Si une `decorations` map est aussi fournie, la map gagne.
|
|
14
|
+
*/
|
|
15
|
+
cellDecoration?: (row: R, value: unknown, colId: string) => CellDecoration | null;
|
|
9
16
|
}
|
|
10
17
|
export interface DataTableRow {
|
|
11
18
|
id: string;
|
|
@@ -20,6 +27,11 @@ import type { HTMLTableAttributes } from "svelte/elements";
|
|
|
20
27
|
type DataTableProps = Omit<HTMLTableAttributes, "class"> & {
|
|
21
28
|
columns: DataTableColumn[];
|
|
22
29
|
rows: DataTableRow[];
|
|
30
|
+
/**
|
|
31
|
+
* Conditional formatting : décorations sémantiques par cellule, indexées
|
|
32
|
+
* `rowId` → `colId` → décoration. Prioritaire sur `column.cellDecoration`.
|
|
33
|
+
*/
|
|
34
|
+
decorations?: Record<string, Record<string, CellDecoration>>;
|
|
23
35
|
caption?: string;
|
|
24
36
|
size?: "sm" | "md" | "lg";
|
|
25
37
|
selectable?: DataTableSelectMode;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DataTable.svelte.d.ts","sourceRoot":"","sources":["../src/lib/DataTable.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"DataTable.svelte.d.ts","sourceRoot":"","sources":["../src/lib/DataTable.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,YAAY,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAEhF,MAAM,WAAW,eAAe,CAAC,CAAC,GAAG,YAAY;IAC/C,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACxC;;;OAGG;IACH,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,KAAK,cAAc,GAAG,IAAI,CAAC;CACnF;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,QAAQ,GAAG,UAAU,CAAC;AAEjE,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,KAAK,GAAG,MAAM,CAAC;CAC3B;AAGH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAKzD,KAAK,cAAc,GAAG,IAAI,CAAC,mBAAmB,EAAE,OAAO,CAAC,GAAG;IACzD,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,IAAI,EAAE,YAAY,EAAE,CAAC;IACrB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC;IAC7D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,UAAU,CAAC,EAAE,mBAAmB,CAAC;IACjC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,KAAK,MAAM,CAAC;IAC9E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,IAAI,CAAC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAuSJ,QAAA,MAAM,SAAS,mFAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
|