@sentropic/design-system-svelte 0.34.28 → 0.34.33
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/Badge.svelte +66 -2
- package/dist/Badge.svelte.d.ts +21 -0
- package/dist/Badge.svelte.d.ts.map +1 -1
- 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/Collapsible.svelte +55 -1
- package/dist/Collapsible.svelte.d.ts +15 -0
- package/dist/Collapsible.svelte.d.ts.map +1 -1
- package/dist/Collapsible.test.d.ts +2 -0
- package/dist/Collapsible.test.d.ts.map +1 -0
- package/dist/Collapsible.test.js +68 -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/SelectableList.svelte +36 -17
- package/dist/SelectableList.svelte.d.ts.map +1 -1
- package/dist/SelectableRow.svelte +53 -1
- package/dist/SelectableRow.svelte.d.ts +10 -0
- package/dist/SelectableRow.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 +3 -3
package/dist/Collapsible.svelte
CHANGED
|
@@ -7,7 +7,22 @@
|
|
|
7
7
|
/** État ouvert (bindable). */
|
|
8
8
|
open?: boolean;
|
|
9
9
|
title: string;
|
|
10
|
+
/**
|
|
11
|
+
* Density of the trigger — `"md"` (default) is the current render. `"sm"`
|
|
12
|
+
* de-emphasizes the trigger (smaller font/weight/padding) for NESTED /
|
|
13
|
+
* level-2 collapsibles; `"lg"` enlarges it. Additive: with `size` unset the
|
|
14
|
+
* trigger renders byte-identically to before.
|
|
15
|
+
*/
|
|
16
|
+
size?: "sm" | "md" | "lg";
|
|
10
17
|
disabled?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Trailing content rendered inside the trigger, BETWEEN the title and the
|
|
20
|
+
* chevron (e.g. a count Badge, a status Tag, a glyph). The chevron stays the
|
|
21
|
+
* rightmost affordance. If the trailing content carries information SR users
|
|
22
|
+
* need as part of the trigger name, set `aria-label` on the Collapsible via
|
|
23
|
+
* `...rest` (e.g. `aria-label="Entities, 128 items"`).
|
|
24
|
+
*/
|
|
25
|
+
trailing?: Snippet;
|
|
11
26
|
onToggle?: (open: boolean) => void;
|
|
12
27
|
class?: string;
|
|
13
28
|
children?: Snippet;
|
|
@@ -16,7 +31,9 @@
|
|
|
16
31
|
let {
|
|
17
32
|
open = $bindable(false),
|
|
18
33
|
title,
|
|
34
|
+
size = "md",
|
|
19
35
|
disabled = false,
|
|
36
|
+
trailing,
|
|
20
37
|
onToggle,
|
|
21
38
|
class: className,
|
|
22
39
|
children,
|
|
@@ -26,7 +43,14 @@
|
|
|
26
43
|
const uid = `st-collapsible-${Math.random().toString(36).slice(2, 9)}`;
|
|
27
44
|
|
|
28
45
|
const classes = $derived(
|
|
29
|
-
[
|
|
46
|
+
[
|
|
47
|
+
"st-collapsible",
|
|
48
|
+
`st-collapsible--${size}`,
|
|
49
|
+
open ? "st-collapsible--open" : null,
|
|
50
|
+
className
|
|
51
|
+
]
|
|
52
|
+
.filter(Boolean)
|
|
53
|
+
.join(" ")
|
|
30
54
|
);
|
|
31
55
|
|
|
32
56
|
function toggle() {
|
|
@@ -47,6 +71,9 @@
|
|
|
47
71
|
onclick={toggle}
|
|
48
72
|
>
|
|
49
73
|
<span class="st-collapsible__title">{title}</span>
|
|
74
|
+
{#if trailing}
|
|
75
|
+
<span class="st-collapsible__trailing">{@render trailing()}</span>
|
|
76
|
+
{/if}
|
|
50
77
|
<span class="st-collapsible__icon" aria-hidden="true">
|
|
51
78
|
<ChevronDown size={18} strokeWidth={2.25} />
|
|
52
79
|
</span>
|
|
@@ -103,10 +130,37 @@
|
|
|
103
130
|
cursor: not-allowed;
|
|
104
131
|
}
|
|
105
132
|
|
|
133
|
+
/* Density variants (additive). `md` is the UNTOUCHED base `.st-collapsible__trigger`
|
|
134
|
+
above — no `--md` rule exists, so a `size="md"` (or unset) trigger renders
|
|
135
|
+
byte-identically. `--sm` de-emphasizes for nesting; `--lg` enlarges. Every
|
|
136
|
+
leaf falls back to a base literal so a theme that emits no
|
|
137
|
+
`--st-component-collapsible-*` renders these variants identically. */
|
|
138
|
+
.st-collapsible--sm .st-collapsible__trigger {
|
|
139
|
+
font-size: var(--st-component-collapsible-sm-fontSize, 0.875rem);
|
|
140
|
+
font-weight: var(--st-component-collapsible-sm-fontWeight, 500);
|
|
141
|
+
padding: var(--st-component-collapsible-sm-paddingBlock, 0.4rem)
|
|
142
|
+
var(--st-component-collapsible-sm-paddingInline, 0.25rem);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.st-collapsible--lg .st-collapsible__trigger {
|
|
146
|
+
font-size: var(--st-component-collapsible-lg-fontSize, 1rem);
|
|
147
|
+
padding: var(--st-component-collapsible-lg-paddingBlock, 0.875rem)
|
|
148
|
+
var(--st-component-collapsible-lg-paddingInline, 0.25rem);
|
|
149
|
+
}
|
|
150
|
+
|
|
106
151
|
.st-collapsible__title {
|
|
107
152
|
flex: 1 1 auto;
|
|
108
153
|
}
|
|
109
154
|
|
|
155
|
+
/* Trigger trailing slot (additive). Holds a count badge / status / glyph
|
|
156
|
+
between the title and the chevron; never grows, so the chevron stays the
|
|
157
|
+
rightmost affordance and the title keeps `flex: 1 1 auto`. */
|
|
158
|
+
.st-collapsible__trailing {
|
|
159
|
+
align-items: center;
|
|
160
|
+
display: inline-flex;
|
|
161
|
+
flex: 0 0 auto;
|
|
162
|
+
}
|
|
163
|
+
|
|
110
164
|
.st-collapsible__icon {
|
|
111
165
|
align-items: center;
|
|
112
166
|
color: var(--st-semantic-text-secondary);
|
|
@@ -4,7 +4,22 @@ type CollapsibleProps = Omit<HTMLAttributes<HTMLDivElement>, "class" | "title">
|
|
|
4
4
|
/** État ouvert (bindable). */
|
|
5
5
|
open?: boolean;
|
|
6
6
|
title: string;
|
|
7
|
+
/**
|
|
8
|
+
* Density of the trigger — `"md"` (default) is the current render. `"sm"`
|
|
9
|
+
* de-emphasizes the trigger (smaller font/weight/padding) for NESTED /
|
|
10
|
+
* level-2 collapsibles; `"lg"` enlarges it. Additive: with `size` unset the
|
|
11
|
+
* trigger renders byte-identically to before.
|
|
12
|
+
*/
|
|
13
|
+
size?: "sm" | "md" | "lg";
|
|
7
14
|
disabled?: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Trailing content rendered inside the trigger, BETWEEN the title and the
|
|
17
|
+
* chevron (e.g. a count Badge, a status Tag, a glyph). The chevron stays the
|
|
18
|
+
* rightmost affordance. If the trailing content carries information SR users
|
|
19
|
+
* need as part of the trigger name, set `aria-label` on the Collapsible via
|
|
20
|
+
* `...rest` (e.g. `aria-label="Entities, 128 items"`).
|
|
21
|
+
*/
|
|
22
|
+
trailing?: Snippet;
|
|
8
23
|
onToggle?: (open: boolean) => void;
|
|
9
24
|
class?: string;
|
|
10
25
|
children?: Snippet;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Collapsible.svelte.d.ts","sourceRoot":"","sources":["../src/lib/Collapsible.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGpD,KAAK,gBAAgB,GAAG,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,GAAG;IAChF,8BAA8B;IAC9B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;
|
|
1
|
+
{"version":3,"file":"Collapsible.svelte.d.ts","sourceRoot":"","sources":["../src/lib/Collapsible.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGpD,KAAK,gBAAgB,GAAG,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,GAAG;IAChF,8BAA8B;IAC9B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AA4DJ,QAAA,MAAM,WAAW,0DAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Collapsible.test.d.ts","sourceRoot":"","sources":["../src/lib/Collapsible.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { fireEvent, render } from "@testing-library/svelte";
|
|
2
|
+
import { createRawSnippet } from "svelte";
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
import Collapsible from "./Collapsible.svelte";
|
|
5
|
+
const snippet = (html) => createRawSnippet(() => ({ render: () => `<span>${html}</span>` }));
|
|
6
|
+
// Svelte scopes component styles by appending a per-component hash class (e.g.
|
|
7
|
+
// "svelte-pav3x3") to every styled element's class list. Compare against the
|
|
8
|
+
// SEMANTIC class only (the first token) so the assertions are stable across
|
|
9
|
+
// builds and read the structural class, not the scope hash.
|
|
10
|
+
const structuralClass = (el) => el.className.split(/\s+/)[0];
|
|
11
|
+
describe("Collapsible — base (byte-identity)", () => {
|
|
12
|
+
it("renders the default trigger with no trailing span and the md density class", () => {
|
|
13
|
+
const { container } = render(Collapsible, { props: { title: "Entities" } });
|
|
14
|
+
const root = container.querySelector(".st-collapsible");
|
|
15
|
+
expect(root).toBeTruthy();
|
|
16
|
+
// Default density is md (additive class), and there is NO trailing span when
|
|
17
|
+
// the slot is absent — the trigger keeps its original [title][chevron] shape.
|
|
18
|
+
expect(root.classList.contains("st-collapsible--md")).toBe(true);
|
|
19
|
+
expect(root.classList.contains("st-collapsible--sm")).toBe(false);
|
|
20
|
+
expect(container.querySelector(".st-collapsible__trailing")).toBeNull();
|
|
21
|
+
expect(container.querySelector(".st-collapsible__title")?.textContent).toBe("Entities");
|
|
22
|
+
// Trigger order is preserved: only the title span then the chevron.
|
|
23
|
+
const trigger = container.querySelector(".st-collapsible__trigger");
|
|
24
|
+
const spans = Array.from(trigger.children).map(structuralClass);
|
|
25
|
+
expect(spans).toEqual(["st-collapsible__title", "st-collapsible__icon"]);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
describe("Collapsible — size", () => {
|
|
29
|
+
it("size=\"sm\" toggles st-collapsible--sm", () => {
|
|
30
|
+
const { container } = render(Collapsible, { props: { title: "Type", size: "sm" } });
|
|
31
|
+
const root = container.querySelector(".st-collapsible");
|
|
32
|
+
expect(root.classList.contains("st-collapsible--sm")).toBe(true);
|
|
33
|
+
expect(root.classList.contains("st-collapsible--md")).toBe(false);
|
|
34
|
+
});
|
|
35
|
+
it("size=\"lg\" toggles st-collapsible--lg", () => {
|
|
36
|
+
const { container } = render(Collapsible, { props: { title: "Section", size: "lg" } });
|
|
37
|
+
expect(container.querySelector(".st-collapsible").classList.contains("st-collapsible--lg")).toBe(true);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
describe("Collapsible — trailing slot", () => {
|
|
41
|
+
it("renders trailing content between the title and the chevron", () => {
|
|
42
|
+
const { container } = render(Collapsible, {
|
|
43
|
+
props: { title: "Entities", trailing: snippet("128") },
|
|
44
|
+
});
|
|
45
|
+
const trailing = container.querySelector(".st-collapsible__trailing");
|
|
46
|
+
expect(trailing).toBeTruthy();
|
|
47
|
+
expect(trailing?.textContent).toContain("128");
|
|
48
|
+
// Order inside the trigger: title, trailing, chevron (chevron stays last).
|
|
49
|
+
const trigger = container.querySelector(".st-collapsible__trigger");
|
|
50
|
+
const order = Array.from(trigger.children).map(structuralClass);
|
|
51
|
+
expect(order).toEqual([
|
|
52
|
+
"st-collapsible__title",
|
|
53
|
+
"st-collapsible__trailing",
|
|
54
|
+
"st-collapsible__icon",
|
|
55
|
+
]);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
describe("Collapsible — a11y unchanged", () => {
|
|
59
|
+
it("toggles aria-expanded on click with size + trailing set", async () => {
|
|
60
|
+
const { container } = render(Collapsible, {
|
|
61
|
+
props: { title: "Entities", size: "sm", trailing: snippet("7") },
|
|
62
|
+
});
|
|
63
|
+
const trigger = container.querySelector(".st-collapsible__trigger");
|
|
64
|
+
expect(trigger.getAttribute("aria-expanded")).toBe("false");
|
|
65
|
+
await fireEvent.click(trigger);
|
|
66
|
+
expect(trigger.getAttribute("aria-expanded")).toBe("true");
|
|
67
|
+
});
|
|
68
|
+
});
|
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"}
|