@sentropic/design-system-svelte 0.34.0 → 0.34.21
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/AppChrome.svelte +660 -0
- package/dist/AppChrome.svelte.d.ts +74 -0
- package/dist/AppChrome.svelte.d.ts.map +1 -0
- package/dist/AppChrome.test.d.ts +2 -0
- package/dist/AppChrome.test.d.ts.map +1 -0
- package/dist/AppChrome.test.js +122 -0
- 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/ConfigItemCard.svelte +303 -0
- package/dist/ConfigItemCard.svelte.d.ts +37 -0
- package/dist/ConfigItemCard.svelte.d.ts.map +1 -0
- package/dist/ContentSwitcher.svelte +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/FieldCard.svelte +220 -0
- package/dist/FieldCard.svelte.d.ts +28 -0
- package/dist/FieldCard.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/ScoreCard.svelte +172 -0
- package/dist/ScoreCard.svelte.d.ts +27 -0
- package/dist/ScoreCard.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 +56 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +28 -0
- package/package.json +5 -3
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
/**
|
|
3
|
+
* OHLCChart - barres open/high/low/close (bâtons financiers).
|
|
4
|
+
* Variante du CandlestickChart : barre verticale du low au high,
|
|
5
|
+
* tick gauche au niveau open, tick droite au niveau close.
|
|
6
|
+
* API canonique (référence Svelte, React/Vue doivent s'aligner)
|
|
7
|
+
*
|
|
8
|
+
* Props obligatoires :
|
|
9
|
+
* data OHLCChartDatum[] - tableau {label, open, high, low, close}
|
|
10
|
+
* label string
|
|
11
|
+
*
|
|
12
|
+
* Props optionnelles :
|
|
13
|
+
* width number (défaut 480)
|
|
14
|
+
* height number (défaut 240)
|
|
15
|
+
* class string
|
|
16
|
+
*/
|
|
17
|
+
export type OHLCChartDatum = {
|
|
18
|
+
label: string;
|
|
19
|
+
open: number;
|
|
20
|
+
high: number;
|
|
21
|
+
low: number;
|
|
22
|
+
close: number;
|
|
23
|
+
};
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<script lang="ts">
|
|
27
|
+
import ChartDataList from "./ChartDataList.svelte";
|
|
28
|
+
|
|
29
|
+
type OHLCChartProps = {
|
|
30
|
+
data: OHLCChartDatum[];
|
|
31
|
+
label: string;
|
|
32
|
+
width?: number;
|
|
33
|
+
height?: number;
|
|
34
|
+
class?: string;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
let {
|
|
38
|
+
data = [],
|
|
39
|
+
label,
|
|
40
|
+
width = 480,
|
|
41
|
+
height = 240,
|
|
42
|
+
class: className
|
|
43
|
+
}: OHLCChartProps = $props();
|
|
44
|
+
|
|
45
|
+
const MARGIN = { top: 12, right: 16, bottom: 32, left: 52 };
|
|
46
|
+
|
|
47
|
+
function scaleLinear(v: number, d0: number, d1: number, r0: number, r1: number) {
|
|
48
|
+
if (d1 === d0) return r0;
|
|
49
|
+
return r0 + ((v - d0) * (r1 - r0)) / (d1 - d0);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function niceTicks(min: number, max: number, target = 5): number[] {
|
|
53
|
+
if (!Number.isFinite(min) || !Number.isFinite(max) || min === max) {
|
|
54
|
+
const base = Number.isFinite(max) ? max : 0;
|
|
55
|
+
return [base];
|
|
56
|
+
}
|
|
57
|
+
const range = max - min;
|
|
58
|
+
const rough = range / Math.max(target - 1, 1);
|
|
59
|
+
const pow = Math.pow(10, Math.floor(Math.log10(rough)));
|
|
60
|
+
const norm = rough / pow;
|
|
61
|
+
let step: number;
|
|
62
|
+
if (norm < 1.5) step = 1 * pow;
|
|
63
|
+
else if (norm < 3) step = 2 * pow;
|
|
64
|
+
else if (norm < 7) step = 5 * pow;
|
|
65
|
+
else step = 10 * pow;
|
|
66
|
+
const start = Math.floor(min / step) * step;
|
|
67
|
+
const end = Math.ceil(max / step) * step;
|
|
68
|
+
const ticks: number[] = [];
|
|
69
|
+
for (let v = start; v <= end + step / 2; v += step) {
|
|
70
|
+
ticks.push(Number(v.toFixed(10)));
|
|
71
|
+
}
|
|
72
|
+
return ticks;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function formatTick(v: number): string {
|
|
76
|
+
if (Math.abs(v) >= 1000) return `${(v / 1000).toFixed(v % 1000 === 0 ? 0 : 1)}k`;
|
|
77
|
+
if (Number.isInteger(v)) return String(v);
|
|
78
|
+
return v.toFixed(1);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let hoveredIndex: number | null = $state(null);
|
|
82
|
+
|
|
83
|
+
const plotWidth = $derived(Math.max(width - MARGIN.left - MARGIN.right, 1));
|
|
84
|
+
const plotHeight = $derived(Math.max(height - MARGIN.top - MARGIN.bottom, 1));
|
|
85
|
+
|
|
86
|
+
// Filtrer les barres invalides AVANT le domaine
|
|
87
|
+
const validData = $derived(
|
|
88
|
+
data.filter((d) =>
|
|
89
|
+
Number.isFinite(d.open) &&
|
|
90
|
+
Number.isFinite(d.high) &&
|
|
91
|
+
Number.isFinite(d.low) &&
|
|
92
|
+
Number.isFinite(d.close)
|
|
93
|
+
)
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const domainBounds = $derived.by(() => {
|
|
97
|
+
const allVals: number[] = [];
|
|
98
|
+
for (const d of validData) {
|
|
99
|
+
// Domaine inclut open/high/low/close (pas seulement high/low)
|
|
100
|
+
allVals.push(d.open, d.high, d.low, d.close);
|
|
101
|
+
}
|
|
102
|
+
if (allVals.length === 0) return { rawMin: 0, rawMax: 1 };
|
|
103
|
+
const rawMin = Math.min(...allVals);
|
|
104
|
+
const rawMax = Math.max(...allVals);
|
|
105
|
+
// Domaine plat → fallback range 1 pour éviter division par 0
|
|
106
|
+
return { rawMin, rawMax: rawMax === rawMin ? rawMin + 1 : rawMax };
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
const ticks = $derived(niceTicks(domainBounds.rawMin, domainBounds.rawMax, 5));
|
|
110
|
+
const domainMin = $derived(ticks[0]);
|
|
111
|
+
const domainMax = $derived(ticks[ticks.length - 1]);
|
|
112
|
+
|
|
113
|
+
const bars = $derived.by(() => {
|
|
114
|
+
if (validData.length === 0) return [];
|
|
115
|
+
const band = plotWidth / validData.length;
|
|
116
|
+
// longueur du tick open/close (de chaque côté de la barre)
|
|
117
|
+
const tickW = Math.min(band * 0.3, 12);
|
|
118
|
+
|
|
119
|
+
return validData.map((d, i) => {
|
|
120
|
+
// clamp high/low pour garantir high≥max(O,C) et low≤min(O,C)
|
|
121
|
+
const clampedHigh = Math.max(d.high, d.open, d.close);
|
|
122
|
+
const clampedLow = Math.min(d.low, d.open, d.close);
|
|
123
|
+
|
|
124
|
+
const bullish = d.close >= d.open;
|
|
125
|
+
const centerX = MARGIN.left + band * i + band / 2;
|
|
126
|
+
|
|
127
|
+
const highY = MARGIN.top + scaleLinear(clampedHigh, domainMin, domainMax, plotHeight, 0);
|
|
128
|
+
const lowY = MARGIN.top + scaleLinear(clampedLow, domainMin, domainMax, plotHeight, 0);
|
|
129
|
+
const openY = MARGIN.top + scaleLinear(d.open, domainMin, domainMax, plotHeight, 0);
|
|
130
|
+
const closeY = MARGIN.top + scaleLinear(d.close, domainMin, domainMax, plotHeight, 0);
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
datum: d,
|
|
134
|
+
index: i,
|
|
135
|
+
bullish,
|
|
136
|
+
centerX,
|
|
137
|
+
barHighY: highY,
|
|
138
|
+
barLowY: lowY,
|
|
139
|
+
openY,
|
|
140
|
+
closeY,
|
|
141
|
+
openX: centerX - tickW,
|
|
142
|
+
closeX: centerX + tickW,
|
|
143
|
+
tooltipY: Math.min(highY, openY, closeY)
|
|
144
|
+
};
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const dataValueItems = $derived(
|
|
149
|
+
validData.map((d) => `${d.label}: O ${d.open} H ${d.high} L ${d.low} C ${d.close}`)
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
function handlePointerMove(event: PointerEvent) {
|
|
153
|
+
const target = event.target;
|
|
154
|
+
if (!(target instanceof Element)) { hoveredIndex = null; return; }
|
|
155
|
+
const idx = Number(target.getAttribute("data-chart-index"));
|
|
156
|
+
hoveredIndex = Number.isInteger(idx) ? idx : null;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const classes = () => ["st-ohlcChart", className].filter(Boolean).join(" ");
|
|
160
|
+
</script>
|
|
161
|
+
|
|
162
|
+
<div class={classes()}>
|
|
163
|
+
<div
|
|
164
|
+
class="st-ohlcChart__visual"
|
|
165
|
+
role="img"
|
|
166
|
+
aria-label={label}
|
|
167
|
+
onpointermove={handlePointerMove}
|
|
168
|
+
onpointerleave={() => (hoveredIndex = null)}
|
|
169
|
+
>
|
|
170
|
+
<svg
|
|
171
|
+
viewBox="0 0 {width} {height}"
|
|
172
|
+
preserveAspectRatio="xMidYMid meet"
|
|
173
|
+
width="100%"
|
|
174
|
+
height="100%"
|
|
175
|
+
focusable="false"
|
|
176
|
+
aria-hidden="true"
|
|
177
|
+
>
|
|
178
|
+
<!-- gridlines + tick labels -->
|
|
179
|
+
{#each ticks as tick (tick)}
|
|
180
|
+
{@const ty = MARGIN.top + scaleLinear(tick, domainMin, domainMax, plotHeight, 0)}
|
|
181
|
+
<line class="st-ohlcChart__grid" x1={MARGIN.left} x2={width - MARGIN.right} y1={ty} y2={ty} />
|
|
182
|
+
<text class="st-ohlcChart__tickLabel" x={MARGIN.left - 6} y={ty} text-anchor="end" dominant-baseline="middle">
|
|
183
|
+
{formatTick(tick)}
|
|
184
|
+
</text>
|
|
185
|
+
{/each}
|
|
186
|
+
|
|
187
|
+
<!-- axes -->
|
|
188
|
+
<line class="st-ohlcChart__axis" x1={MARGIN.left} x2={MARGIN.left} y1={MARGIN.top} y2={height - MARGIN.bottom} />
|
|
189
|
+
<line class="st-ohlcChart__axis" x1={MARGIN.left} x2={width - MARGIN.right} y1={height - MARGIN.bottom} y2={height - MARGIN.bottom} />
|
|
190
|
+
|
|
191
|
+
<!-- clé composite pour éviter les doublons -->
|
|
192
|
+
{#each bars as b, i (`${i}-${b.datum.label}`)}
|
|
193
|
+
<g
|
|
194
|
+
class="st-ohlcChart__bar st-ohlcChart__bar--{b.bullish ? 'up' : 'down'}"
|
|
195
|
+
class:st-ohlcChart__bar--dim={hoveredIndex !== null && hoveredIndex !== i}
|
|
196
|
+
>
|
|
197
|
+
<!-- barre verticale low → high -->
|
|
198
|
+
<line
|
|
199
|
+
class="st-ohlcChart__range"
|
|
200
|
+
x1={b.centerX}
|
|
201
|
+
x2={b.centerX}
|
|
202
|
+
y1={b.barHighY}
|
|
203
|
+
y2={b.barLowY}
|
|
204
|
+
data-chart-index={i}
|
|
205
|
+
/>
|
|
206
|
+
<!-- tick open (gauche) -->
|
|
207
|
+
<line
|
|
208
|
+
class="st-ohlcChart__open"
|
|
209
|
+
x1={b.openX}
|
|
210
|
+
x2={b.centerX}
|
|
211
|
+
y1={b.openY}
|
|
212
|
+
y2={b.openY}
|
|
213
|
+
data-chart-index={i}
|
|
214
|
+
/>
|
|
215
|
+
<!-- tick close (droite) -->
|
|
216
|
+
<line
|
|
217
|
+
class="st-ohlcChart__close"
|
|
218
|
+
x1={b.centerX}
|
|
219
|
+
x2={b.closeX}
|
|
220
|
+
y1={b.closeY}
|
|
221
|
+
y2={b.closeY}
|
|
222
|
+
data-chart-index={i}
|
|
223
|
+
/>
|
|
224
|
+
</g>
|
|
225
|
+
<!-- category label -->
|
|
226
|
+
<text
|
|
227
|
+
class="st-ohlcChart__categoryLabel"
|
|
228
|
+
x={b.centerX}
|
|
229
|
+
y={height - MARGIN.bottom + 16}
|
|
230
|
+
text-anchor="middle"
|
|
231
|
+
>
|
|
232
|
+
{b.datum.label}
|
|
233
|
+
</text>
|
|
234
|
+
{/each}
|
|
235
|
+
</svg>
|
|
236
|
+
</div>
|
|
237
|
+
|
|
238
|
+
<ChartDataList {label} items={dataValueItems} />
|
|
239
|
+
|
|
240
|
+
{#if hoveredIndex !== null && bars[hoveredIndex]}
|
|
241
|
+
{@const b = bars[hoveredIndex]}
|
|
242
|
+
<div
|
|
243
|
+
class="st-ohlcChart__tooltip"
|
|
244
|
+
role="presentation"
|
|
245
|
+
style="left: {(b.centerX / width) * 100}%; top: {(b.tooltipY / height) * 100}%"
|
|
246
|
+
>
|
|
247
|
+
<span class="st-ohlcChart__tooltipLabel">{b.datum.label}</span>
|
|
248
|
+
<span class="st-ohlcChart__tooltipValue">O {b.datum.open} H {b.datum.high} L {b.datum.low} C {b.datum.close}</span>
|
|
249
|
+
</div>
|
|
250
|
+
{/if}
|
|
251
|
+
</div>
|
|
252
|
+
|
|
253
|
+
<style>
|
|
254
|
+
.st-ohlcChart {
|
|
255
|
+
color: var(--st-semantic-text-secondary);
|
|
256
|
+
display: block;
|
|
257
|
+
font-family: inherit;
|
|
258
|
+
position: relative;
|
|
259
|
+
width: 100%;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.st-ohlcChart svg {
|
|
263
|
+
display: block;
|
|
264
|
+
overflow: visible;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
.st-ohlcChart__visual {
|
|
268
|
+
display: block;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
.st-ohlcChart__axis {
|
|
272
|
+
stroke: var(--st-semantic-border-subtle);
|
|
273
|
+
stroke-width: 1;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.st-ohlcChart__grid {
|
|
277
|
+
stroke: var(--st-semantic-border-subtle);
|
|
278
|
+
stroke-dasharray: 2 3;
|
|
279
|
+
stroke-width: 1;
|
|
280
|
+
opacity: 0.7;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.st-ohlcChart__bar {
|
|
284
|
+
cursor: pointer;
|
|
285
|
+
transition: opacity 120ms ease;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
.st-ohlcChart__bar--dim {
|
|
289
|
+
opacity: 0.4;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.st-ohlcChart__range,
|
|
293
|
+
.st-ohlcChart__open,
|
|
294
|
+
.st-ohlcChart__close {
|
|
295
|
+
stroke-width: 1.75;
|
|
296
|
+
stroke-linecap: round;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
.st-ohlcChart__bar--up :is(.st-ohlcChart__range, .st-ohlcChart__open, .st-ohlcChart__close) {
|
|
300
|
+
stroke: var(--st-semantic-feedback-success);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.st-ohlcChart__bar--down :is(.st-ohlcChart__range, .st-ohlcChart__open, .st-ohlcChart__close) {
|
|
304
|
+
stroke: var(--st-semantic-feedback-error);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
@media (prefers-reduced-motion: reduce) {
|
|
308
|
+
.st-ohlcChart__bar {
|
|
309
|
+
transition: none;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.st-ohlcChart__tickLabel,
|
|
314
|
+
.st-ohlcChart__categoryLabel {
|
|
315
|
+
fill: var(--st-semantic-text-secondary);
|
|
316
|
+
font-size: 0.6875rem;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
.st-ohlcChart__tooltip {
|
|
320
|
+
background: var(--st-semantic-surface-inverse);
|
|
321
|
+
border-radius: var(--st-radius-sm, 0.25rem);
|
|
322
|
+
color: var(--st-semantic-text-inverse);
|
|
323
|
+
display: inline-flex;
|
|
324
|
+
flex-direction: column;
|
|
325
|
+
font-size: 0.75rem;
|
|
326
|
+
gap: 0.125rem;
|
|
327
|
+
line-height: 1.2;
|
|
328
|
+
padding: 0.375rem 0.5rem;
|
|
329
|
+
pointer-events: none;
|
|
330
|
+
position: absolute;
|
|
331
|
+
transform: translate(-50%, calc(-100% - 8px));
|
|
332
|
+
white-space: nowrap;
|
|
333
|
+
z-index: 1;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
.st-ohlcChart__tooltipLabel {
|
|
337
|
+
font-weight: 600;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
.st-ohlcChart__tooltipValue {
|
|
341
|
+
opacity: 0.85;
|
|
342
|
+
}
|
|
343
|
+
</style>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OHLCChart - barres open/high/low/close (bâtons financiers).
|
|
3
|
+
* Variante du CandlestickChart : barre verticale du low au high,
|
|
4
|
+
* tick gauche au niveau open, tick droite au niveau close.
|
|
5
|
+
* API canonique (référence Svelte, React/Vue doivent s'aligner)
|
|
6
|
+
*
|
|
7
|
+
* Props obligatoires :
|
|
8
|
+
* data OHLCChartDatum[] - tableau {label, open, high, low, close}
|
|
9
|
+
* label string
|
|
10
|
+
*
|
|
11
|
+
* Props optionnelles :
|
|
12
|
+
* width number (défaut 480)
|
|
13
|
+
* height number (défaut 240)
|
|
14
|
+
* class string
|
|
15
|
+
*/
|
|
16
|
+
export type OHLCChartDatum = {
|
|
17
|
+
label: string;
|
|
18
|
+
open: number;
|
|
19
|
+
high: number;
|
|
20
|
+
low: number;
|
|
21
|
+
close: number;
|
|
22
|
+
};
|
|
23
|
+
type OHLCChartProps = {
|
|
24
|
+
data: OHLCChartDatum[];
|
|
25
|
+
label: string;
|
|
26
|
+
width?: number;
|
|
27
|
+
height?: number;
|
|
28
|
+
class?: string;
|
|
29
|
+
};
|
|
30
|
+
declare const OHLCChart: import("svelte").Component<OHLCChartProps, {}, "">;
|
|
31
|
+
type OHLCChart = ReturnType<typeof OHLCChart>;
|
|
32
|
+
export default OHLCChart;
|
|
33
|
+
//# sourceMappingURL=OHLCChart.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OHLCChart.svelte.d.ts","sourceRoot":"","sources":["../src/lib/OHLCChart.svelte.ts"],"names":[],"mappings":"AAGE;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAMF,KAAK,cAAc,GAAG;IACpB,IAAI,EAAE,cAAc,EAAE,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAqLJ,QAAA,MAAM,SAAS,oDAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
export type OrganizationChartTone =
|
|
3
|
+
| "category1" | "category2" | "category3" | "category4"
|
|
4
|
+
| "category5" | "category6" | "category7" | "category8";
|
|
5
|
+
|
|
6
|
+
export type OrganizationChartNode = {
|
|
7
|
+
id: string;
|
|
8
|
+
parentId?: string | null;
|
|
9
|
+
label: string;
|
|
10
|
+
tone?: OrganizationChartTone;
|
|
11
|
+
};
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<script lang="ts">
|
|
15
|
+
import ChartDataList from "./ChartDataList.svelte";
|
|
16
|
+
|
|
17
|
+
type OrganizationChartProps = {
|
|
18
|
+
/** Nœuds plats : `parentId` null/absent = racine. Plusieurs racines acceptées. */
|
|
19
|
+
data: OrganizationChartNode[];
|
|
20
|
+
width?: number;
|
|
21
|
+
height?: number;
|
|
22
|
+
label: string;
|
|
23
|
+
class?: string;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
let {
|
|
27
|
+
data,
|
|
28
|
+
width = 640,
|
|
29
|
+
height = 320,
|
|
30
|
+
label,
|
|
31
|
+
class: className
|
|
32
|
+
}: OrganizationChartProps = $props();
|
|
33
|
+
|
|
34
|
+
const TONES = [
|
|
35
|
+
"category1", "category2", "category3", "category4",
|
|
36
|
+
"category5", "category6", "category7", "category8"
|
|
37
|
+
] as const;
|
|
38
|
+
|
|
39
|
+
type Box = {
|
|
40
|
+
id: string;
|
|
41
|
+
label: string;
|
|
42
|
+
tone: OrganizationChartTone;
|
|
43
|
+
depth: number;
|
|
44
|
+
x: number; // centre X
|
|
45
|
+
y: number; // haut de la boîte
|
|
46
|
+
parentId: string | null;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
type Link = { id: string; x1: number; y1: number; x2: number; y2: number };
|
|
50
|
+
|
|
51
|
+
const BOX_H = 36;
|
|
52
|
+
const GAP_X = 16; // espace horizontal entre frères (feuilles)
|
|
53
|
+
const PAD_X = 8; // marge latérale
|
|
54
|
+
|
|
55
|
+
type Layout = { boxes: Box[]; links: Link[]; boxW: number };
|
|
56
|
+
|
|
57
|
+
const layout = $derived.by<Layout>(() => {
|
|
58
|
+
if (!data || data.length === 0) return { boxes: [], links: [], boxW: 0 };
|
|
59
|
+
|
|
60
|
+
// Index + détection des racines (parentId invalide/cyclique → traité comme racine).
|
|
61
|
+
const byId = new Map<string, OrganizationChartNode>();
|
|
62
|
+
for (const n of data) if (n.id != null && !byId.has(n.id)) byId.set(n.id, n);
|
|
63
|
+
|
|
64
|
+
const validParent = (n: OrganizationChartNode): string | null => {
|
|
65
|
+
const p = n.parentId;
|
|
66
|
+
if (p == null) return null;
|
|
67
|
+
if (!byId.has(p) || p === n.id) return null;
|
|
68
|
+
// Évite les cycles : remonte la chaîne, si on revient sur n.id → racine.
|
|
69
|
+
let cursor: string | null = p;
|
|
70
|
+
const seen = new Set<string>([n.id]);
|
|
71
|
+
while (cursor != null) {
|
|
72
|
+
if (seen.has(cursor)) return null;
|
|
73
|
+
seen.add(cursor);
|
|
74
|
+
const parent: OrganizationChartNode | undefined = byId.get(cursor);
|
|
75
|
+
cursor = parent ? (parent.parentId ?? null) : null;
|
|
76
|
+
}
|
|
77
|
+
return p;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const childrenOf = new Map<string, string[]>();
|
|
81
|
+
const roots: string[] = [];
|
|
82
|
+
for (const n of byId.values()) {
|
|
83
|
+
const p = validParent(n);
|
|
84
|
+
if (p == null) {
|
|
85
|
+
roots.push(n.id);
|
|
86
|
+
} else {
|
|
87
|
+
const list = childrenOf.get(p) ?? [];
|
|
88
|
+
list.push(n.id);
|
|
89
|
+
childrenOf.set(p, list);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Profondeur par nœud + position des feuilles (parcours en profondeur, ordre stable).
|
|
94
|
+
const depthOf = new Map<string, number>();
|
|
95
|
+
const leafOrder = new Map<string, number>();
|
|
96
|
+
let leafCounter = 0;
|
|
97
|
+
let maxDepth = 0;
|
|
98
|
+
|
|
99
|
+
const visit = (id: string, depth: number) => {
|
|
100
|
+
depthOf.set(id, depth);
|
|
101
|
+
if (depth > maxDepth) maxDepth = depth;
|
|
102
|
+
const kids = childrenOf.get(id) ?? [];
|
|
103
|
+
if (kids.length === 0) {
|
|
104
|
+
leafOrder.set(id, leafCounter);
|
|
105
|
+
leafCounter += 1;
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
for (const k of kids) visit(k, depth + 1);
|
|
109
|
+
};
|
|
110
|
+
for (const r of roots) visit(r, 0);
|
|
111
|
+
|
|
112
|
+
const leafCount = Math.max(leafCounter, 1);
|
|
113
|
+
|
|
114
|
+
// Largeur de boîte : on répartit `leafCount` colonnes sur la largeur utile.
|
|
115
|
+
const usable = Math.max(width - PAD_X * 2, 1);
|
|
116
|
+
const colW = usable / leafCount;
|
|
117
|
+
const boxW = Math.max(colW - GAP_X, 24);
|
|
118
|
+
|
|
119
|
+
// X (centre) : feuille = sa colonne ; parent = moyenne des enfants.
|
|
120
|
+
const centerX = new Map<string, number>();
|
|
121
|
+
const computeX = (id: string): number => {
|
|
122
|
+
const cached = centerX.get(id);
|
|
123
|
+
if (cached != null) return cached;
|
|
124
|
+
const kids = childrenOf.get(id) ?? [];
|
|
125
|
+
let cx: number;
|
|
126
|
+
if (kids.length === 0) {
|
|
127
|
+
const col = leafOrder.get(id) ?? 0;
|
|
128
|
+
cx = PAD_X + col * colW + colW / 2;
|
|
129
|
+
} else {
|
|
130
|
+
const xs = kids.map((k) => computeX(k));
|
|
131
|
+
cx = xs.reduce((s, v) => s + v, 0) / xs.length;
|
|
132
|
+
}
|
|
133
|
+
centerX.set(id, cx);
|
|
134
|
+
return cx;
|
|
135
|
+
};
|
|
136
|
+
for (const r of roots) computeX(r);
|
|
137
|
+
|
|
138
|
+
// Y : une rangée par niveau, répartie verticalement.
|
|
139
|
+
const levels = maxDepth + 1;
|
|
140
|
+
const usableY = Math.max(height - BOX_H, 1);
|
|
141
|
+
const rowGap = levels > 1 ? usableY / (levels - 1) : 0;
|
|
142
|
+
const yForDepth = (d: number) => (levels > 1 ? d * rowGap : (height - BOX_H) / 2);
|
|
143
|
+
|
|
144
|
+
const boxes: Box[] = [];
|
|
145
|
+
let toneIdx = 0;
|
|
146
|
+
// Ordre : profondeur croissante puis ordre d'insertion → rendu stable.
|
|
147
|
+
const ordered = [...byId.values()].filter((n) => depthOf.has(n.id));
|
|
148
|
+
for (const n of ordered) {
|
|
149
|
+
const depth = depthOf.get(n.id) ?? 0;
|
|
150
|
+
const tone = n.tone ?? TONES[toneIdx % TONES.length];
|
|
151
|
+
if (!n.tone) toneIdx += 1;
|
|
152
|
+
boxes.push({
|
|
153
|
+
id: n.id,
|
|
154
|
+
label: n.label,
|
|
155
|
+
tone,
|
|
156
|
+
depth,
|
|
157
|
+
x: centerX.get(n.id) ?? width / 2,
|
|
158
|
+
y: yForDepth(depth),
|
|
159
|
+
parentId: validParent(n)
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const boxById = new Map(boxes.map((b) => [b.id, b]));
|
|
164
|
+
const links: Link[] = [];
|
|
165
|
+
for (const b of boxes) {
|
|
166
|
+
if (b.parentId == null) continue;
|
|
167
|
+
const parent = boxById.get(b.parentId);
|
|
168
|
+
if (!parent) continue;
|
|
169
|
+
links.push({
|
|
170
|
+
id: b.id,
|
|
171
|
+
x1: parent.x,
|
|
172
|
+
y1: parent.y + BOX_H,
|
|
173
|
+
x2: b.x,
|
|
174
|
+
y2: b.y
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return { boxes, links, boxW };
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// Police adaptée à la largeur de boîte (plus petite si étroite).
|
|
182
|
+
const fontSize = $derived(layout.boxW < 56 ? 9 : layout.boxW < 88 ? 10 : 12);
|
|
183
|
+
// Nombre de caractères affichables avant ellipsis (approx. largeur de glyphe).
|
|
184
|
+
const maxChars = $derived(Math.max(2, Math.floor((layout.boxW - 10) / (fontSize * 0.58))));
|
|
185
|
+
|
|
186
|
+
const clip = (s: string, n: number) => (s.length > n ? `${s.slice(0, Math.max(1, n - 1))}…` : s);
|
|
187
|
+
|
|
188
|
+
const dataValueItems = $derived(
|
|
189
|
+
layout.boxes.map((b) => {
|
|
190
|
+
const parent = b.parentId != null ? layout.boxes.find((p) => p.id === b.parentId) : undefined;
|
|
191
|
+
return parent ? `${b.label} (${b.id}) → ${parent.label}` : `${b.label} (${b.id})`;
|
|
192
|
+
})
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
const classes = () => ["st-organizationChart", className].filter(Boolean).join(" ");
|
|
196
|
+
</script>
|
|
197
|
+
|
|
198
|
+
<div class={classes()}>
|
|
199
|
+
<div class="st-organizationChart__visual" role="img" aria-label={label}>
|
|
200
|
+
<svg
|
|
201
|
+
viewBox="0 0 {width} {height}"
|
|
202
|
+
preserveAspectRatio="xMidYMid meet"
|
|
203
|
+
width="100%"
|
|
204
|
+
height="100%"
|
|
205
|
+
focusable="false"
|
|
206
|
+
aria-hidden="true"
|
|
207
|
+
>
|
|
208
|
+
{#each layout.links as link (link.id)}
|
|
209
|
+
{@const midY = (link.y1 + link.y2) / 2}
|
|
210
|
+
<path
|
|
211
|
+
class="st-organizationChart__link"
|
|
212
|
+
d="M {link.x1} {link.y1} V {midY} H {link.x2} V {link.y2}"
|
|
213
|
+
fill="none"
|
|
214
|
+
/>
|
|
215
|
+
{/each}
|
|
216
|
+
|
|
217
|
+
{#each layout.boxes as box (box.id)}
|
|
218
|
+
<g class="st-organizationChart__node">
|
|
219
|
+
<rect
|
|
220
|
+
class="st-organizationChart__box st-organizationChart__box--{box.tone}"
|
|
221
|
+
x={box.x - layout.boxW / 2}
|
|
222
|
+
y={box.y}
|
|
223
|
+
width={layout.boxW}
|
|
224
|
+
height={BOX_H}
|
|
225
|
+
rx="6"
|
|
226
|
+
/>
|
|
227
|
+
<text
|
|
228
|
+
class="st-organizationChart__label"
|
|
229
|
+
x={box.x}
|
|
230
|
+
y={box.y + BOX_H / 2}
|
|
231
|
+
text-anchor="middle"
|
|
232
|
+
dominant-baseline="central"
|
|
233
|
+
style="font-size: {fontSize}px"
|
|
234
|
+
>
|
|
235
|
+
{clip(box.label, maxChars)}
|
|
236
|
+
</text>
|
|
237
|
+
</g>
|
|
238
|
+
{/each}
|
|
239
|
+
</svg>
|
|
240
|
+
</div>
|
|
241
|
+
|
|
242
|
+
<ChartDataList {label} items={dataValueItems} />
|
|
243
|
+
</div>
|
|
244
|
+
|
|
245
|
+
<style>
|
|
246
|
+
.st-organizationChart {
|
|
247
|
+
color: var(--st-semantic-text-secondary);
|
|
248
|
+
display: block;
|
|
249
|
+
font-family: inherit;
|
|
250
|
+
position: relative;
|
|
251
|
+
width: 100%;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.st-organizationChart svg,
|
|
255
|
+
.st-organizationChart__visual {
|
|
256
|
+
display: block;
|
|
257
|
+
overflow: visible;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.st-organizationChart__link {
|
|
261
|
+
stroke: var(--st-semantic-border-default, #cbd5e1);
|
|
262
|
+
stroke-width: 1.5;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.st-organizationChart__box {
|
|
266
|
+
stroke: var(--st-semantic-surface-default, #fff);
|
|
267
|
+
stroke-width: 1.5;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.st-organizationChart__box--category1 { fill: var(--st-semantic-data-category1); }
|
|
271
|
+
.st-organizationChart__box--category2 { fill: var(--st-semantic-data-category2); }
|
|
272
|
+
.st-organizationChart__box--category3 { fill: var(--st-semantic-data-category3); }
|
|
273
|
+
.st-organizationChart__box--category4 { fill: var(--st-semantic-data-category4); }
|
|
274
|
+
.st-organizationChart__box--category5 { fill: var(--st-semantic-data-category5); }
|
|
275
|
+
.st-organizationChart__box--category6 { fill: var(--st-semantic-data-category6); }
|
|
276
|
+
.st-organizationChart__box--category7 { fill: var(--st-semantic-data-category7); }
|
|
277
|
+
.st-organizationChart__box--category8 { fill: var(--st-semantic-data-category8); }
|
|
278
|
+
|
|
279
|
+
.st-organizationChart__label {
|
|
280
|
+
fill: var(--st-semantic-text-inverse, #fff);
|
|
281
|
+
font-weight: 600;
|
|
282
|
+
pointer-events: none;
|
|
283
|
+
}
|
|
284
|
+
</style>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type OrganizationChartTone = "category1" | "category2" | "category3" | "category4" | "category5" | "category6" | "category7" | "category8";
|
|
2
|
+
export type OrganizationChartNode = {
|
|
3
|
+
id: string;
|
|
4
|
+
parentId?: string | null;
|
|
5
|
+
label: string;
|
|
6
|
+
tone?: OrganizationChartTone;
|
|
7
|
+
};
|
|
8
|
+
type OrganizationChartProps = {
|
|
9
|
+
/** Nœuds plats : `parentId` null/absent = racine. Plusieurs racines acceptées. */
|
|
10
|
+
data: OrganizationChartNode[];
|
|
11
|
+
width?: number;
|
|
12
|
+
height?: number;
|
|
13
|
+
label: string;
|
|
14
|
+
class?: string;
|
|
15
|
+
};
|
|
16
|
+
declare const OrganizationChart: import("svelte").Component<OrganizationChartProps, {}, "">;
|
|
17
|
+
type OrganizationChart = ReturnType<typeof OrganizationChart>;
|
|
18
|
+
export default OrganizationChart;
|
|
19
|
+
//# sourceMappingURL=OrganizationChart.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OrganizationChart.svelte.d.ts","sourceRoot":"","sources":["../src/lib/OrganizationChart.svelte.ts"],"names":[],"mappings":"AAGE,MAAM,MAAM,qBAAqB,GAC7B,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,GACrD,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,CAAC;AAE1D,MAAM,MAAM,qBAAqB,GAAG;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,qBAAqB,CAAC;CAC9B,CAAC;AAMF,KAAK,sBAAsB,GAAG;IAC5B,kFAAkF;IAClF,IAAI,EAAE,qBAAqB,EAAE,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAgNJ,QAAA,MAAM,iBAAiB,4DAAwC,CAAC;AAChE,KAAK,iBAAiB,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAC9D,eAAe,iBAAiB,CAAC"}
|
|
@@ -130,14 +130,17 @@
|
|
|
130
130
|
|
|
131
131
|
.st-passwordInput--sm {
|
|
132
132
|
min-height: var(--st-component-control-smHeight, 2rem);
|
|
133
|
+
font-size: 0.8125rem;
|
|
133
134
|
}
|
|
134
135
|
|
|
135
136
|
.st-passwordInput--md {
|
|
136
137
|
min-height: var(--st-component-control-mdHeight, 2.5rem);
|
|
138
|
+
font-size: 0.875rem;
|
|
137
139
|
}
|
|
138
140
|
|
|
139
141
|
.st-passwordInput--lg {
|
|
140
142
|
min-height: var(--st-component-control-lgHeight, 3rem);
|
|
143
|
+
font-size: 1rem;
|
|
141
144
|
}
|
|
142
145
|
|
|
143
146
|
.st-passwordInput:hover:not(:has(input:disabled)) {
|