@sentropic/design-system-svelte 0.34.0 → 0.34.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AppHeader.svelte +159 -1
- package/dist/AppHeader.svelte.d.ts +18 -1
- package/dist/AppHeader.svelte.d.ts.map +1 -1
- package/dist/ArcDiagramChart.svelte +380 -0
- package/dist/ArcDiagramChart.svelte.d.ts +43 -0
- package/dist/ArcDiagramChart.svelte.d.ts.map +1 -0
- package/dist/AreaRangeChart.svelte +487 -0
- package/dist/AreaRangeChart.svelte.d.ts +38 -0
- package/dist/AreaRangeChart.svelte.d.ts.map +1 -0
- package/dist/AreaSplineRangeChart.svelte +478 -0
- package/dist/AreaSplineRangeChart.svelte.d.ts +37 -0
- package/dist/AreaSplineRangeChart.svelte.d.ts.map +1 -0
- package/dist/BellCurveChart.svelte +487 -0
- package/dist/BellCurveChart.svelte.d.ts +40 -0
- package/dist/BellCurveChart.svelte.d.ts.map +1 -0
- package/dist/Calendar.svelte +11 -0
- package/dist/ChatThread.svelte +32 -1
- package/dist/ChatThread.svelte.d.ts +14 -0
- package/dist/ChatThread.svelte.d.ts.map +1 -1
- package/dist/ColumnPyramidChart.svelte +332 -0
- package/dist/ColumnPyramidChart.svelte.d.ts +35 -0
- package/dist/ColumnPyramidChart.svelte.d.ts.map +1 -0
- package/dist/ColumnRangeChart.svelte +432 -0
- package/dist/ColumnRangeChart.svelte.d.ts +42 -0
- package/dist/ColumnRangeChart.svelte.d.ts.map +1 -0
- package/dist/Combobox.svelte +3 -0
- package/dist/ContentSwitcher.svelte +1 -1
- package/dist/DataTable.svelte.d.ts +1 -1
- package/dist/DatePicker.svelte +3 -0
- package/dist/DependencyWheelChart.svelte +413 -0
- package/dist/DependencyWheelChart.svelte.d.ts +42 -0
- package/dist/DependencyWheelChart.svelte.d.ts.map +1 -0
- package/dist/DumbbellChart.svelte +403 -0
- package/dist/DumbbellChart.svelte.d.ts +44 -0
- package/dist/DumbbellChart.svelte.d.ts.map +1 -0
- package/dist/ErrorBarChart.svelte +428 -0
- package/dist/ErrorBarChart.svelte.d.ts +40 -0
- package/dist/ErrorBarChart.svelte.d.ts.map +1 -0
- package/dist/GanttChart.svelte +410 -0
- package/dist/GanttChart.svelte.d.ts +39 -0
- package/dist/GanttChart.svelte.d.ts.map +1 -0
- package/dist/HLCChart.svelte +330 -0
- package/dist/HLCChart.svelte.d.ts +32 -0
- package/dist/HLCChart.svelte.d.ts.map +1 -0
- package/dist/HeikinAshiChart.svelte +365 -0
- package/dist/HeikinAshiChart.svelte.d.ts +37 -0
- package/dist/HeikinAshiChart.svelte.d.ts.map +1 -0
- package/dist/HollowCandlestickChart.svelte +357 -0
- package/dist/HollowCandlestickChart.svelte.d.ts +34 -0
- package/dist/HollowCandlestickChart.svelte.d.ts.map +1 -0
- package/dist/Input.svelte +3 -0
- package/dist/ItemChart.svelte +389 -0
- package/dist/ItemChart.svelte.d.ts +67 -0
- package/dist/ItemChart.svelte.d.ts.map +1 -0
- package/dist/LollipopChart.svelte +1 -1
- package/dist/MultiSelect.svelte +3 -0
- package/dist/NumberInput.svelte +3 -0
- package/dist/OHLCChart.svelte +343 -0
- package/dist/OHLCChart.svelte.d.ts +33 -0
- package/dist/OHLCChart.svelte.d.ts.map +1 -0
- package/dist/OrganizationChart.svelte +284 -0
- package/dist/OrganizationChart.svelte.d.ts +19 -0
- package/dist/OrganizationChart.svelte.d.ts.map +1 -0
- package/dist/PasswordInput.svelte +3 -0
- package/dist/PolygonChart.svelte +189 -0
- package/dist/PolygonChart.svelte.d.ts +17 -0
- package/dist/PolygonChart.svelte.d.ts.map +1 -0
- package/dist/Search.svelte +7 -5
- package/dist/Select.svelte +3 -0
- package/dist/StreamgraphChart.svelte +283 -0
- package/dist/StreamgraphChart.svelte.d.ts +23 -0
- package/dist/StreamgraphChart.svelte.d.ts.map +1 -0
- package/dist/StreamingMessage.svelte +44 -2
- package/dist/StreamingMessage.svelte.d.ts +18 -1
- package/dist/StreamingMessage.svelte.d.ts.map +1 -1
- package/dist/TileMapChart.svelte +314 -0
- package/dist/TileMapChart.svelte.d.ts +45 -0
- package/dist/TileMapChart.svelte.d.ts.map +1 -0
- package/dist/TimePicker.svelte +3 -0
- package/dist/TimelineChart.svelte +362 -0
- package/dist/TimelineChart.svelte.d.ts +22 -0
- package/dist/TimelineChart.svelte.d.ts.map +1 -0
- package/dist/TreegraphChart.svelte +281 -0
- package/dist/TreegraphChart.svelte.d.ts +19 -0
- package/dist/TreegraphChart.svelte.d.ts.map +1 -0
- package/dist/VariablePieChart.svelte +313 -0
- package/dist/VariablePieChart.svelte.d.ts +52 -0
- package/dist/VariablePieChart.svelte.d.ts.map +1 -0
- package/dist/VennChart.svelte +348 -0
- package/dist/VennChart.svelte.d.ts +72 -0
- package/dist/VennChart.svelte.d.ts.map +1 -0
- package/dist/WordCloudChart.svelte +279 -0
- package/dist/WordCloudChart.svelte.d.ts +18 -0
- package/dist/WordCloudChart.svelte.d.ts.map +1 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -0
- package/package.json +5 -3
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
export type PolygonChartTone =
|
|
3
|
+
| "category1" | "category2" | "category3" | "category4"
|
|
4
|
+
| "category5" | "category6" | "category7" | "category8";
|
|
5
|
+
|
|
6
|
+
export type PolygonChartPoint = {
|
|
7
|
+
x: number;
|
|
8
|
+
y: number;
|
|
9
|
+
};
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<script lang="ts">
|
|
13
|
+
import ChartDataList from "./ChartDataList.svelte";
|
|
14
|
+
|
|
15
|
+
type PolygonChartProps = {
|
|
16
|
+
data: PolygonChartPoint[];
|
|
17
|
+
label: string;
|
|
18
|
+
tone?: PolygonChartTone;
|
|
19
|
+
width?: number;
|
|
20
|
+
height?: number;
|
|
21
|
+
class?: string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
let {
|
|
25
|
+
data,
|
|
26
|
+
label,
|
|
27
|
+
tone = "category1",
|
|
28
|
+
width = 480,
|
|
29
|
+
height = 360,
|
|
30
|
+
class: className
|
|
31
|
+
}: PolygonChartProps = $props();
|
|
32
|
+
|
|
33
|
+
const MARGIN = { top: 14, right: 18, bottom: 36, left: 48 };
|
|
34
|
+
|
|
35
|
+
function niceTicks(min: number, max: number, target = 5): number[] {
|
|
36
|
+
if (!Number.isFinite(min) || !Number.isFinite(max) || min === max) {
|
|
37
|
+
return [Number.isFinite(max) ? max : 0];
|
|
38
|
+
}
|
|
39
|
+
const range = max - min;
|
|
40
|
+
const rough = range / Math.max(target - 1, 1);
|
|
41
|
+
const pow = Math.pow(10, Math.floor(Math.log10(rough)));
|
|
42
|
+
const norm = rough / pow;
|
|
43
|
+
let step: number;
|
|
44
|
+
if (norm < 1.5) step = pow;
|
|
45
|
+
else if (norm < 3) step = 2 * pow;
|
|
46
|
+
else if (norm < 7) step = 5 * pow;
|
|
47
|
+
else step = 10 * pow;
|
|
48
|
+
const start = Math.floor(min / step) * step;
|
|
49
|
+
const end = Math.ceil(max / step) * step;
|
|
50
|
+
const ticks: number[] = [];
|
|
51
|
+
for (let v = start; v <= end + step / 2; v += step) ticks.push(Number(v.toFixed(10)));
|
|
52
|
+
return ticks;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function scaleLinear(v: number, d0: number, d1: number, r0: number, r1: number) {
|
|
56
|
+
if (d1 === d0) return r0;
|
|
57
|
+
return r0 + ((v - d0) * (r1 - r0)) / (d1 - d0);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function fmt(v: number): string {
|
|
61
|
+
if (Math.abs(v) >= 1000) return `${(v / 1000).toFixed(v % 1000 === 0 ? 0 : 1)}k`;
|
|
62
|
+
return Number.isInteger(v) ? String(v) : v.toFixed(1);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let hoveredIndex: number | null = $state(null);
|
|
66
|
+
|
|
67
|
+
// Non-finite coordinates are dropped before anything else.
|
|
68
|
+
const validData = $derived(
|
|
69
|
+
(data ?? []).filter((d) => Number.isFinite(d.x) && Number.isFinite(d.y))
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
const scales = $derived.by(() => {
|
|
73
|
+
const xs = validData.map((d) => d.x);
|
|
74
|
+
const ys = validData.map((d) => d.y);
|
|
75
|
+
const xTicks = niceTicks(Math.min(...xs), Math.max(...xs));
|
|
76
|
+
const yTicks = niceTicks(Math.min(...ys), Math.max(...ys));
|
|
77
|
+
const plotW = Math.max(width - MARGIN.left - MARGIN.right, 1);
|
|
78
|
+
const plotH = Math.max(height - MARGIN.top - MARGIN.bottom, 1);
|
|
79
|
+
return {
|
|
80
|
+
xTicks, yTicks,
|
|
81
|
+
xMin: xTicks[0], xMax: xTicks[xTicks.length - 1],
|
|
82
|
+
yMin: yTicks[0], yMax: yTicks[yTicks.length - 1],
|
|
83
|
+
plotW, plotH
|
|
84
|
+
};
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const points = $derived.by(() => {
|
|
88
|
+
const { xMin, xMax, yMin, yMax, plotW, plotH } = scales;
|
|
89
|
+
return validData.map((d) => ({
|
|
90
|
+
cx: MARGIN.left + scaleLinear(d.x, xMin, xMax, 0, plotW),
|
|
91
|
+
cy: MARGIN.top + scaleLinear(d.y, yMin, yMax, plotH, 0),
|
|
92
|
+
datum: d
|
|
93
|
+
}));
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const polygonPoints = $derived(points.map((p) => `${p.cx},${p.cy}`).join(" "));
|
|
97
|
+
|
|
98
|
+
const dataValueItems = $derived(validData.map((d) => `x ${d.x}, y ${d.y}`));
|
|
99
|
+
|
|
100
|
+
function handleVisualPointerMove(event: PointerEvent) {
|
|
101
|
+
const target = event.target;
|
|
102
|
+
if (!(target instanceof Element)) {
|
|
103
|
+
hoveredIndex = null;
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const index = Number(target.getAttribute("data-chart-index"));
|
|
107
|
+
hoveredIndex = Number.isInteger(index) ? index : null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const classes = () => ["st-polygonChart", className].filter(Boolean).join(" ");
|
|
111
|
+
</script>
|
|
112
|
+
|
|
113
|
+
<div class={classes()}>
|
|
114
|
+
<div
|
|
115
|
+
class="st-polygonChart__visual"
|
|
116
|
+
role="img"
|
|
117
|
+
aria-label={label}
|
|
118
|
+
onpointermove={handleVisualPointerMove}
|
|
119
|
+
onpointerleave={() => (hoveredIndex = null)}
|
|
120
|
+
>
|
|
121
|
+
<svg viewBox="0 0 {width} {height}" preserveAspectRatio="xMidYMid meet" width="100%" height="100%" focusable="false" aria-hidden="true">
|
|
122
|
+
<!-- gridlines + ticks Y -->
|
|
123
|
+
{#each scales.yTicks as t (t)}
|
|
124
|
+
{@const y = MARGIN.top + scaleLinear(t, scales.yMin, scales.yMax, scales.plotH, 0)}
|
|
125
|
+
<line class="st-polygonChart__grid" x1={MARGIN.left} x2={width - MARGIN.right} y1={y} y2={y} />
|
|
126
|
+
<text class="st-polygonChart__tick" x={MARGIN.left - 6} y={y} text-anchor="end" dominant-baseline="middle">{fmt(t)}</text>
|
|
127
|
+
{/each}
|
|
128
|
+
<!-- ticks X -->
|
|
129
|
+
{#each scales.xTicks as t (t)}
|
|
130
|
+
{@const x = MARGIN.left + scaleLinear(t, scales.xMin, scales.xMax, 0, scales.plotW)}
|
|
131
|
+
<text class="st-polygonChart__tick" x={x} y={height - MARGIN.bottom + 16} text-anchor="middle">{fmt(t)}</text>
|
|
132
|
+
{/each}
|
|
133
|
+
|
|
134
|
+
<!-- axes -->
|
|
135
|
+
<line class="st-polygonChart__axis" x1={MARGIN.left} x2={MARGIN.left} y1={MARGIN.top} y2={height - MARGIN.bottom} />
|
|
136
|
+
<line class="st-polygonChart__axis" x1={MARGIN.left} x2={width - MARGIN.right} y1={height - MARGIN.bottom} y2={height - MARGIN.bottom} />
|
|
137
|
+
|
|
138
|
+
<!-- closed filled polygon connecting the points in order -->
|
|
139
|
+
{#if points.length >= 2}
|
|
140
|
+
<polygon class="st-polygonChart__polygon st-polygonChart__polygon--{tone}" points={polygonPoints} />
|
|
141
|
+
{/if}
|
|
142
|
+
|
|
143
|
+
<!-- vertex markers -->
|
|
144
|
+
{#each points as p, i (i)}
|
|
145
|
+
<circle
|
|
146
|
+
class="st-polygonChart__vertex st-polygonChart__vertex--{tone}"
|
|
147
|
+
cx={p.cx}
|
|
148
|
+
cy={p.cy}
|
|
149
|
+
r="3.5"
|
|
150
|
+
data-chart-index={i}
|
|
151
|
+
/>
|
|
152
|
+
{/each}
|
|
153
|
+
</svg>
|
|
154
|
+
</div>
|
|
155
|
+
|
|
156
|
+
<ChartDataList {label} items={dataValueItems} />
|
|
157
|
+
|
|
158
|
+
{#if hoveredIndex !== null && points[hoveredIndex]}
|
|
159
|
+
{@const p = points[hoveredIndex]}
|
|
160
|
+
<div class="st-polygonChart__tooltip" role="presentation" style="left: {(p.cx / width) * 100}%; top: {(p.cy / height) * 100}%">
|
|
161
|
+
<span class="st-polygonChart__tooltipValue">x {p.datum.x} · y {p.datum.y}</span>
|
|
162
|
+
</div>
|
|
163
|
+
{/if}
|
|
164
|
+
</div>
|
|
165
|
+
|
|
166
|
+
<style>
|
|
167
|
+
.st-polygonChart { color: var(--st-semantic-text-secondary); display: block; font-family: inherit; position: relative; width: 100%; }
|
|
168
|
+
.st-polygonChart svg, .st-polygonChart__visual { display: block; overflow: visible; }
|
|
169
|
+
.st-polygonChart__grid { stroke: var(--st-semantic-border-subtle); stroke-dasharray: 2 3; stroke-width: 1; opacity: 0.7; }
|
|
170
|
+
.st-polygonChart__axis { stroke: var(--st-semantic-border-subtle); stroke-width: 1; }
|
|
171
|
+
.st-polygonChart__tick { fill: var(--st-semantic-text-secondary); font-size: 0.6875rem; }
|
|
172
|
+
.st-polygonChart__polygon { fill: currentColor; fill-opacity: 0.18; stroke: currentColor; stroke-width: 2; stroke-linejoin: round; }
|
|
173
|
+
.st-polygonChart__vertex { fill: currentColor; }
|
|
174
|
+
.st-polygonChart__polygon--category1, .st-polygonChart__vertex--category1 { color: var(--st-semantic-data-category1); }
|
|
175
|
+
.st-polygonChart__polygon--category2, .st-polygonChart__vertex--category2 { color: var(--st-semantic-data-category2); }
|
|
176
|
+
.st-polygonChart__polygon--category3, .st-polygonChart__vertex--category3 { color: var(--st-semantic-data-category3); }
|
|
177
|
+
.st-polygonChart__polygon--category4, .st-polygonChart__vertex--category4 { color: var(--st-semantic-data-category4); }
|
|
178
|
+
.st-polygonChart__polygon--category5, .st-polygonChart__vertex--category5 { color: var(--st-semantic-data-category5); }
|
|
179
|
+
.st-polygonChart__polygon--category6, .st-polygonChart__vertex--category6 { color: var(--st-semantic-data-category6); }
|
|
180
|
+
.st-polygonChart__polygon--category7, .st-polygonChart__vertex--category7 { color: var(--st-semantic-data-category7); }
|
|
181
|
+
.st-polygonChart__polygon--category8, .st-polygonChart__vertex--category8 { color: var(--st-semantic-data-category8); }
|
|
182
|
+
.st-polygonChart__tooltip {
|
|
183
|
+
background: var(--st-semantic-surface-inverse); border-radius: var(--st-radius-sm, 0.25rem);
|
|
184
|
+
color: var(--st-semantic-text-inverse); display: inline-flex; flex-direction: column;
|
|
185
|
+
font-size: 0.75rem; gap: 0.125rem; line-height: 1.2; padding: 0.375rem 0.5rem;
|
|
186
|
+
pointer-events: none; position: absolute; transform: translate(-50%, calc(-100% - 8px)); white-space: nowrap; z-index: 1;
|
|
187
|
+
}
|
|
188
|
+
.st-polygonChart__tooltipValue { opacity: 0.85; }
|
|
189
|
+
</style>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type PolygonChartTone = "category1" | "category2" | "category3" | "category4" | "category5" | "category6" | "category7" | "category8";
|
|
2
|
+
export type PolygonChartPoint = {
|
|
3
|
+
x: number;
|
|
4
|
+
y: number;
|
|
5
|
+
};
|
|
6
|
+
type PolygonChartProps = {
|
|
7
|
+
data: PolygonChartPoint[];
|
|
8
|
+
label: string;
|
|
9
|
+
tone?: PolygonChartTone;
|
|
10
|
+
width?: number;
|
|
11
|
+
height?: number;
|
|
12
|
+
class?: string;
|
|
13
|
+
};
|
|
14
|
+
declare const PolygonChart: import("svelte").Component<PolygonChartProps, {}, "">;
|
|
15
|
+
type PolygonChart = ReturnType<typeof PolygonChart>;
|
|
16
|
+
export default PolygonChart;
|
|
17
|
+
//# sourceMappingURL=PolygonChart.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PolygonChart.svelte.d.ts","sourceRoot":"","sources":["../src/lib/PolygonChart.svelte.ts"],"names":[],"mappings":"AAGE,MAAM,MAAM,gBAAgB,GACxB,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,GACrD,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,CAAC;AAE1D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX,CAAC;AAMF,KAAK,iBAAiB,GAAG;IACvB,IAAI,EAAE,iBAAiB,EAAE,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AA6IJ,QAAA,MAAM,YAAY,uDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
|
package/dist/Search.svelte
CHANGED
|
@@ -143,14 +143,17 @@
|
|
|
143
143
|
|
|
144
144
|
.st-search--sm {
|
|
145
145
|
min-height: var(--st-component-control-smHeight, 2rem);
|
|
146
|
+
font-size: 0.8125rem;
|
|
146
147
|
}
|
|
147
148
|
|
|
148
149
|
.st-search--md {
|
|
149
150
|
min-height: var(--st-component-control-mdHeight, 2.5rem);
|
|
151
|
+
font-size: 0.875rem;
|
|
150
152
|
}
|
|
151
153
|
|
|
152
154
|
.st-search--lg {
|
|
153
155
|
min-height: var(--st-component-control-lgHeight, 3rem);
|
|
156
|
+
font-size: 1rem;
|
|
154
157
|
}
|
|
155
158
|
|
|
156
159
|
.st-search:hover:not(:has(input:disabled)) {
|
|
@@ -210,18 +213,17 @@
|
|
|
210
213
|
align-items: center;
|
|
211
214
|
background: transparent;
|
|
212
215
|
border: 0;
|
|
213
|
-
border-radius:
|
|
214
|
-
color:
|
|
216
|
+
border-radius: var(--st-component-control-anatomy-shape-radius, 0.375rem);
|
|
217
|
+
color: var(--st-semantic-text-secondary);
|
|
215
218
|
cursor: pointer;
|
|
216
219
|
display: inline-flex;
|
|
217
220
|
flex: 0 0 auto;
|
|
218
221
|
font: inherit;
|
|
219
|
-
font-size: 1.125rem;
|
|
220
222
|
height: 1.5rem;
|
|
221
223
|
justify-content: center;
|
|
222
224
|
line-height: 1;
|
|
223
|
-
margin-inline-end: 0.
|
|
224
|
-
padding: 0;
|
|
225
|
+
margin-inline-end: 0.25rem;
|
|
226
|
+
padding: 0 0.25rem;
|
|
225
227
|
transition: background-color var(--st-motion-fast, 120ms) var(--st-motion-easing, ease);
|
|
226
228
|
width: 1.5rem;
|
|
227
229
|
}
|
package/dist/Select.svelte
CHANGED
|
@@ -144,14 +144,17 @@
|
|
|
144
144
|
|
|
145
145
|
.st-select--sm {
|
|
146
146
|
min-height: var(--st-component-control-smHeight, 2rem);
|
|
147
|
+
font-size: 0.8125rem;
|
|
147
148
|
}
|
|
148
149
|
|
|
149
150
|
.st-select--md {
|
|
150
151
|
min-height: var(--st-component-control-mdHeight, 2.5rem);
|
|
152
|
+
font-size: 0.875rem;
|
|
151
153
|
}
|
|
152
154
|
|
|
153
155
|
.st-select--lg {
|
|
154
156
|
min-height: var(--st-component-control-lgHeight, 3rem);
|
|
157
|
+
font-size: 1rem;
|
|
155
158
|
}
|
|
156
159
|
|
|
157
160
|
.st-select:focus-visible {
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
export type StreamgraphChartTone =
|
|
3
|
+
| "category1" | "category2" | "category3" | "category4"
|
|
4
|
+
| "category5" | "category6" | "category7" | "category8";
|
|
5
|
+
|
|
6
|
+
// Une série pour un point d'abscisse : un libellé + sa valeur.
|
|
7
|
+
export type StreamgraphChartSeriesValue = {
|
|
8
|
+
label: string;
|
|
9
|
+
value: number;
|
|
10
|
+
tone?: StreamgraphChartTone;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// Un point sur l'axe X (catégorie / temps) avec ses séries empilées.
|
|
14
|
+
export type StreamgraphChartDatum = {
|
|
15
|
+
category: string;
|
|
16
|
+
values: StreamgraphChartSeriesValue[];
|
|
17
|
+
};
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<script lang="ts">
|
|
21
|
+
import ChartDataList from "./ChartDataList.svelte";
|
|
22
|
+
|
|
23
|
+
type StreamgraphChartProps = {
|
|
24
|
+
data: StreamgraphChartDatum[];
|
|
25
|
+
width?: number;
|
|
26
|
+
height?: number;
|
|
27
|
+
label: string;
|
|
28
|
+
smooth?: boolean;
|
|
29
|
+
showLegend?: boolean;
|
|
30
|
+
class?: string;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
let {
|
|
34
|
+
data = [],
|
|
35
|
+
width = 480,
|
|
36
|
+
height = 240,
|
|
37
|
+
label,
|
|
38
|
+
smooth = true,
|
|
39
|
+
showLegend = true,
|
|
40
|
+
class: className
|
|
41
|
+
}: StreamgraphChartProps = $props();
|
|
42
|
+
|
|
43
|
+
const MARGIN = { top: 12, right: 16, bottom: 32, left: 16 };
|
|
44
|
+
const TONES = ["category1","category2","category3","category4","category5","category6","category7","category8"] as const;
|
|
45
|
+
|
|
46
|
+
const scaleLinear = (v: number, d0: number, d1: number, r0: number, r1: number) =>
|
|
47
|
+
d1 === d0 ? r0 : r0 + ((v - d0) * (r1 - r0)) / (d1 - d0);
|
|
48
|
+
|
|
49
|
+
function buildLinearPath(pts: { x: number; y: number }[]): string {
|
|
50
|
+
return pts.map((p, i) => `${i === 0 ? "M" : "L"}${p.x.toFixed(2)},${p.y.toFixed(2)}`).join(" ");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function buildSmoothPath(pts: { x: number; y: number }[]): string {
|
|
54
|
+
if (pts.length < 2) return buildLinearPath(pts);
|
|
55
|
+
const t = 0.18;
|
|
56
|
+
let d = `M${pts[0].x.toFixed(2)},${pts[0].y.toFixed(2)}`;
|
|
57
|
+
for (let i = 0; i < pts.length - 1; i++) {
|
|
58
|
+
const p0 = pts[i - 1] ?? pts[i];
|
|
59
|
+
const p1 = pts[i];
|
|
60
|
+
const p2 = pts[i + 1];
|
|
61
|
+
const p3 = pts[i + 2] ?? p2;
|
|
62
|
+
const c1x = p1.x + (p2.x - p0.x) * t;
|
|
63
|
+
const c1y = p1.y + (p2.y - p0.y) * t;
|
|
64
|
+
const c2x = p2.x - (p3.x - p1.x) * t;
|
|
65
|
+
const c2y = p2.y - (p3.y - p1.y) * t;
|
|
66
|
+
d += ` C${c1x.toFixed(2)},${c1y.toFixed(2)} ${c2x.toFixed(2)},${c2y.toFixed(2)} ${p2.x.toFixed(2)},${p2.y.toFixed(2)}`;
|
|
67
|
+
}
|
|
68
|
+
return d;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Valeur non-finie ou négative → 0 (le streamgraph empile des grandeurs ≥ 0).
|
|
72
|
+
const safeValue = (v: number) => (Number.isFinite(v) && v > 0 ? v : 0);
|
|
73
|
+
|
|
74
|
+
const plotWidth = $derived(Math.max(width - MARGIN.left - MARGIN.right, 1));
|
|
75
|
+
const plotHeight = $derived(Math.max(height - MARGIN.top - MARGIN.bottom, 1));
|
|
76
|
+
|
|
77
|
+
// Ordre stable des séries (1re apparition) + ton associé.
|
|
78
|
+
const series = $derived.by(() => {
|
|
79
|
+
const seen = new Map<string, StreamgraphChartTone>();
|
|
80
|
+
data.forEach((d) =>
|
|
81
|
+
d.values.forEach((sv, i) => {
|
|
82
|
+
if (!seen.has(sv.label)) seen.set(sv.label, sv.tone ?? TONES[seen.size % TONES.length]);
|
|
83
|
+
})
|
|
84
|
+
);
|
|
85
|
+
return [...seen.entries()].map(([seriesLabel, tone]) => ({ seriesLabel, tone }));
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Échelle Y symétrique : demi-amplitude = max des sommes empilées / 2.
|
|
89
|
+
const halfMax = $derived.by(() => {
|
|
90
|
+
let max = 0;
|
|
91
|
+
for (const d of data) {
|
|
92
|
+
let sum = 0;
|
|
93
|
+
for (const sv of d.values) sum += safeValue(sv.value);
|
|
94
|
+
if (sum > max) max = sum;
|
|
95
|
+
}
|
|
96
|
+
return max / 2 || 1;
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// Pour chaque x : abscisse en px + bornes basse/haute (px) par série,
|
|
100
|
+
// baseline « wiggle » centrée (pile centrée autour de 0 ⇒ baseline = -somme/2).
|
|
101
|
+
const layout = $derived.by(() => {
|
|
102
|
+
const n = data.length;
|
|
103
|
+
const xs = data.map((_, i) => {
|
|
104
|
+
const denom = Math.max(n - 1, 1);
|
|
105
|
+
const xRatio = n === 1 ? 0.5 : i / denom;
|
|
106
|
+
return MARGIN.left + xRatio * plotWidth;
|
|
107
|
+
});
|
|
108
|
+
const midY = MARGIN.top + plotHeight / 2;
|
|
109
|
+
// valToY : une grandeur signée (par rapport au centre) → coordonnée px.
|
|
110
|
+
const valToY = (signed: number) => midY - scaleLinear(signed, 0, halfMax, 0, plotHeight / 2);
|
|
111
|
+
|
|
112
|
+
// bands[seriesIndex][xIndex] = { x, top, bottom } en px.
|
|
113
|
+
const bands = series.map(() => [] as { x: number; top: number; bottom: number }[]);
|
|
114
|
+
data.forEach((d, xi) => {
|
|
115
|
+
const total = d.values.reduce((s, sv) => s + safeValue(sv.value), 0);
|
|
116
|
+
let acc = -total / 2; // baseline centrée
|
|
117
|
+
series.forEach((s, si) => {
|
|
118
|
+
const sv = d.values.find((v) => v.label === s.seriesLabel);
|
|
119
|
+
const v = sv ? safeValue(sv.value) : 0;
|
|
120
|
+
const lower = acc;
|
|
121
|
+
const upper = acc + v;
|
|
122
|
+
acc = upper;
|
|
123
|
+
bands[si].push({ x: xs[xi], top: valToY(upper), bottom: valToY(lower) });
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
return { xs, bands };
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// Aire fermée par série (haut gauche→droite, puis bas droite→gauche).
|
|
130
|
+
const areas = $derived.by(() =>
|
|
131
|
+
series.map((s, si) => {
|
|
132
|
+
const band = layout.bands[si];
|
|
133
|
+
if (!band || band.length === 0) return { tone: s.tone, seriesLabel: s.seriesLabel, d: "" };
|
|
134
|
+
const topPts = band.map((b) => ({ x: b.x, y: b.top }));
|
|
135
|
+
const bottomPts = band.map((b) => ({ x: b.x, y: b.bottom })).reverse();
|
|
136
|
+
const topPath = smooth ? buildSmoothPath(topPts) : buildLinearPath(topPts);
|
|
137
|
+
const bottomPath = (smooth ? buildSmoothPath(bottomPts) : buildLinearPath(bottomPts)).replace(/^M/, "L");
|
|
138
|
+
return { tone: s.tone, seriesLabel: s.seriesLabel, d: `${topPath} ${bottomPath} Z` };
|
|
139
|
+
})
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
// Étiquettes d'axe X : jusqu'à 5 catégories réparties.
|
|
143
|
+
const xTickEntries = $derived.by(() => {
|
|
144
|
+
const n = data.length;
|
|
145
|
+
if (n === 0) return [] as { x: number; label: string }[];
|
|
146
|
+
const target = Math.min(5, n);
|
|
147
|
+
const stride = Math.max(1, Math.round((n - 1) / (target - 1 || 1)));
|
|
148
|
+
const entries: { x: number; label: string }[] = [];
|
|
149
|
+
for (let i = 0; i < n; i += stride) entries.push({ x: layout.xs[i], label: data[i].category });
|
|
150
|
+
const lastIdx = n - 1;
|
|
151
|
+
if (entries[entries.length - 1]?.label !== data[lastIdx].category) {
|
|
152
|
+
entries.push({ x: layout.xs[lastIdx], label: data[lastIdx].category });
|
|
153
|
+
}
|
|
154
|
+
return entries;
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// Liste accessible : total par série + total global.
|
|
158
|
+
const dataValueItems = $derived.by(() => {
|
|
159
|
+
const items = series.map((s) => {
|
|
160
|
+
const total = data.reduce((sum, d) => {
|
|
161
|
+
const sv = d.values.find((v) => v.label === s.seriesLabel);
|
|
162
|
+
return sum + (sv ? safeValue(sv.value) : 0);
|
|
163
|
+
}, 0);
|
|
164
|
+
return `${s.seriesLabel}: ${total}`;
|
|
165
|
+
});
|
|
166
|
+
const grand = data.reduce((sum, d) => sum + d.values.reduce((s, sv) => s + safeValue(sv.value), 0), 0);
|
|
167
|
+
if (series.length > 0) items.push(`Total: ${grand}`);
|
|
168
|
+
return items;
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
let hovered: number | null = $state(null);
|
|
172
|
+
|
|
173
|
+
function handleVisualPointerMove(event: PointerEvent) {
|
|
174
|
+
const target = event.target;
|
|
175
|
+
if (!(target instanceof Element)) {
|
|
176
|
+
hovered = null;
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
const si = Number(target.getAttribute("data-series-index"));
|
|
180
|
+
hovered = Number.isInteger(si) ? si : null;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const tooltip = $derived.by(() => {
|
|
184
|
+
if (hovered === null || !series[hovered]) return null;
|
|
185
|
+
const s = series[hovered];
|
|
186
|
+
const band = layout.bands[hovered];
|
|
187
|
+
if (!band || band.length === 0) return null;
|
|
188
|
+
const mid = band[Math.floor(band.length / 2)];
|
|
189
|
+
const total = data.reduce((sum, d) => {
|
|
190
|
+
const sv = d.values.find((v) => v.label === s.seriesLabel);
|
|
191
|
+
return sum + (sv ? safeValue(sv.value) : 0);
|
|
192
|
+
}, 0);
|
|
193
|
+
return { label: s.seriesLabel, value: total, cx: mid.x, cy: (mid.top + mid.bottom) / 2 };
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
const classes = () => ["st-streamgraphChart", className].filter(Boolean).join(" ");
|
|
197
|
+
</script>
|
|
198
|
+
|
|
199
|
+
<div class={classes()}>
|
|
200
|
+
<div
|
|
201
|
+
class="st-streamgraphChart__visual"
|
|
202
|
+
role="img"
|
|
203
|
+
aria-label={label}
|
|
204
|
+
onpointermove={handleVisualPointerMove}
|
|
205
|
+
onpointerleave={() => (hovered = null)}
|
|
206
|
+
>
|
|
207
|
+
<svg viewBox="0 0 {width} {height}" preserveAspectRatio="xMidYMid meet" width="100%" height="100%" focusable="false" aria-hidden="true">
|
|
208
|
+
<line class="st-streamgraphChart__axis" x1={MARGIN.left} x2={width - MARGIN.right} y1={height - MARGIN.bottom} y2={height - MARGIN.bottom} />
|
|
209
|
+
|
|
210
|
+
{#each xTickEntries as tick, i (i)}
|
|
211
|
+
<text class="st-streamgraphChart__tickLabel" x={tick.x} y={height - MARGIN.bottom + 16} text-anchor="middle">{tick.label}</text>
|
|
212
|
+
{/each}
|
|
213
|
+
|
|
214
|
+
{#each areas as area, si (area.seriesLabel)}
|
|
215
|
+
{#if area.d}
|
|
216
|
+
<path
|
|
217
|
+
class="st-streamgraphChart__area st-streamgraphChart__area--{area.tone}"
|
|
218
|
+
class:st-streamgraphChart__area--dim={hovered !== null && hovered !== si}
|
|
219
|
+
d={area.d}
|
|
220
|
+
data-series-index={si}
|
|
221
|
+
/>
|
|
222
|
+
{/if}
|
|
223
|
+
{/each}
|
|
224
|
+
</svg>
|
|
225
|
+
</div>
|
|
226
|
+
|
|
227
|
+
<ChartDataList {label} items={dataValueItems} />
|
|
228
|
+
|
|
229
|
+
{#if tooltip}
|
|
230
|
+
<div class="st-streamgraphChart__tooltip" role="presentation" style="left: {(tooltip.cx / width) * 100}%; top: {(tooltip.cy / height) * 100}%">
|
|
231
|
+
<span class="st-streamgraphChart__tooltipLabel">{tooltip.label}</span>
|
|
232
|
+
<span class="st-streamgraphChart__tooltipValue">{tooltip.value}</span>
|
|
233
|
+
</div>
|
|
234
|
+
{/if}
|
|
235
|
+
|
|
236
|
+
{#if showLegend && series.length > 0}
|
|
237
|
+
<ul class="st-streamgraphChart__legend">
|
|
238
|
+
{#each series as item (item.seriesLabel)}
|
|
239
|
+
<li class="st-streamgraphChart__legendItem">
|
|
240
|
+
<span class="st-streamgraphChart__legendSwatch st-streamgraphChart__legendSwatch--{item.tone}" aria-hidden="true"></span>
|
|
241
|
+
{item.seriesLabel}
|
|
242
|
+
</li>
|
|
243
|
+
{/each}
|
|
244
|
+
</ul>
|
|
245
|
+
{/if}
|
|
246
|
+
</div>
|
|
247
|
+
|
|
248
|
+
<style>
|
|
249
|
+
.st-streamgraphChart { color: var(--st-semantic-text-secondary); display: block; font-family: inherit; position: relative; width: 100%; }
|
|
250
|
+
.st-streamgraphChart svg, .st-streamgraphChart__visual { display: block; overflow: visible; }
|
|
251
|
+
.st-streamgraphChart__axis { stroke: var(--st-semantic-border-subtle); stroke-width: 1; }
|
|
252
|
+
.st-streamgraphChart__tickLabel { fill: var(--st-semantic-text-secondary); font-size: 0.6875rem; }
|
|
253
|
+
.st-streamgraphChart__area { cursor: pointer; stroke: var(--st-semantic-surface-default, #fff); stroke-width: 0.75; transition: opacity 120ms ease; }
|
|
254
|
+
.st-streamgraphChart__area--dim { opacity: 0.4; }
|
|
255
|
+
.st-streamgraphChart__area--category1 { fill: var(--st-semantic-data-category1); }
|
|
256
|
+
.st-streamgraphChart__area--category2 { fill: var(--st-semantic-data-category2); }
|
|
257
|
+
.st-streamgraphChart__area--category3 { fill: var(--st-semantic-data-category3); }
|
|
258
|
+
.st-streamgraphChart__area--category4 { fill: var(--st-semantic-data-category4); }
|
|
259
|
+
.st-streamgraphChart__area--category5 { fill: var(--st-semantic-data-category5); }
|
|
260
|
+
.st-streamgraphChart__area--category6 { fill: var(--st-semantic-data-category6); }
|
|
261
|
+
.st-streamgraphChart__area--category7 { fill: var(--st-semantic-data-category7); }
|
|
262
|
+
.st-streamgraphChart__area--category8 { fill: var(--st-semantic-data-category8); }
|
|
263
|
+
@media (prefers-reduced-motion: reduce) { .st-streamgraphChart__area { transition: none; } }
|
|
264
|
+
.st-streamgraphChart__tooltip {
|
|
265
|
+
background: var(--st-semantic-surface-inverse); border-radius: var(--st-radius-sm, 0.25rem);
|
|
266
|
+
color: var(--st-semantic-text-inverse); display: inline-flex; flex-direction: column; font-size: 0.75rem;
|
|
267
|
+
gap: 0.125rem; line-height: 1.2; padding: 0.375rem 0.5rem; pointer-events: none; position: absolute;
|
|
268
|
+
transform: translate(-50%, calc(-100% - 8px)); white-space: nowrap; z-index: 1;
|
|
269
|
+
}
|
|
270
|
+
.st-streamgraphChart__tooltipLabel { font-weight: 600; }
|
|
271
|
+
.st-streamgraphChart__tooltipValue { opacity: 0.85; }
|
|
272
|
+
.st-streamgraphChart__legend { display: flex; flex-wrap: wrap; gap: 0.75rem; list-style: none; margin: 0.5rem 0 0; padding: 0; }
|
|
273
|
+
.st-streamgraphChart__legendItem { align-items: center; color: var(--st-semantic-text-secondary); display: inline-flex; font-size: 0.75rem; gap: 0.35rem; }
|
|
274
|
+
.st-streamgraphChart__legendSwatch { border-radius: 2px; height: 0.7rem; width: 0.7rem; }
|
|
275
|
+
.st-streamgraphChart__legendSwatch--category1 { background: var(--st-semantic-data-category1); }
|
|
276
|
+
.st-streamgraphChart__legendSwatch--category2 { background: var(--st-semantic-data-category2); }
|
|
277
|
+
.st-streamgraphChart__legendSwatch--category3 { background: var(--st-semantic-data-category3); }
|
|
278
|
+
.st-streamgraphChart__legendSwatch--category4 { background: var(--st-semantic-data-category4); }
|
|
279
|
+
.st-streamgraphChart__legendSwatch--category5 { background: var(--st-semantic-data-category5); }
|
|
280
|
+
.st-streamgraphChart__legendSwatch--category6 { background: var(--st-semantic-data-category6); }
|
|
281
|
+
.st-streamgraphChart__legendSwatch--category7 { background: var(--st-semantic-data-category7); }
|
|
282
|
+
.st-streamgraphChart__legendSwatch--category8 { background: var(--st-semantic-data-category8); }
|
|
283
|
+
</style>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type StreamgraphChartTone = "category1" | "category2" | "category3" | "category4" | "category5" | "category6" | "category7" | "category8";
|
|
2
|
+
export type StreamgraphChartSeriesValue = {
|
|
3
|
+
label: string;
|
|
4
|
+
value: number;
|
|
5
|
+
tone?: StreamgraphChartTone;
|
|
6
|
+
};
|
|
7
|
+
export type StreamgraphChartDatum = {
|
|
8
|
+
category: string;
|
|
9
|
+
values: StreamgraphChartSeriesValue[];
|
|
10
|
+
};
|
|
11
|
+
type StreamgraphChartProps = {
|
|
12
|
+
data: StreamgraphChartDatum[];
|
|
13
|
+
width?: number;
|
|
14
|
+
height?: number;
|
|
15
|
+
label: string;
|
|
16
|
+
smooth?: boolean;
|
|
17
|
+
showLegend?: boolean;
|
|
18
|
+
class?: string;
|
|
19
|
+
};
|
|
20
|
+
declare const StreamgraphChart: import("svelte").Component<StreamgraphChartProps, {}, "">;
|
|
21
|
+
type StreamgraphChart = ReturnType<typeof StreamgraphChart>;
|
|
22
|
+
export default StreamgraphChart;
|
|
23
|
+
//# sourceMappingURL=StreamgraphChart.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StreamgraphChart.svelte.d.ts","sourceRoot":"","sources":["../src/lib/StreamgraphChart.svelte.ts"],"names":[],"mappings":"AAGE,MAAM,MAAM,oBAAoB,GAC5B,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,GACrD,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,CAAC;AAG1D,MAAM,MAAM,2BAA2B,GAAG;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,oBAAoB,CAAC;CAC7B,CAAC;AAGF,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,2BAA2B,EAAE,CAAC;CACvC,CAAC;AAMF,KAAK,qBAAqB,GAAG;IAC3B,IAAI,EAAE,qBAAqB,EAAE,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAuNJ,QAAA,MAAM,gBAAgB,2DAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
|
|
@@ -34,6 +34,18 @@
|
|
|
34
34
|
|
|
35
35
|
export type StreamingMessageMode = "live" | "passive";
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Simple data-driven event shape (cross-framework parity with React/Vue
|
|
39
|
+
* ports). Distinguished from the rich event-stream shape by the absence of a
|
|
40
|
+
* `type` discriminant.
|
|
41
|
+
*/
|
|
42
|
+
export type StreamingMessageSimpleEvent = {
|
|
43
|
+
id: string;
|
|
44
|
+
label: string;
|
|
45
|
+
text?: string;
|
|
46
|
+
status?: ChatMessageStatus;
|
|
47
|
+
};
|
|
48
|
+
|
|
37
49
|
type ToolCallState = {
|
|
38
50
|
toolCallId: string;
|
|
39
51
|
toolName: string;
|
|
@@ -48,8 +60,14 @@
|
|
|
48
60
|
status?: ChatMessageStatus;
|
|
49
61
|
streamId?: string;
|
|
50
62
|
initialEvents?: StreamingMessageEvent[];
|
|
51
|
-
events?: StreamingMessageEvent[];
|
|
63
|
+
events?: StreamingMessageEvent[] | StreamingMessageSimpleEvent[];
|
|
52
64
|
finalContent?: string;
|
|
65
|
+
/**
|
|
66
|
+
* Plain message body (cross-framework parity with React/Vue ports). When
|
|
67
|
+
* provided, it is used as the rendered content (equivalent to
|
|
68
|
+
* `finalContent`). The canonical event-stream API still works.
|
|
69
|
+
*/
|
|
70
|
+
text?: string;
|
|
53
71
|
mode?: StreamingMessageMode;
|
|
54
72
|
placeholder?: string;
|
|
55
73
|
showTrail?: boolean;
|
|
@@ -65,6 +83,7 @@
|
|
|
65
83
|
initialEvents = [],
|
|
66
84
|
events = [],
|
|
67
85
|
finalContent,
|
|
86
|
+
text,
|
|
68
87
|
mode = "live",
|
|
69
88
|
placeholder = "Streaming en cours…",
|
|
70
89
|
showTrail = true,
|
|
@@ -76,10 +95,25 @@
|
|
|
76
95
|
|
|
77
96
|
const classes = () => ["st-streamingMessage", className].filter(Boolean).join(" ");
|
|
78
97
|
|
|
79
|
-
|
|
98
|
+
// A simple event lacks the `type` discriminant of the rich stream shape.
|
|
99
|
+
const isSimpleEvent = (
|
|
100
|
+
event: StreamingMessageEvent | StreamingMessageSimpleEvent
|
|
101
|
+
): event is StreamingMessageSimpleEvent =>
|
|
102
|
+
event != null && typeof event === "object" && !("type" in event);
|
|
103
|
+
|
|
104
|
+
const simpleEvents = (): StreamingMessageSimpleEvent[] =>
|
|
105
|
+
(events as Array<StreamingMessageEvent | StreamingMessageSimpleEvent>).filter(isSimpleEvent);
|
|
106
|
+
|
|
107
|
+
const richEvents = (): StreamingMessageEvent[] =>
|
|
108
|
+
(events as Array<StreamingMessageEvent | StreamingMessageSimpleEvent>).filter(
|
|
109
|
+
(event): event is StreamingMessageEvent => !isSimpleEvent(event)
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const mergedEvents = () => [...initialEvents, ...richEvents()];
|
|
80
113
|
|
|
81
114
|
const resolvedContent = () => {
|
|
82
115
|
if (finalContent) return finalContent;
|
|
116
|
+
if (text) return text;
|
|
83
117
|
return mergedEvents()
|
|
84
118
|
.filter((event) => event.type === "message.delta")
|
|
85
119
|
.map((event) => event.delta)
|
|
@@ -242,6 +276,14 @@
|
|
|
242
276
|
</ul>
|
|
243
277
|
</details>
|
|
244
278
|
{/if}
|
|
279
|
+
|
|
280
|
+
{#if simpleEvents().length > 0}
|
|
281
|
+
<ul class="st-streamingMessage__trailList">
|
|
282
|
+
{#each simpleEvents() as event (event.id)}
|
|
283
|
+
<li>{event.label}</li>
|
|
284
|
+
{/each}
|
|
285
|
+
</ul>
|
|
286
|
+
{/if}
|
|
245
287
|
</ChatMessage>
|
|
246
288
|
|
|
247
289
|
<style>
|