@konce-pt/angular 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,957 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, output, ViewEncapsulation, ChangeDetectionStrategy, Component, computed, signal } from '@angular/core';
3
+
4
+ function n$2(value) {
5
+ // Skraca zapis (mniejszy DOM) i unika notacji wykładniczej dla małych liczb.
6
+ return Number.isInteger(value) ? String(value) : value.toFixed(3);
7
+ }
8
+ /** Ścieżka odcinkowa (M/L). */
9
+ function linearPath(points) {
10
+ return points.map((p, i) => `${i === 0 ? 'M' : 'L'} ${n$2(p.x)} ${n$2(p.y)}`).join(' ');
11
+ }
12
+ /** Standardowa interpolacja sześcienna (Catmull-Rom przez punkty → krzywe Béziera). */
13
+ function cubicPath(points) {
14
+ if (points.length < 3)
15
+ return linearPath(points);
16
+ let d = `M ${n$2(points[0].x)} ${n$2(points[0].y)}`;
17
+ for (let i = 0; i < points.length - 1; i++) {
18
+ const p0 = points[i - 1] ?? points[i];
19
+ const p1 = points[i];
20
+ const p2 = points[i + 1];
21
+ const p3 = points[i + 2] ?? p2;
22
+ const c1x = p1.x + (p2.x - p0.x) / 6;
23
+ const c1y = p1.y + (p2.y - p0.y) / 6;
24
+ const c2x = p2.x - (p3.x - p1.x) / 6;
25
+ const c2y = p2.y - (p3.y - p1.y) / 6;
26
+ d += ` C ${n$2(c1x)} ${n$2(c1y)}, ${n$2(c2x)} ${n$2(c2y)}, ${n$2(p2.x)} ${n$2(p2.y)}`;
27
+ }
28
+ return d;
29
+ }
30
+ /**
31
+ * Monotoniczna interpolacja sześcienna (Fritsch–Carlson).
32
+ * Styczne przycinane tak, by krzywa nie przestrzelała ekstremów danych.
33
+ * Liczona względem indeksu punktu (równe odstępy w X, typowe dla osi kategorii).
34
+ */
35
+ function monotonePath(points) {
36
+ const size = points.length;
37
+ if (size < 3)
38
+ return linearPath(points);
39
+ const dx = [];
40
+ const dy = [];
41
+ const slope = [];
42
+ for (let i = 0; i < size - 1; i++) {
43
+ dx[i] = points[i + 1].x - points[i].x;
44
+ dy[i] = points[i + 1].y - points[i].y;
45
+ slope[i] = dy[i] / (dx[i] || 1);
46
+ }
47
+ // Styczne w węzłach (m).
48
+ const m = new Array(size);
49
+ m[0] = slope[0];
50
+ m[size - 1] = slope[size - 2];
51
+ for (let i = 1; i < size - 1; i++) {
52
+ if (slope[i - 1] * slope[i] <= 0) {
53
+ m[i] = 0; // lokalne ekstremum → styczna pozioma
54
+ }
55
+ else {
56
+ m[i] = (slope[i - 1] + slope[i]) / 2;
57
+ }
58
+ }
59
+ // Ograniczenie Fritsch–Carlson.
60
+ for (let i = 0; i < size - 1; i++) {
61
+ if (slope[i] === 0) {
62
+ m[i] = 0;
63
+ m[i + 1] = 0;
64
+ continue;
65
+ }
66
+ const a = m[i] / slope[i];
67
+ const b = m[i + 1] / slope[i];
68
+ const h = Math.hypot(a, b);
69
+ if (h > 3) {
70
+ const t = 3 / h;
71
+ m[i] = t * a * slope[i];
72
+ m[i + 1] = t * b * slope[i];
73
+ }
74
+ }
75
+ let d = `M ${n$2(points[0].x)} ${n$2(points[0].y)}`;
76
+ for (let i = 0; i < size - 1; i++) {
77
+ const c1x = points[i].x + dx[i] / 3;
78
+ const c1y = points[i].y + (m[i] * dx[i]) / 3;
79
+ const c2x = points[i + 1].x - dx[i] / 3;
80
+ const c2y = points[i + 1].y - (m[i + 1] * dx[i]) / 3;
81
+ d += ` C ${n$2(c1x)} ${n$2(c1y)}, ${n$2(c2x)} ${n$2(c2y)}, ${n$2(points[i + 1].x)} ${n$2(points[i + 1].y)}`;
82
+ }
83
+ return d;
84
+ }
85
+ /** Ścieżka linii dla wybranego trybu interpolacji. */
86
+ function linePath(points, mode = 'linear') {
87
+ if (points.length === 0)
88
+ return '';
89
+ if (points.length === 1)
90
+ return `M ${n$2(points[0].x)} ${n$2(points[0].y)}`;
91
+ switch (mode) {
92
+ case 'cubic':
93
+ return cubicPath(points);
94
+ case 'cubic-monotonic':
95
+ return monotonePath(points);
96
+ default:
97
+ return linearPath(points);
98
+ }
99
+ }
100
+ /** Ścieżka obszaru pod linią (domknięta do `baselineY`). */
101
+ function areaPath(points, baselineY, mode = 'linear') {
102
+ if (points.length === 0)
103
+ return '';
104
+ const top = linePath(points, mode);
105
+ const first = points[0];
106
+ const last = points[points.length - 1];
107
+ return `${top} L ${n$2(last.x)} ${n$2(baselineY)} L ${n$2(first.x)} ${n$2(baselineY)} Z`;
108
+ }
109
+
110
+ /*
111
+ * Skalowanie i „ładne" podziałki osi (auto-skala).
112
+ * Wszystkie funkcje czyste — bez stanu i DOM.
113
+ */
114
+ /** Zakres [min, max] tablicy liczb (0..0 dla pustej). */
115
+ function extent(values) {
116
+ if (values.length === 0)
117
+ return [0, 0];
118
+ let min = values[0];
119
+ let max = values[0];
120
+ for (const v of values) {
121
+ if (v < min)
122
+ min = v;
123
+ if (v > max)
124
+ max = v;
125
+ }
126
+ return [min, max];
127
+ }
128
+ /** Interpolacja liniowa domeny → zakresu (np. wartość → współrzędna piksela). */
129
+ function linearScale(domainMin, domainMax, rangeMin, rangeMax) {
130
+ const dSpan = domainMax - domainMin || 1;
131
+ const rSpan = rangeMax - rangeMin;
132
+ return (value) => rangeMin + ((value - domainMin) / dSpan) * rSpan;
133
+ }
134
+ /** Zaokrągla liczbę do „ładnej" wartości (1/2/5 × 10ⁿ). */
135
+ function niceNum(range, round) {
136
+ const exponent = Math.floor(Math.log10(range || 1));
137
+ const fraction = (range || 1) / 10 ** exponent;
138
+ return niceFraction(fraction, round) * 10 ** exponent;
139
+ }
140
+ /** Mantysa 1/2/5/10 — do najbliższej (`round`) albo w górę. */
141
+ function niceFraction(fraction, round) {
142
+ if (round) {
143
+ if (fraction < 1.5)
144
+ return 1;
145
+ if (fraction < 3)
146
+ return 2;
147
+ if (fraction < 7)
148
+ return 5;
149
+ return 10;
150
+ }
151
+ if (fraction <= 1)
152
+ return 1;
153
+ if (fraction <= 2)
154
+ return 2;
155
+ if (fraction <= 5)
156
+ return 5;
157
+ return 10;
158
+ }
159
+ /**
160
+ * „Ładna" skala obejmująca [dataMin, dataMax] z ~`maxTicks` podziałkami.
161
+ * Wynikowy zakres jest rozszerzony do równych kroków; zawsze zawiera 0,
162
+ * gdy dane są jednoznakowe (typowe dla słupków/linii wartościowych).
163
+ */
164
+ function niceScale(dataMin, dataMax, maxTicks = 5) {
165
+ let lo = dataMin;
166
+ let hi = dataMax;
167
+ if (lo > 0)
168
+ lo = 0;
169
+ if (hi < 0)
170
+ hi = 0;
171
+ if (lo === hi) {
172
+ hi = lo + 1;
173
+ }
174
+ const range = niceNum(hi - lo, false);
175
+ const step = niceNum(range / Math.max(1, maxTicks - 1), true);
176
+ const niceMin = Math.floor(lo / step) * step;
177
+ const niceMax = Math.ceil(hi / step) * step;
178
+ const ticks = [];
179
+ // +1e-9: zabezpieczenie przed błędami zmiennoprzecinkowymi na krańcu pętli.
180
+ for (let t = niceMin; t <= niceMax + 1e-9; t += step) {
181
+ ticks.push(Math.abs(t) < 1e-9 ? 0 : t);
182
+ }
183
+ return { min: niceMin, max: niceMax, step, ticks };
184
+ }
185
+ /**
186
+ * Skala pasmowa: dzieli [rangeStart, rangeEnd] na `count` równych pasm
187
+ * z marginesem `padding` (0..1 udziału pasma) po bokach i między pasmami.
188
+ */
189
+ function bandScale(count, rangeStart, rangeEnd, padding = 0.1) {
190
+ const span = rangeEnd - rangeStart;
191
+ const step = count > 0 ? span / count : span;
192
+ const bandwidth = step * (1 - padding);
193
+ const offset = (step - bandwidth) / 2;
194
+ return {
195
+ bandwidth,
196
+ start: (index) => rangeStart + index * step + offset,
197
+ center: (index) => rangeStart + index * step + step / 2,
198
+ };
199
+ }
200
+
201
+ /*
202
+ * Geometria wykresu słupkowego (pionowego i poziomego) z zaokrągleniem rogów.
203
+ * Wspiera wiele serii (grupowanie obok siebie w obrębie kategorii).
204
+ */
205
+ function n$1(value) {
206
+ return Number.isInteger(value) ? String(value) : value.toFixed(3);
207
+ }
208
+ /** Prostokąt z zaokrąglonymi rogami tylko po wybranych stronach. */
209
+ function roundedRect(x, y, w, h, r, corners) {
210
+ const radius = Math.max(0, Math.min(r, w / 2, h / 2));
211
+ const tl = corners.tl ? radius : 0;
212
+ const tr = corners.tr ? radius : 0;
213
+ const br = corners.br ? radius : 0;
214
+ const bl = corners.bl ? radius : 0;
215
+ return [
216
+ `M ${n$1(x + tl)} ${n$1(y)}`,
217
+ `H ${n$1(x + w - tr)}`,
218
+ tr ? `A ${n$1(tr)} ${n$1(tr)} 0 0 1 ${n$1(x + w)} ${n$1(y + tr)}` : '',
219
+ `V ${n$1(y + h - br)}`,
220
+ br ? `A ${n$1(br)} ${n$1(br)} 0 0 1 ${n$1(x + w - br)} ${n$1(y + h)}` : '',
221
+ `H ${n$1(x + bl)}`,
222
+ bl ? `A ${n$1(bl)} ${n$1(bl)} 0 0 1 ${n$1(x)} ${n$1(y + h - bl)}` : '',
223
+ `V ${n$1(y + tl)}`,
224
+ tl ? `A ${n$1(tl)} ${n$1(tl)} 0 0 1 ${n$1(x + tl)} ${n$1(y)}` : '',
225
+ 'Z',
226
+ ]
227
+ .filter(Boolean)
228
+ .join(' ');
229
+ }
230
+ /**
231
+ * Wylicza słupki dla obszaru `plot`.
232
+ * @param seriesData wartości[serie][kategorie]
233
+ * @param plot obszar rysowania {x,y,width,height}
234
+ * @param orientation pionowa (rośnie w górę) lub pozioma (rośnie w prawo)
235
+ * @param borderRadius promień zaokrąglenia w px
236
+ */
237
+ function barLayout(seriesData, categoryCount, plot, orientation, borderRadius) {
238
+ let min = 0;
239
+ let max = 0;
240
+ for (const row of seriesData) {
241
+ for (const v of row) {
242
+ if (v < min)
243
+ min = v;
244
+ if (v > max)
245
+ max = v;
246
+ }
247
+ }
248
+ const scale = niceScale(min, max, 5);
249
+ const seriesCount = seriesData.length || 1;
250
+ const bars = [];
251
+ if (orientation === 'vertical') {
252
+ const value = linearScale(scale.min, scale.max, plot.y + plot.height, plot.y);
253
+ const baseline = value(0);
254
+ const cat = bandScale(categoryCount, plot.x, plot.x + plot.width, 0.2);
255
+ const group = bandScale(seriesCount, 0, cat.bandwidth, 0.1);
256
+ seriesData.forEach((values, s) => {
257
+ for (let c = 0; c < categoryCount; c++) {
258
+ const v = values[c] ?? 0;
259
+ const x = cat.start(c) + group.start(s);
260
+ const w = group.bandwidth;
261
+ const yv = value(v);
262
+ const top = Math.min(baseline, yv);
263
+ const h = Math.abs(yv - baseline);
264
+ const up = v >= 0;
265
+ bars.push({
266
+ rect: { x, y: top, width: w, height: h },
267
+ path: roundedRect(x, top, w, h, borderRadius, up ? { tl: true, tr: true } : { bl: true, br: true }),
268
+ seriesIndex: s,
269
+ categoryIndex: c,
270
+ value: v,
271
+ });
272
+ }
273
+ });
274
+ return { bars, baseline, scale, ticks: scale.ticks.map((t) => ({ value: t, pos: value(t) })) };
275
+ }
276
+ // horizontal
277
+ const value = linearScale(scale.min, scale.max, plot.x, plot.x + plot.width);
278
+ const baseline = value(0);
279
+ const cat = bandScale(categoryCount, plot.y, plot.y + plot.height, 0.2);
280
+ const group = bandScale(seriesCount, 0, cat.bandwidth, 0.1);
281
+ seriesData.forEach((values, s) => {
282
+ for (let c = 0; c < categoryCount; c++) {
283
+ const v = values[c] ?? 0;
284
+ const y = cat.start(c) + group.start(s);
285
+ const h = group.bandwidth;
286
+ const xv = value(v);
287
+ const left = Math.min(baseline, xv);
288
+ const w = Math.abs(xv - baseline);
289
+ const right = v >= 0;
290
+ bars.push({
291
+ rect: { x: left, y, width: w, height: h },
292
+ path: roundedRect(left, y, w, h, borderRadius, right ? { tr: true, br: true } : { tl: true, bl: true }),
293
+ seriesIndex: s,
294
+ categoryIndex: c,
295
+ value: v,
296
+ });
297
+ }
298
+ });
299
+ return { bars, baseline, scale, ticks: scale.ticks.map((t) => ({ value: t, pos: value(t) })) };
300
+ }
301
+
302
+ const HALF_PI = Math.PI / 2;
303
+ function n(value) {
304
+ return Number.isInteger(value) ? String(value) : value.toFixed(3);
305
+ }
306
+ /** Punkt na okręgu dla kąta (0 = góra, rośnie zgodnie z zegarem). */
307
+ function polarToCartesian(cx, cy, radius, angle) {
308
+ const a = angle - HALF_PI;
309
+ return { x: cx + radius * Math.cos(a), y: cy + radius * Math.sin(a) };
310
+ }
311
+ /**
312
+ * Ścieżka wycinka pierścienia od `startAngle` do `endAngle`.
313
+ * `innerRadius = 0` → pełny wycinek koła (pie); > 0 → pierścień (doughnut).
314
+ */
315
+ function arcPath(cx, cy, outerRadius, innerRadius, startAngle, endAngle) {
316
+ const sweep = endAngle - startAngle;
317
+ const largeArc = sweep > Math.PI ? 1 : 0;
318
+ // Pełny okrąg (np. pojedynczy wycinek 100%) — rozbijamy na dwa łuki.
319
+ if (sweep >= Math.PI * 2 - 1e-6) {
320
+ const outer = fullCircle(cx, cy, outerRadius, 1);
321
+ if (innerRadius <= 0)
322
+ return outer;
323
+ return `${outer} ${fullCircle(cx, cy, innerRadius, 0)}`;
324
+ }
325
+ const oStart = polarToCartesian(cx, cy, outerRadius, startAngle);
326
+ const oEnd = polarToCartesian(cx, cy, outerRadius, endAngle);
327
+ if (innerRadius <= 0) {
328
+ return [
329
+ `M ${n(cx)} ${n(cy)}`,
330
+ `L ${n(oStart.x)} ${n(oStart.y)}`,
331
+ `A ${n(outerRadius)} ${n(outerRadius)} 0 ${largeArc} 1 ${n(oEnd.x)} ${n(oEnd.y)}`,
332
+ 'Z',
333
+ ].join(' ');
334
+ }
335
+ const iStart = polarToCartesian(cx, cy, innerRadius, endAngle);
336
+ const iEnd = polarToCartesian(cx, cy, innerRadius, startAngle);
337
+ return [
338
+ `M ${n(oStart.x)} ${n(oStart.y)}`,
339
+ `A ${n(outerRadius)} ${n(outerRadius)} 0 ${largeArc} 1 ${n(oEnd.x)} ${n(oEnd.y)}`,
340
+ `L ${n(iStart.x)} ${n(iStart.y)}`,
341
+ `A ${n(innerRadius)} ${n(innerRadius)} 0 ${largeArc} 0 ${n(iEnd.x)} ${n(iEnd.y)}`,
342
+ 'Z',
343
+ ].join(' ');
344
+ }
345
+ /** Pełny okrąg jako dwa półłuki (sweepFlag steruje kierunkiem — dla wycięcia). */
346
+ function fullCircle(cx, cy, r, sweepFlag) {
347
+ const top = polarToCartesian(cx, cy, r, 0);
348
+ const bottom = polarToCartesian(cx, cy, r, Math.PI);
349
+ return [
350
+ `M ${n(top.x)} ${n(top.y)}`,
351
+ `A ${n(r)} ${n(r)} 0 1 ${sweepFlag} ${n(bottom.x)} ${n(bottom.y)}`,
352
+ `A ${n(r)} ${n(r)} 0 1 ${sweepFlag} ${n(top.x)} ${n(top.y)}`,
353
+ 'Z',
354
+ ].join(' ');
355
+ }
356
+ /**
357
+ * Wycinki pie/doughnut: kąt proporcjonalny do wartości (Σ = 2π).
358
+ * `innerRadius` w px (0 dla pie).
359
+ */
360
+ function pieSlices(values, cx, cy, outerRadius, innerRadius = 0) {
361
+ const total = values.reduce((a, v) => a + Math.max(0, v), 0);
362
+ if (total <= 0)
363
+ return [];
364
+ const slices = [];
365
+ let angle = 0;
366
+ for (const value of values) {
367
+ const sweep = (Math.max(0, value) / total) * Math.PI * 2;
368
+ const start = angle;
369
+ const end = angle + sweep;
370
+ slices.push({
371
+ path: arcPath(cx, cy, outerRadius, innerRadius, start, end),
372
+ midAngle: (start + end) / 2,
373
+ startAngle: start,
374
+ endAngle: end,
375
+ value,
376
+ });
377
+ angle = end;
378
+ }
379
+ return slices;
380
+ }
381
+ /**
382
+ * Wycinki polar area: równy kąt (2π/N), promień proporcjonalny do wartości.
383
+ * `maxValue` skaluje promień do `outerRadius`.
384
+ */
385
+ function polarSlices(values, cx, cy, outerRadius, maxValue = 1) {
386
+ const count = values.length;
387
+ if (count === 0)
388
+ return [];
389
+ const sweep = (Math.PI * 2) / count;
390
+ return values.map((value, i) => {
391
+ const start = i * sweep;
392
+ const end = start + sweep;
393
+ const r = (Math.max(0, value) / maxValue) * outerRadius;
394
+ return {
395
+ path: arcPath(cx, cy, r, 0, start, end),
396
+ midAngle: (start + end) / 2,
397
+ startAngle: start,
398
+ endAngle: end,
399
+ value,
400
+ };
401
+ });
402
+ }
403
+
404
+ /*
405
+ * Geometria wykresu radarowego: osie promieniowe + wielokąty serii.
406
+ */
407
+ /** Siatka radaru: `levels` koncentrycznych wielokątów + osie do etykiet. */
408
+ function radarGrid(count, cx, cy, radius, levels = 4) {
409
+ const axes = [];
410
+ const step = (Math.PI * 2) / Math.max(1, count);
411
+ for (let i = 0; i < count; i++) {
412
+ const angle = i * step;
413
+ axes.push({ angle, labelIndex: i, end: polarToCartesian(cx, cy, radius, angle) });
414
+ }
415
+ const rings = [];
416
+ for (let l = 1; l <= levels; l++) {
417
+ const r = (radius * l) / levels;
418
+ rings.push(polygonPath(axes.map((a) => polarToCartesian(cx, cy, r, a.angle))));
419
+ }
420
+ return { rings, axes };
421
+ }
422
+ /** Punkty wielokąta serii (wartość → promień, skalowane do `maxValue`). */
423
+ function radarPoints(values, cx, cy, radius, maxValue) {
424
+ const count = values.length;
425
+ const step = (Math.PI * 2) / Math.max(1, count);
426
+ const max = maxValue || 1;
427
+ return values.map((v, i) => polarToCartesian(cx, cy, (Math.max(0, v) / max) * radius, i * step));
428
+ }
429
+ /** Zamknięta ścieżka wielokąta łącząca punkty. */
430
+ function polygonPath(points) {
431
+ if (points.length === 0)
432
+ return '';
433
+ const nf = (v) => (Number.isInteger(v) ? String(v) : v.toFixed(3));
434
+ return points.map((p, i) => `${i === 0 ? 'M' : 'L'} ${nf(p.x)} ${nf(p.y)}`).join(' ') + ' Z';
435
+ }
436
+
437
+ /** Obszar rysowania po odjęciu marginesów od pełnego viewBoxa. */
438
+ function plotArea(width, height, insets) {
439
+ return {
440
+ x: insets.left,
441
+ y: insets.top,
442
+ width: Math.max(0, width - insets.left - insets.right),
443
+ height: Math.max(0, height - insets.top - insets.bottom),
444
+ };
445
+ }
446
+ /** Środek i promień dla wykresów promieniowych (pie/doughnut/polar/radar). */
447
+ function radialArea(width, height, padding) {
448
+ const cx = width / 2;
449
+ const cy = height / 2;
450
+ const radius = Math.max(0, Math.min(width, height) / 2 - padding);
451
+ return { cx, cy, radius };
452
+ }
453
+
454
+ /*
455
+ * Domyślna paleta kolorów wykresów — oparta o semantyczne tokeny Koncept UI.
456
+ * Rozszerzona o dodatkowe odcienie skali, by obsłużyć wiele serii/wycinków.
457
+ */
458
+ /** Kolejność kolorów dla kolejnych serii / wycinków. */
459
+ const KPT_CHART_PALETTE = [
460
+ 'var(--kpt-color-primary)',
461
+ 'var(--kpt-color-info)',
462
+ 'var(--kpt-color-success)',
463
+ 'var(--kpt-color-warning)',
464
+ 'var(--kpt-color-danger)',
465
+ 'var(--kpt-color-blue-700)',
466
+ 'var(--kpt-color-green-700)',
467
+ 'var(--kpt-color-amber-700)',
468
+ 'var(--kpt-color-red-700)',
469
+ 'var(--kpt-color-neutral-500)',
470
+ ];
471
+ /** Kolor serii/wycinka: jawny z danych albo z palety (cyklicznie). */
472
+ function seriesColor(explicit, index, palette = KPT_CHART_PALETTE) {
473
+ return explicit ?? palette[index % palette.length];
474
+ }
475
+
476
+ /*
477
+ * Lekki formatter liczb dla etykiet osi i tooltipów.
478
+ * Framework-free; korzysta z wbudowanego Intl (dostępny w przeglądarce i Node).
479
+ */
480
+ /** Formatuje liczbę zwięźle (do 2 miejsc, bez zer końcowych). */
481
+ function formatNumber(value, locale) {
482
+ if (!Number.isFinite(value))
483
+ return '';
484
+ return new Intl.NumberFormat(locale, { maximumFractionDigits: 2 }).format(value);
485
+ }
486
+ /** Zaokrągla do skończonej liczby miejsc (do etykiet osi). */
487
+ function round(value, digits = 0) {
488
+ const f = 10 ** digits;
489
+ return Math.round(value * f) / f;
490
+ }
491
+
492
+ /**
493
+ * Legenda wykresu — klikalne elementy (toggle serii), podświetlanie na hover,
494
+ * pozycjonowanie, wyrównanie, styl punktu i opcjonalny tytuł.
495
+ * Element wewnętrzny biblioteki — nie jest częścią publicznego API.
496
+ */
497
+ class KptChartLegend {
498
+ items = input([], /* @ts-ignore */
499
+ ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
500
+ position = input('bottom', /* @ts-ignore */
501
+ ...(ngDevMode ? [{ debugName: "position" }] : /* istanbul ignore next */ []));
502
+ align = input('center', /* @ts-ignore */
503
+ ...(ngDevMode ? [{ debugName: "align" }] : /* istanbul ignore next */ []));
504
+ pointStyle = input('circle', /* @ts-ignore */
505
+ ...(ngDevMode ? [{ debugName: "pointStyle" }] : /* istanbul ignore next */ []));
506
+ title = input(undefined, /* @ts-ignore */
507
+ ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
508
+ /** Kliknięcie elementu — przełącz widoczność serii/wycinka (klucz). */
509
+ toggle = output();
510
+ /** Wejście/wyjście kursora — podświetl serię (klucz) lub wyczyść (null). */
511
+ highlight = output();
512
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: KptChartLegend, deps: [], target: i0.ɵɵFactoryTarget.Component });
513
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: KptChartLegend, isStandalone: true, selector: "kpt-chart-legend", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null }, pointStyle: { classPropertyName: "pointStyle", publicName: "pointStyle", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { toggle: "toggle", highlight: "highlight" }, host: { classAttribute: "kpt-chart-legend-host" }, ngImport: i0, template: `
514
+ <div class="kpt-chart__legend" [attr.data-position]="position()" [attr.data-align]="align()">
515
+ @if (title()?.display && title()?.text) {
516
+ <div class="kpt-chart__legend-title" [attr.data-align]="title()?.position ?? 'start'">{{ title()?.text }}</div>
517
+ }
518
+ <ul class="kpt-chart__legend-list">
519
+ @for (item of items(); track item.key) {
520
+ <li>
521
+ <button
522
+ type="button"
523
+ class="kpt-chart__legend-item"
524
+ [class.kpt-chart__legend-item--hidden]="item.hidden"
525
+ (click)="toggle.emit(item.key)"
526
+ (pointerenter)="highlight.emit(item.key)"
527
+ (pointerleave)="highlight.emit(null)"
528
+ >
529
+ <span class="kpt-chart__legend-marker" [attr.data-style]="pointStyle()" [style.--kpt-chart-legend-color]="item.color"></span>
530
+ <span class="kpt-chart__legend-label">{{ item.name }}</span>
531
+ </button>
532
+ </li>
533
+ }
534
+ </ul>
535
+ </div>
536
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
537
+ }
538
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: KptChartLegend, decorators: [{
539
+ type: Component,
540
+ args: [{
541
+ selector: 'kpt-chart-legend',
542
+ changeDetection: ChangeDetectionStrategy.OnPush,
543
+ encapsulation: ViewEncapsulation.None,
544
+ template: `
545
+ <div class="kpt-chart__legend" [attr.data-position]="position()" [attr.data-align]="align()">
546
+ @if (title()?.display && title()?.text) {
547
+ <div class="kpt-chart__legend-title" [attr.data-align]="title()?.position ?? 'start'">{{ title()?.text }}</div>
548
+ }
549
+ <ul class="kpt-chart__legend-list">
550
+ @for (item of items(); track item.key) {
551
+ <li>
552
+ <button
553
+ type="button"
554
+ class="kpt-chart__legend-item"
555
+ [class.kpt-chart__legend-item--hidden]="item.hidden"
556
+ (click)="toggle.emit(item.key)"
557
+ (pointerenter)="highlight.emit(item.key)"
558
+ (pointerleave)="highlight.emit(null)"
559
+ >
560
+ <span class="kpt-chart__legend-marker" [attr.data-style]="pointStyle()" [style.--kpt-chart-legend-color]="item.color"></span>
561
+ <span class="kpt-chart__legend-label">{{ item.name }}</span>
562
+ </button>
563
+ </li>
564
+ }
565
+ </ul>
566
+ </div>
567
+ `,
568
+ host: { class: 'kpt-chart-legend-host' },
569
+ }]
570
+ }], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], align: [{ type: i0.Input, args: [{ isSignal: true, alias: "align", required: false }] }], pointStyle: [{ type: i0.Input, args: [{ isSignal: true, alias: "pointStyle", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], toggle: [{ type: i0.Output, args: ["toggle"] }], highlight: [{ type: i0.Output, args: ["highlight"] }] } });
571
+
572
+ /**
573
+ * Dymek (tooltip) wykresu — pozycjonowany procentowo względem kontenera
574
+ * (niezależnie od skali SVG). Zabezpieczony przed wyjściem poza krawędzie.
575
+ * Element wewnętrzny biblioteki — nie jest częścią publicznego API.
576
+ */
577
+ class KptChartTooltip {
578
+ state = input(null, /* @ts-ignore */
579
+ ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
580
+ // Utrzymuje dymek w granicach kontenera (margines 6%).
581
+ clampX = computed(() => {
582
+ const t = this.state();
583
+ if (!t)
584
+ return 50;
585
+ return Math.min(94, Math.max(6, t.x));
586
+ }, /* @ts-ignore */
587
+ ...(ngDevMode ? [{ debugName: "clampX" }] : /* istanbul ignore next */ []));
588
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: KptChartTooltip, deps: [], target: i0.ɵɵFactoryTarget.Component });
589
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: KptChartTooltip, isStandalone: true, selector: "kpt-chart-tooltip", inputs: { state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "kpt-chart-tooltip-host" }, ngImport: i0, template: `
590
+ @if (state(); as t) {
591
+ <div class="kpt-chart__tooltip" [class.kpt-chart__tooltip--flip]="t.y < 22" [style.left.%]="clampX()" [style.top.%]="t.y" role="tooltip">
592
+ @if (t.label) {
593
+ <div class="kpt-chart__tooltip-title">{{ t.label }}</div>
594
+ }
595
+ @for (row of t.rows; track $index) {
596
+ <div class="kpt-chart__tooltip-row">
597
+ <span class="kpt-chart__tooltip-dot" [style.background]="row.color"></span>
598
+ <span class="kpt-chart__tooltip-name">{{ row.name }}</span>
599
+ <span class="kpt-chart__tooltip-val">{{ row.value }}</span>
600
+ </div>
601
+ }
602
+ </div>
603
+ }
604
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
605
+ }
606
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: KptChartTooltip, decorators: [{
607
+ type: Component,
608
+ args: [{
609
+ selector: 'kpt-chart-tooltip',
610
+ changeDetection: ChangeDetectionStrategy.OnPush,
611
+ encapsulation: ViewEncapsulation.None,
612
+ template: `
613
+ @if (state(); as t) {
614
+ <div class="kpt-chart__tooltip" [class.kpt-chart__tooltip--flip]="t.y < 22" [style.left.%]="clampX()" [style.top.%]="t.y" role="tooltip">
615
+ @if (t.label) {
616
+ <div class="kpt-chart__tooltip-title">{{ t.label }}</div>
617
+ }
618
+ @for (row of t.rows; track $index) {
619
+ <div class="kpt-chart__tooltip-row">
620
+ <span class="kpt-chart__tooltip-dot" [style.background]="row.color"></span>
621
+ <span class="kpt-chart__tooltip-name">{{ row.name }}</span>
622
+ <span class="kpt-chart__tooltip-val">{{ row.value }}</span>
623
+ </div>
624
+ }
625
+ </div>
626
+ }
627
+ `,
628
+ host: { class: 'kpt-chart-tooltip-host' },
629
+ }]
630
+ }], propDecorators: { state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: false }] }] } });
631
+
632
+ const VERTICAL_INSETS = { top: 12, right: 16, bottom: 28, left: 46 };
633
+ const HORIZONTAL_INSETS = { top: 12, right: 16, bottom: 28, left: 76 };
634
+ /**
635
+ * Wykres SVG w pełni konfigurowalny przez obiekt `config`.
636
+ * Typy: line, bar, pie, doughnut, polarArea, radar. Sygnały + zoneless,
637
+ * natywne renderowanie SVG (bez manipulacji DOM). Rdzeń obliczeń: `../core`.
638
+ *
639
+ * @example
640
+ * <kpt-chart [config]="{ type: 'line', labels: ['Sty','Lut','Mar'],
641
+ * series: [{ name: 'Sprzedaż', data: [12, 19, 7] }] }" />
642
+ */
643
+ class KptChart {
644
+ config = input.required(/* @ts-ignore */
645
+ ...(ngDevMode ? [{ debugName: "config" }] : /* istanbul ignore next */ []));
646
+ /** Szerokość viewBoxa (jednostki SVG). Wykres skaluje się do kontenera. */
647
+ width = input(600, /* @ts-ignore */
648
+ ...(ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []));
649
+ /** Wysokość viewBoxa (jednostki SVG). */
650
+ height = input(360, /* @ts-ignore */
651
+ ...(ngDevMode ? [{ debugName: "height" }] : /* istanbul ignore next */ []));
652
+ // --- Stan interakcji ---
653
+ hidden = signal(new Set(), /* @ts-ignore */
654
+ ...(ngDevMode ? [{ debugName: "hidden" }] : /* istanbul ignore next */ []));
655
+ highlighted = signal(null, /* @ts-ignore */
656
+ ...(ngDevMode ? [{ debugName: "highlighted" }] : /* istanbul ignore next */ []));
657
+ tooltip = signal(null, /* @ts-ignore */
658
+ ...(ngDevMode ? [{ debugName: "tooltip" }] : /* istanbul ignore next */ []));
659
+ /** Indeks kategorii pod kursorem (dla prowadnicy crosshair w wykresie liniowym). */
660
+ activeIndex = signal(null, /* @ts-ignore */
661
+ ...(ngDevMode ? [{ debugName: "activeIndex" }] : /* istanbul ignore next */ []));
662
+ // --- Znormalizowana konfiguracja ---
663
+ type = computed(() => this.config().type, /* @ts-ignore */
664
+ ...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
665
+ anim = computed(() => {
666
+ const a = this.config().animation ?? {};
667
+ return { enabled: a.enabled ?? true, duration: a.duration ?? 400, delay: a.delay ?? 0, stagger: a.stagger ?? 60 };
668
+ }, /* @ts-ignore */
669
+ ...(ngDevMode ? [{ debugName: "anim" }] : /* istanbul ignore next */ []));
670
+ legendOpts = computed(() => {
671
+ const l = this.config().legend ?? {};
672
+ return {
673
+ display: l.display ?? true,
674
+ position: l.position ?? 'bottom',
675
+ align: l.align ?? 'center',
676
+ pointStyle: l.pointStyle ?? 'circle',
677
+ title: l.title,
678
+ };
679
+ }, /* @ts-ignore */
680
+ ...(ngDevMode ? [{ debugName: "legendOpts" }] : /* istanbul ignore next */ []));
681
+ titleOpts = computed(() => this.config().title, /* @ts-ignore */
682
+ ...(ngDevMode ? [{ debugName: "titleOpts" }] : /* istanbul ignore next */ []));
683
+ subtitleOpts = computed(() => this.config().subtitle, /* @ts-ignore */
684
+ ...(ngDevMode ? [{ debugName: "subtitleOpts" }] : /* istanbul ignore next */ []));
685
+ titleStyle = computed(() => styleFor(this.titleOpts()?.align), /* @ts-ignore */
686
+ ...(ngDevMode ? [{ debugName: "titleStyle" }] : /* istanbul ignore next */ []));
687
+ subtitleStyle = computed(() => styleFor(this.subtitleOpts()?.align), /* @ts-ignore */
688
+ ...(ngDevMode ? [{ debugName: "subtitleStyle" }] : /* istanbul ignore next */ []));
689
+ // --- Legenda ---
690
+ legendItems = computed(() => {
691
+ const c = this.config();
692
+ const h = this.hidden();
693
+ if (c.type === 'pie' || c.type === 'doughnut' || c.type === 'polarArea') {
694
+ return c.labels.map((label, i) => {
695
+ const key = 'cat:' + i;
696
+ return { key, name: label, color: seriesColor(c.series[0]?.color, i), hidden: h.has(key) };
697
+ });
698
+ }
699
+ return c.series.map((s, i) => {
700
+ const key = 'ser:' + s.name;
701
+ return { key, name: s.name, color: seriesColor(s.color, i), hidden: h.has(key) };
702
+ });
703
+ }, /* @ts-ignore */
704
+ ...(ngDevMode ? [{ debugName: "legendItems" }] : /* istanbul ignore next */ []));
705
+ // --- Wykres liniowy ---
706
+ lineVm = computed(() => {
707
+ const c = this.config();
708
+ if (c.type !== 'line')
709
+ return null;
710
+ const h = this.hidden();
711
+ const active = c.series.map((s, i) => ({ s, i })).filter(({ s }) => !h.has('ser:' + s.name));
712
+ const values = [];
713
+ for (const { s } of active)
714
+ values.push(...s.data);
715
+ const [mn, mx] = extent(values.length ? values : [0]);
716
+ const scale = niceScale(Math.min(0, mn), Math.max(0, mx), 5);
717
+ const plot = plotArea(this.width(), this.height(), VERTICAL_INSETS);
718
+ const yScale = linearScale(scale.min, scale.max, plot.y + plot.height, plot.y);
719
+ const n = c.labels.length;
720
+ const xAt = (i) => (n > 1 ? plot.x + (i / (n - 1)) * plot.width : plot.x + plot.width / 2);
721
+ const baseline = yScale(0);
722
+ const series = active.map(({ s, i }) => {
723
+ const pts = s.data.map((v, idx) => ({ x: xAt(idx), y: yScale(v), value: v, label: c.labels[idx] ?? '' }));
724
+ const color = seriesColor(s.color, i);
725
+ return {
726
+ key: 'ser:' + s.name,
727
+ name: s.name,
728
+ color,
729
+ path: linePath(pts, c.interpolation ?? 'linear'),
730
+ area: c.area ? areaPath(pts, baseline, c.interpolation ?? 'linear') : null,
731
+ points: pts,
732
+ };
733
+ });
734
+ return {
735
+ plot,
736
+ series,
737
+ grid: scale.ticks.map((t) => ({ x1: plot.x, y1: yScale(t), x2: plot.x + plot.width, y2: yScale(t) })),
738
+ valueLabels: scale.ticks.map((t) => ({ x: plot.x - 8, y: yScale(t) + 3, text: formatNumber(t), anchor: 'end' })),
739
+ catLabels: c.labels.map((l, idx) => ({ x: xAt(idx), y: plot.y + plot.height + 18, text: l, anchor: 'middle' })),
740
+ showPoints: c.points ?? true,
741
+ };
742
+ }, /* @ts-ignore */
743
+ ...(ngDevMode ? [{ debugName: "lineVm" }] : /* istanbul ignore next */ []));
744
+ // --- Wykres słupkowy ---
745
+ barVm = computed(() => {
746
+ const c = this.config();
747
+ if (c.type !== 'bar')
748
+ return null;
749
+ const h = this.hidden();
750
+ const orientation = c.orientation ?? 'vertical';
751
+ const active = c.series.map((s, i) => ({ s, i })).filter(({ s }) => !h.has('ser:' + s.name));
752
+ const plot = plotArea(this.width(), this.height(), orientation === 'vertical' ? VERTICAL_INSETS : HORIZONTAL_INSETS);
753
+ const layout = barLayout(active.map(({ s }) => s.data), c.labels.length, plot, orientation, c.borderRadius ?? 4);
754
+ const colors = active.map(({ s, i }) => seriesColor(s.color, i));
755
+ const names = active.map(({ s }) => s.name);
756
+ const bars = layout.bars.map((b, order) => {
757
+ const tipX = orientation === 'vertical' ? b.rect.x + b.rect.width / 2 : b.rect.x + b.rect.width;
758
+ const tipY = orientation === 'vertical' ? b.rect.y : b.rect.y + b.rect.height / 2;
759
+ return {
760
+ key: 'ser:' + names[b.seriesIndex],
761
+ path: b.path,
762
+ color: colors[b.seriesIndex],
763
+ name: names[b.seriesIndex],
764
+ label: c.labels[b.categoryIndex] ?? '',
765
+ value: b.value,
766
+ tipX,
767
+ tipY,
768
+ index: order,
769
+ };
770
+ });
771
+ const cat = orientation === 'vertical'
772
+ ? bandScale(c.labels.length, plot.x, plot.x + plot.width, 0.2)
773
+ : bandScale(c.labels.length, plot.y, plot.y + plot.height, 0.2);
774
+ if (orientation === 'vertical') {
775
+ return {
776
+ orientation,
777
+ bars,
778
+ grid: layout.ticks.map((t) => ({ x1: plot.x, y1: t.pos, x2: plot.x + plot.width, y2: t.pos })),
779
+ valueLabels: layout.ticks.map((t) => ({ x: plot.x - 8, y: t.pos + 3, text: formatNumber(t.value), anchor: 'end' })),
780
+ catLabels: c.labels.map((l, idx) => ({ x: cat.center(idx), y: plot.y + plot.height + 18, text: l, anchor: 'middle' })),
781
+ };
782
+ }
783
+ return {
784
+ orientation,
785
+ bars,
786
+ grid: layout.ticks.map((t) => ({ x1: t.pos, y1: plot.y, x2: t.pos, y2: plot.y + plot.height })),
787
+ valueLabels: layout.ticks.map((t) => ({ x: t.pos, y: plot.y + plot.height + 18, text: formatNumber(t.value), anchor: 'middle' })),
788
+ catLabels: c.labels.map((l, idx) => ({ x: plot.x - 8, y: cat.center(idx) + 3, text: l, anchor: 'end' })),
789
+ };
790
+ }, /* @ts-ignore */
791
+ ...(ngDevMode ? [{ debugName: "barVm" }] : /* istanbul ignore next */ []));
792
+ // --- Pie / Doughnut / Polar Area ---
793
+ pieVm = computed(() => {
794
+ const c = this.config();
795
+ if (c.type !== 'pie' && c.type !== 'doughnut' && c.type !== 'polarArea')
796
+ return null;
797
+ const h = this.hidden();
798
+ const s0 = c.series[0];
799
+ if (!s0)
800
+ return [];
801
+ const { cx, cy, radius } = radialArea(this.width(), this.height(), 8);
802
+ const entries = c.labels
803
+ .map((label, i) => ({ label, i, value: s0.data[i] ?? 0, color: seriesColor(s0.color, i) }))
804
+ .filter((e) => !h.has('cat:' + e.i));
805
+ const values = entries.map((e) => e.value);
806
+ if (c.type === 'polarArea') {
807
+ const max = Math.max(1, ...values.map((v) => Math.max(0, v)));
808
+ const slices = polarSlices(values, cx, cy, radius, max);
809
+ return slices.map((sl, k) => tipSlice(entries[k], sl.midAngle, radius * 0.62, cx, cy, sl.path));
810
+ }
811
+ const inner = c.type === 'doughnut' ? radius * (c.innerRadius ?? 0.6) : 0;
812
+ const slices = pieSlices(values, cx, cy, radius, inner);
813
+ return slices.map((sl, k) => tipSlice(entries[k], sl.midAngle, (radius + inner) / 2, cx, cy, sl.path));
814
+ }, /* @ts-ignore */
815
+ ...(ngDevMode ? [{ debugName: "pieVm" }] : /* istanbul ignore next */ []));
816
+ // --- Radar ---
817
+ radarVm = computed(() => {
818
+ const c = this.config();
819
+ if (c.type !== 'radar')
820
+ return null;
821
+ const h = this.hidden();
822
+ const count = c.labels.length;
823
+ const { cx, cy, radius } = radialArea(this.width(), this.height(), 30);
824
+ const grid = radarGrid(count, cx, cy, radius, 4);
825
+ const active = c.series.map((s, i) => ({ s, i })).filter(({ s }) => !h.has('ser:' + s.name));
826
+ let max = 1;
827
+ for (const { s } of active)
828
+ for (const v of s.data)
829
+ max = Math.max(max, v);
830
+ const series = active.map(({ s, i }) => {
831
+ const pts = radarPoints(s.data, cx, cy, radius, max);
832
+ return {
833
+ key: 'ser:' + s.name,
834
+ name: s.name,
835
+ color: seriesColor(s.color, i),
836
+ polygon: polygonPath(pts),
837
+ points: pts.map((p, idx) => ({ x: p.x, y: p.y, value: s.data[idx] ?? 0, label: c.labels[idx] ?? '' })),
838
+ };
839
+ });
840
+ return {
841
+ rings: grid.rings,
842
+ axisLines: grid.axes.map((a) => ({ x1: cx, y1: cy, x2: a.end.x, y2: a.end.y })),
843
+ axisLabels: grid.axes.map((a) => {
844
+ const p = polarToCartesian(cx, cy, radius + 14, a.angle);
845
+ const anchor = Math.abs(p.x - cx) < 1 ? 'middle' : p.x > cx ? 'start' : 'end';
846
+ return { x: p.x, y: p.y + 3, text: c.labels[a.labelIndex] ?? '', anchor };
847
+ }),
848
+ series,
849
+ };
850
+ }, /* @ts-ignore */
851
+ ...(ngDevMode ? [{ debugName: "radarVm" }] : /* istanbul ignore next */ []));
852
+ // --- Crosshair (prowadnica + pigułka etykiety osi X) dla wykresu liniowego ---
853
+ crosshair = computed(() => {
854
+ const c = this.config();
855
+ if (c.type !== 'line' || !c.crosshair)
856
+ return null;
857
+ const i = this.activeIndex();
858
+ const vm = this.lineVm();
859
+ if (i === null || !vm)
860
+ return null;
861
+ const cat = vm.catLabels[i];
862
+ if (!cat)
863
+ return null;
864
+ const w = Math.max(28, cat.text.length * 7 + 16);
865
+ return {
866
+ x: cat.x,
867
+ y1: vm.plot.y,
868
+ y2: vm.plot.y + vm.plot.height,
869
+ text: cat.text,
870
+ labelX: cat.x,
871
+ labelY: cat.y,
872
+ pillX: cat.x - w / 2,
873
+ pillY: cat.y - 13,
874
+ pillW: w,
875
+ pillH: 18,
876
+ };
877
+ }, /* @ts-ignore */
878
+ ...(ngDevMode ? [{ debugName: "crosshair" }] : /* istanbul ignore next */ []));
879
+ // --- Zdarzenia ---
880
+ onLineEnter(idx, tipX, tipY, name, label, value, color) {
881
+ this.activeIndex.set(idx);
882
+ this.showTip(tipX, tipY, name, label, value, color);
883
+ }
884
+ onLineLeave() {
885
+ this.activeIndex.set(null);
886
+ this.hideTip();
887
+ }
888
+ showTip(tipX, tipY, name, label, value, color) {
889
+ if (this.config().tooltip?.enabled === false)
890
+ return;
891
+ const fmt = this.config().tooltip?.format;
892
+ const text = fmt ? fmt({ series: name, label, value }) : formatNumber(value);
893
+ this.tooltip.set({ x: (tipX / this.width()) * 100, y: (tipY / this.height()) * 100, label, rows: [{ name, value: text, color }] });
894
+ }
895
+ hideTip() {
896
+ this.tooltip.set(null);
897
+ }
898
+ onToggle(key) {
899
+ const set = new Set(this.hidden());
900
+ if (set.has(key))
901
+ set.delete(key);
902
+ else
903
+ set.add(key);
904
+ this.hidden.set(set);
905
+ }
906
+ onHighlight(key) {
907
+ this.highlighted.set(key);
908
+ }
909
+ /** Czy dany element ma być wygaszony (aktywne podświetlenie innego). */
910
+ dim(key) {
911
+ const hi = this.highlighted();
912
+ return hi !== null && hi !== key;
913
+ }
914
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: KptChart, deps: [], target: i0.ɵɵFactoryTarget.Component });
915
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: KptChart, isStandalone: true, selector: "kpt-chart", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.kpt-chart--no-anim": "!anim().enabled", "style.--kpt-chart-duration": "anim().duration + 'ms'", "style.--kpt-chart-stagger": "anim().stagger + 'ms'", "style.--kpt-chart-delay": "anim().delay + 'ms'" }, classAttribute: "kpt-chart-host" }, ngImport: i0, template: "<div class=\"kpt-chart\" [attr.data-legend]=\"legendOpts().display ? legendOpts().position : null\">\n @if (titleOpts()?.display && titleOpts()?.text) {\n <div\n class=\"kpt-chart__title\"\n [style.text-align]=\"titleStyle()\"\n [style.color]=\"titleOpts()?.color\"\n [style.font-family]=\"titleOpts()?.font?.family\"\n [style.font-size.px]=\"titleOpts()?.font?.size\"\n [style.font-weight]=\"titleOpts()?.font?.weight\"\n [style.padding-top.px]=\"titleOpts()?.padding?.top\"\n [style.padding-bottom.px]=\"titleOpts()?.padding?.bottom\"\n >\n {{ titleOpts()?.text }}\n </div>\n }\n @if (subtitleOpts()?.display && subtitleOpts()?.text) {\n <div\n class=\"kpt-chart__subtitle\"\n [style.text-align]=\"subtitleStyle()\"\n [style.color]=\"subtitleOpts()?.color\"\n [style.font-family]=\"subtitleOpts()?.font?.family\"\n [style.font-size.px]=\"subtitleOpts()?.font?.size\"\n [style.font-weight]=\"subtitleOpts()?.font?.weight\"\n [style.padding-top.px]=\"subtitleOpts()?.padding?.top\"\n [style.padding-bottom.px]=\"subtitleOpts()?.padding?.bottom\"\n >\n {{ subtitleOpts()?.text }}\n </div>\n }\n\n <div class=\"kpt-chart__body\">\n <div class=\"kpt-chart__canvas\">\n <svg [attr.viewBox]=\"'0 0 ' + width() + ' ' + height()\" class=\"kpt-chart__svg\" role=\"img\" preserveAspectRatio=\"xMidYMid meet\">\n @switch (type()) {\n @case ('line') {\n @if (lineVm(); as vm) {\n <g class=\"kpt-chart__grid\">\n @for (g of vm.grid; track $index) {\n <line class=\"kpt-chart__gridline\" [attr.x1]=\"g.x1\" [attr.y1]=\"g.y1\" [attr.x2]=\"g.x2\" [attr.y2]=\"g.y2\" />\n }\n </g>\n @if (crosshair(); as ch) {\n <line class=\"kpt-chart__crosshair\" [attr.x1]=\"ch.x\" [attr.y1]=\"ch.y1\" [attr.x2]=\"ch.x\" [attr.y2]=\"ch.y2\" />\n }\n <g class=\"kpt-chart__axis\">\n @for (l of vm.valueLabels; track $index) {\n <text [attr.x]=\"l.x\" [attr.y]=\"l.y\" [attr.text-anchor]=\"l.anchor\">{{ l.text }}</text>\n }\n @for (l of vm.catLabels; track $index) {\n <text [attr.x]=\"l.x\" [attr.y]=\"l.y\" [attr.text-anchor]=\"l.anchor\">{{ l.text }}</text>\n }\n </g>\n @if (crosshair(); as ch) {\n <g class=\"kpt-chart__cat-active\">\n <rect [attr.x]=\"ch.pillX\" [attr.y]=\"ch.pillY\" [attr.width]=\"ch.pillW\" [attr.height]=\"ch.pillH\" [attr.rx]=\"ch.pillH / 2\" />\n <text [attr.x]=\"ch.labelX\" [attr.y]=\"ch.labelY\" text-anchor=\"middle\">{{ ch.text }}</text>\n </g>\n }\n @for (s of vm.series; track s.key) {\n <g class=\"kpt-chart__series\" [class.kpt-chart__series--dim]=\"dim(s.key)\">\n @if (s.area) {\n <path class=\"kpt-chart__area\" [attr.d]=\"s.area\" [style.fill]=\"s.color\" />\n }\n <path class=\"kpt-chart__line\" [attr.d]=\"s.path\" [style.stroke]=\"s.color\" pathLength=\"1\" />\n @for (p of s.points; track $index) {\n @if (vm.showPoints) {\n <circle\n class=\"kpt-chart__point\"\n [class.kpt-chart__point--active]=\"activeIndex() === $index\"\n [attr.cx]=\"p.x\"\n [attr.cy]=\"p.y\"\n r=\"4\"\n [style.fill]=\"s.color\"\n [style.--kpt-chart-i]=\"$index\"\n />\n }\n <circle\n class=\"kpt-chart__hit\"\n [attr.cx]=\"p.x\"\n [attr.cy]=\"p.y\"\n r=\"12\"\n (pointerenter)=\"onLineEnter($index, p.x, p.y, s.name, p.label, p.value, s.color)\"\n (pointerleave)=\"onLineLeave()\"\n />\n }\n </g>\n }\n }\n }\n\n @case ('bar') {\n @if (barVm(); as vm) {\n <g class=\"kpt-chart__grid\">\n @for (g of vm.grid; track $index) {\n <line class=\"kpt-chart__gridline\" [attr.x1]=\"g.x1\" [attr.y1]=\"g.y1\" [attr.x2]=\"g.x2\" [attr.y2]=\"g.y2\" />\n }\n </g>\n <g class=\"kpt-chart__axis\">\n @for (l of vm.valueLabels; track $index) {\n <text [attr.x]=\"l.x\" [attr.y]=\"l.y\" [attr.text-anchor]=\"l.anchor\">{{ l.text }}</text>\n }\n @for (l of vm.catLabels; track $index) {\n <text [attr.x]=\"l.x\" [attr.y]=\"l.y\" [attr.text-anchor]=\"l.anchor\">{{ l.text }}</text>\n }\n </g>\n <g class=\"kpt-chart__bars\" [attr.data-orientation]=\"vm.orientation\">\n @for (b of vm.bars; track b.key + '#' + b.index) {\n <path\n class=\"kpt-chart__bar\"\n [class.kpt-chart__series--dim]=\"dim(b.key)\"\n [attr.d]=\"b.path\"\n [style.fill]=\"b.color\"\n [style.--kpt-chart-i]=\"b.index\"\n (pointerenter)=\"showTip(b.tipX, b.tipY, b.name, b.label, b.value, b.color)\"\n (pointerleave)=\"hideTip()\"\n />\n }\n </g>\n }\n }\n\n @case ('radar') {\n @if (radarVm(); as vm) {\n <g class=\"kpt-chart__grid\">\n @for (r of vm.rings; track $index) {\n <path class=\"kpt-chart__ring\" [attr.d]=\"r\" />\n }\n @for (a of vm.axisLines; track $index) {\n <line class=\"kpt-chart__gridline\" [attr.x1]=\"a.x1\" [attr.y1]=\"a.y1\" [attr.x2]=\"a.x2\" [attr.y2]=\"a.y2\" />\n }\n </g>\n <g class=\"kpt-chart__axis\">\n @for (l of vm.axisLabels; track $index) {\n <text [attr.x]=\"l.x\" [attr.y]=\"l.y\" [attr.text-anchor]=\"l.anchor\">{{ l.text }}</text>\n }\n </g>\n @for (s of vm.series; track s.key) {\n <g class=\"kpt-chart__series kpt-chart__radar\" [class.kpt-chart__series--dim]=\"dim(s.key)\">\n <path class=\"kpt-chart__radar-area\" [attr.d]=\"s.polygon\" [style.fill]=\"s.color\" [style.stroke]=\"s.color\" />\n @for (p of s.points; track $index) {\n <circle\n class=\"kpt-chart__point\"\n [attr.cx]=\"p.x\"\n [attr.cy]=\"p.y\"\n r=\"4\"\n [style.fill]=\"s.color\"\n [style.--kpt-chart-i]=\"$index\"\n (pointerenter)=\"showTip(p.x, p.y, s.name, p.label, p.value, s.color)\"\n (pointerleave)=\"hideTip()\"\n />\n }\n </g>\n }\n }\n }\n\n @default {\n @if (pieVm(); as slices) {\n <g class=\"kpt-chart__slices\">\n @for (sl of slices; track sl.key) {\n <path\n class=\"kpt-chart__slice\"\n [class.kpt-chart__series--dim]=\"dim(sl.key)\"\n [attr.d]=\"sl.path\"\n [style.fill]=\"sl.color\"\n [style.--kpt-chart-i]=\"$index\"\n (pointerenter)=\"showTip(sl.tipX, sl.tipY, sl.label, '', sl.value, sl.color)\"\n (pointerleave)=\"hideTip()\"\n />\n }\n </g>\n }\n }\n }\n </svg>\n\n <kpt-chart-tooltip [state]=\"tooltip()\" />\n </div>\n\n @if (legendOpts().display) {\n <kpt-chart-legend\n [items]=\"legendItems()\"\n [position]=\"legendOpts().position\"\n [align]=\"legendOpts().align\"\n [pointStyle]=\"legendOpts().pointStyle\"\n [title]=\"legendOpts().title\"\n (toggle)=\"onToggle($event)\"\n (highlight)=\"onHighlight($event)\"\n />\n }\n </div>\n</div>\n", styles: ["@layer kpt.components{.kpt-chart-host{display:block;--kpt-chart-duration: .4s;--kpt-chart-stagger: 60ms;--kpt-chart-delay: 0ms;--kpt-chart-ease: cubic-bezier(.25, 1, .5, 1)}.kpt-chart{display:flex;flex-direction:column;gap:var(--kpt-space-2, .5rem);color:var(--kpt-color-on-surface)}.kpt-chart__title{font-size:var(--kpt-font-size-lg, 1.125rem);font-weight:var(--kpt-font-weight-semibold, 600);color:var(--kpt-color-on-surface)}.kpt-chart__subtitle{font-size:var(--kpt-font-size-sm, .875rem);color:var(--kpt-color-on-surface-muted, #6b7280)}.kpt-chart__body{display:flex;gap:var(--kpt-space-3, .75rem);align-items:center;min-width:0}.kpt-chart[data-legend=top] .kpt-chart__body,.kpt-chart[data-legend=bottom] .kpt-chart__body{flex-direction:column}.kpt-chart[data-legend=top] .kpt-chart-legend-host,.kpt-chart[data-legend=left] .kpt-chart-legend-host{order:-1}.kpt-chart__canvas{position:relative;flex:1 1 auto;min-width:0;width:100%}.kpt-chart__svg{display:block;width:100%;height:auto}.kpt-chart__gridline{stroke:var(--kpt-color-border);stroke-width:1;opacity:.7}.kpt-chart__ring{fill:none;stroke:var(--kpt-color-border);stroke-width:1;opacity:.7}.kpt-chart__axis text{fill:var(--kpt-color-on-surface-muted, #6b7280);font-size:11px;font-family:var(--kpt-font-family-sans)}.kpt-chart__crosshair{stroke:var(--kpt-color-border-strong);stroke-width:1;stroke-dasharray:4 4;opacity:.75;pointer-events:none}.kpt-chart__cat-active{pointer-events:none}.kpt-chart__cat-active rect{fill:var(--kpt-color-on-surface)}.kpt-chart__cat-active text{fill:var(--kpt-color-surface);font-size:11px;font-weight:var(--kpt-font-weight-medium, 500);font-family:var(--kpt-font-family-sans)}.kpt-chart__series,.kpt-chart__bar,.kpt-chart__slice{transition:opacity .2s var(--kpt-chart-ease)}.kpt-chart__series--dim{opacity:.22}.kpt-chart__line{fill:none;stroke-width:2.5;stroke-linecap:round;stroke-linejoin:round}.kpt-chart__area{stroke:none;opacity:.15}.kpt-chart__point{stroke:var(--kpt-color-surface);stroke-width:2;cursor:pointer;transition:transform .15s var(--kpt-chart-ease);transform-box:fill-box;transform-origin:center}.kpt-chart__point:hover{transform:scale(1.4)}.kpt-chart__point--active{transform:scale(1.5)}.kpt-chart__hit{fill:transparent;pointer-events:all;cursor:pointer}.kpt-chart__bar{cursor:pointer}.kpt-chart__slice{cursor:pointer;stroke:var(--kpt-color-surface);stroke-width:1.5}.kpt-chart__radar-area{fill-opacity:.18;stroke-width:2;stroke-linejoin:round}.kpt-chart__legend-title{font-size:var(--kpt-font-size-sm, .875rem);font-weight:var(--kpt-font-weight-medium, 500);color:var(--kpt-color-on-surface-muted);margin-bottom:var(--kpt-space-1, .25rem)}.kpt-chart__legend-list{display:flex;flex-wrap:wrap;gap:var(--kpt-space-2, .5rem) var(--kpt-space-4, 1rem);align-items:center;justify-content:center;margin:0;padding:0;list-style:none}.kpt-chart__legend[data-align=start] .kpt-chart__legend-list{justify-content:flex-start}.kpt-chart__legend[data-align=end] .kpt-chart__legend-list{justify-content:flex-end}.kpt-chart__legend[data-position=left] .kpt-chart__legend-list,.kpt-chart__legend[data-position=right] .kpt-chart__legend-list{flex-direction:column;align-items:flex-start}.kpt-chart__legend-item{display:inline-flex;align-items:center;gap:var(--kpt-space-2, .5rem);padding:0;border:0;background:none;cursor:pointer;font:inherit;font-size:var(--kpt-font-size-sm, .875rem);color:var(--kpt-color-on-surface)}.kpt-chart__legend-item--hidden{opacity:.45}.kpt-chart__legend-item--hidden .kpt-chart__legend-label{text-decoration:line-through}.kpt-chart__legend-marker{display:inline-block;width:12px;height:12px;background:var(--kpt-chart-legend-color);flex:none}.kpt-chart__legend-marker[data-style=circle]{border-radius:var(--kpt-radius-full, 9999px)}.kpt-chart__legend-marker[data-style=rect]{border-radius:var(--kpt-radius-sm, .25rem)}.kpt-chart__legend-marker[data-style=rectRot]{transform:rotate(45deg);border-radius:2px}.kpt-chart__legend-marker[data-style=triangle]{background:none;width:0;height:0;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:12px solid var(--kpt-chart-legend-color)}.kpt-chart__legend-marker[data-style=line]{height:3px;border-radius:var(--kpt-radius-full, 9999px)}.kpt-chart__tooltip{position:absolute;transform:translate(-50%,calc(-100% - 10px));pointer-events:none;background:var(--kpt-color-surface-raised, var(--kpt-color-surface));color:var(--kpt-color-on-surface);border:1px solid var(--kpt-color-border);border-radius:var(--kpt-radius-md, .5rem);box-shadow:0 4px 16px #0000001f;padding:var(--kpt-space-2, .5rem) var(--kpt-space-3, .75rem);font-size:var(--kpt-font-size-sm, .875rem);white-space:nowrap;z-index:2}.kpt-chart__tooltip--flip{transform:translate(-50%,12px)}.kpt-chart__tooltip-title{font-weight:var(--kpt-font-weight-semibold, 600);margin-bottom:2px}.kpt-chart__tooltip-row{display:flex;align-items:center;gap:var(--kpt-space-2, .5rem)}.kpt-chart__tooltip-dot{width:8px;height:8px;border-radius:var(--kpt-radius-full, 9999px);flex:none}.kpt-chart__tooltip-name{color:var(--kpt-color-on-surface-muted)}.kpt-chart__tooltip-val{margin-left:auto;font-weight:var(--kpt-font-weight-medium, 500)}.kpt-chart__line{stroke-dasharray:1;stroke-dashoffset:1;animation:kpt-chart-draw var(--kpt-chart-duration) var(--kpt-chart-ease) var(--kpt-chart-delay) forwards}.kpt-chart__area{animation:kpt-chart-fade var(--kpt-chart-duration) var(--kpt-chart-ease) var(--kpt-chart-delay) backwards}.kpt-chart__point{animation:kpt-chart-fade var(--kpt-chart-duration) var(--kpt-chart-ease) calc(var(--kpt-chart-delay) + var(--kpt-chart-duration) + var(--kpt-chart-i, 0) * var(--kpt-chart-stagger)) backwards}.kpt-chart__bars[data-orientation=vertical] .kpt-chart__bar{transform-box:fill-box;transform-origin:bottom;animation:kpt-chart-grow-y var(--kpt-chart-duration) var(--kpt-chart-ease) calc(var(--kpt-chart-delay) + var(--kpt-chart-i, 0) * var(--kpt-chart-stagger)) backwards}.kpt-chart__bars[data-orientation=horizontal] .kpt-chart__bar{transform-box:fill-box;transform-origin:left;animation:kpt-chart-grow-x var(--kpt-chart-duration) var(--kpt-chart-ease) calc(var(--kpt-chart-delay) + var(--kpt-chart-i, 0) * var(--kpt-chart-stagger)) backwards}.kpt-chart__slice,.kpt-chart__radar-area{transform-box:fill-box;transform-origin:center}.kpt-chart__slice{animation:kpt-chart-pop var(--kpt-chart-duration) var(--kpt-chart-ease) calc(var(--kpt-chart-delay) + var(--kpt-chart-i, 0) * var(--kpt-chart-stagger)) backwards}.kpt-chart__radar-area{animation:kpt-chart-pop var(--kpt-chart-duration) var(--kpt-chart-ease) var(--kpt-chart-delay) backwards}@keyframes kpt-chart-draw{to{stroke-dashoffset:0}}@keyframes kpt-chart-fade{0%{opacity:0}}@keyframes kpt-chart-grow-y{0%{transform:scaleY(0)}}@keyframes kpt-chart-grow-x{0%{transform:scaleX(0)}}@keyframes kpt-chart-pop{0%{transform:scale(0);opacity:0}}.kpt-chart--no-anim :is(.kpt-chart__line,.kpt-chart__area,.kpt-chart__point,.kpt-chart__bar,.kpt-chart__slice,.kpt-chart__radar-area){animation:none;stroke-dashoffset:0}@media(prefers-reduced-motion:reduce){.kpt-chart :is(.kpt-chart__line,.kpt-chart__area,.kpt-chart__point,.kpt-chart__bar,.kpt-chart__slice,.kpt-chart__radar-area){animation:none;stroke-dashoffset:0}}}\n"], dependencies: [{ kind: "component", type: KptChartLegend, selector: "kpt-chart-legend", inputs: ["items", "position", "align", "pointStyle", "title"], outputs: ["toggle", "highlight"] }, { kind: "component", type: KptChartTooltip, selector: "kpt-chart-tooltip", inputs: ["state"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
916
+ }
917
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: KptChart, decorators: [{
918
+ type: Component,
919
+ args: [{ selector: 'kpt-chart', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [KptChartLegend, KptChartTooltip], host: {
920
+ class: 'kpt-chart-host',
921
+ '[class.kpt-chart--no-anim]': '!anim().enabled',
922
+ '[style.--kpt-chart-duration]': "anim().duration + 'ms'",
923
+ '[style.--kpt-chart-stagger]': "anim().stagger + 'ms'",
924
+ '[style.--kpt-chart-delay]': "anim().delay + 'ms'",
925
+ }, template: "<div class=\"kpt-chart\" [attr.data-legend]=\"legendOpts().display ? legendOpts().position : null\">\n @if (titleOpts()?.display && titleOpts()?.text) {\n <div\n class=\"kpt-chart__title\"\n [style.text-align]=\"titleStyle()\"\n [style.color]=\"titleOpts()?.color\"\n [style.font-family]=\"titleOpts()?.font?.family\"\n [style.font-size.px]=\"titleOpts()?.font?.size\"\n [style.font-weight]=\"titleOpts()?.font?.weight\"\n [style.padding-top.px]=\"titleOpts()?.padding?.top\"\n [style.padding-bottom.px]=\"titleOpts()?.padding?.bottom\"\n >\n {{ titleOpts()?.text }}\n </div>\n }\n @if (subtitleOpts()?.display && subtitleOpts()?.text) {\n <div\n class=\"kpt-chart__subtitle\"\n [style.text-align]=\"subtitleStyle()\"\n [style.color]=\"subtitleOpts()?.color\"\n [style.font-family]=\"subtitleOpts()?.font?.family\"\n [style.font-size.px]=\"subtitleOpts()?.font?.size\"\n [style.font-weight]=\"subtitleOpts()?.font?.weight\"\n [style.padding-top.px]=\"subtitleOpts()?.padding?.top\"\n [style.padding-bottom.px]=\"subtitleOpts()?.padding?.bottom\"\n >\n {{ subtitleOpts()?.text }}\n </div>\n }\n\n <div class=\"kpt-chart__body\">\n <div class=\"kpt-chart__canvas\">\n <svg [attr.viewBox]=\"'0 0 ' + width() + ' ' + height()\" class=\"kpt-chart__svg\" role=\"img\" preserveAspectRatio=\"xMidYMid meet\">\n @switch (type()) {\n @case ('line') {\n @if (lineVm(); as vm) {\n <g class=\"kpt-chart__grid\">\n @for (g of vm.grid; track $index) {\n <line class=\"kpt-chart__gridline\" [attr.x1]=\"g.x1\" [attr.y1]=\"g.y1\" [attr.x2]=\"g.x2\" [attr.y2]=\"g.y2\" />\n }\n </g>\n @if (crosshair(); as ch) {\n <line class=\"kpt-chart__crosshair\" [attr.x1]=\"ch.x\" [attr.y1]=\"ch.y1\" [attr.x2]=\"ch.x\" [attr.y2]=\"ch.y2\" />\n }\n <g class=\"kpt-chart__axis\">\n @for (l of vm.valueLabels; track $index) {\n <text [attr.x]=\"l.x\" [attr.y]=\"l.y\" [attr.text-anchor]=\"l.anchor\">{{ l.text }}</text>\n }\n @for (l of vm.catLabels; track $index) {\n <text [attr.x]=\"l.x\" [attr.y]=\"l.y\" [attr.text-anchor]=\"l.anchor\">{{ l.text }}</text>\n }\n </g>\n @if (crosshair(); as ch) {\n <g class=\"kpt-chart__cat-active\">\n <rect [attr.x]=\"ch.pillX\" [attr.y]=\"ch.pillY\" [attr.width]=\"ch.pillW\" [attr.height]=\"ch.pillH\" [attr.rx]=\"ch.pillH / 2\" />\n <text [attr.x]=\"ch.labelX\" [attr.y]=\"ch.labelY\" text-anchor=\"middle\">{{ ch.text }}</text>\n </g>\n }\n @for (s of vm.series; track s.key) {\n <g class=\"kpt-chart__series\" [class.kpt-chart__series--dim]=\"dim(s.key)\">\n @if (s.area) {\n <path class=\"kpt-chart__area\" [attr.d]=\"s.area\" [style.fill]=\"s.color\" />\n }\n <path class=\"kpt-chart__line\" [attr.d]=\"s.path\" [style.stroke]=\"s.color\" pathLength=\"1\" />\n @for (p of s.points; track $index) {\n @if (vm.showPoints) {\n <circle\n class=\"kpt-chart__point\"\n [class.kpt-chart__point--active]=\"activeIndex() === $index\"\n [attr.cx]=\"p.x\"\n [attr.cy]=\"p.y\"\n r=\"4\"\n [style.fill]=\"s.color\"\n [style.--kpt-chart-i]=\"$index\"\n />\n }\n <circle\n class=\"kpt-chart__hit\"\n [attr.cx]=\"p.x\"\n [attr.cy]=\"p.y\"\n r=\"12\"\n (pointerenter)=\"onLineEnter($index, p.x, p.y, s.name, p.label, p.value, s.color)\"\n (pointerleave)=\"onLineLeave()\"\n />\n }\n </g>\n }\n }\n }\n\n @case ('bar') {\n @if (barVm(); as vm) {\n <g class=\"kpt-chart__grid\">\n @for (g of vm.grid; track $index) {\n <line class=\"kpt-chart__gridline\" [attr.x1]=\"g.x1\" [attr.y1]=\"g.y1\" [attr.x2]=\"g.x2\" [attr.y2]=\"g.y2\" />\n }\n </g>\n <g class=\"kpt-chart__axis\">\n @for (l of vm.valueLabels; track $index) {\n <text [attr.x]=\"l.x\" [attr.y]=\"l.y\" [attr.text-anchor]=\"l.anchor\">{{ l.text }}</text>\n }\n @for (l of vm.catLabels; track $index) {\n <text [attr.x]=\"l.x\" [attr.y]=\"l.y\" [attr.text-anchor]=\"l.anchor\">{{ l.text }}</text>\n }\n </g>\n <g class=\"kpt-chart__bars\" [attr.data-orientation]=\"vm.orientation\">\n @for (b of vm.bars; track b.key + '#' + b.index) {\n <path\n class=\"kpt-chart__bar\"\n [class.kpt-chart__series--dim]=\"dim(b.key)\"\n [attr.d]=\"b.path\"\n [style.fill]=\"b.color\"\n [style.--kpt-chart-i]=\"b.index\"\n (pointerenter)=\"showTip(b.tipX, b.tipY, b.name, b.label, b.value, b.color)\"\n (pointerleave)=\"hideTip()\"\n />\n }\n </g>\n }\n }\n\n @case ('radar') {\n @if (radarVm(); as vm) {\n <g class=\"kpt-chart__grid\">\n @for (r of vm.rings; track $index) {\n <path class=\"kpt-chart__ring\" [attr.d]=\"r\" />\n }\n @for (a of vm.axisLines; track $index) {\n <line class=\"kpt-chart__gridline\" [attr.x1]=\"a.x1\" [attr.y1]=\"a.y1\" [attr.x2]=\"a.x2\" [attr.y2]=\"a.y2\" />\n }\n </g>\n <g class=\"kpt-chart__axis\">\n @for (l of vm.axisLabels; track $index) {\n <text [attr.x]=\"l.x\" [attr.y]=\"l.y\" [attr.text-anchor]=\"l.anchor\">{{ l.text }}</text>\n }\n </g>\n @for (s of vm.series; track s.key) {\n <g class=\"kpt-chart__series kpt-chart__radar\" [class.kpt-chart__series--dim]=\"dim(s.key)\">\n <path class=\"kpt-chart__radar-area\" [attr.d]=\"s.polygon\" [style.fill]=\"s.color\" [style.stroke]=\"s.color\" />\n @for (p of s.points; track $index) {\n <circle\n class=\"kpt-chart__point\"\n [attr.cx]=\"p.x\"\n [attr.cy]=\"p.y\"\n r=\"4\"\n [style.fill]=\"s.color\"\n [style.--kpt-chart-i]=\"$index\"\n (pointerenter)=\"showTip(p.x, p.y, s.name, p.label, p.value, s.color)\"\n (pointerleave)=\"hideTip()\"\n />\n }\n </g>\n }\n }\n }\n\n @default {\n @if (pieVm(); as slices) {\n <g class=\"kpt-chart__slices\">\n @for (sl of slices; track sl.key) {\n <path\n class=\"kpt-chart__slice\"\n [class.kpt-chart__series--dim]=\"dim(sl.key)\"\n [attr.d]=\"sl.path\"\n [style.fill]=\"sl.color\"\n [style.--kpt-chart-i]=\"$index\"\n (pointerenter)=\"showTip(sl.tipX, sl.tipY, sl.label, '', sl.value, sl.color)\"\n (pointerleave)=\"hideTip()\"\n />\n }\n </g>\n }\n }\n }\n </svg>\n\n <kpt-chart-tooltip [state]=\"tooltip()\" />\n </div>\n\n @if (legendOpts().display) {\n <kpt-chart-legend\n [items]=\"legendItems()\"\n [position]=\"legendOpts().position\"\n [align]=\"legendOpts().align\"\n [pointStyle]=\"legendOpts().pointStyle\"\n [title]=\"legendOpts().title\"\n (toggle)=\"onToggle($event)\"\n (highlight)=\"onHighlight($event)\"\n />\n }\n </div>\n</div>\n", styles: ["@layer kpt.components{.kpt-chart-host{display:block;--kpt-chart-duration: .4s;--kpt-chart-stagger: 60ms;--kpt-chart-delay: 0ms;--kpt-chart-ease: cubic-bezier(.25, 1, .5, 1)}.kpt-chart{display:flex;flex-direction:column;gap:var(--kpt-space-2, .5rem);color:var(--kpt-color-on-surface)}.kpt-chart__title{font-size:var(--kpt-font-size-lg, 1.125rem);font-weight:var(--kpt-font-weight-semibold, 600);color:var(--kpt-color-on-surface)}.kpt-chart__subtitle{font-size:var(--kpt-font-size-sm, .875rem);color:var(--kpt-color-on-surface-muted, #6b7280)}.kpt-chart__body{display:flex;gap:var(--kpt-space-3, .75rem);align-items:center;min-width:0}.kpt-chart[data-legend=top] .kpt-chart__body,.kpt-chart[data-legend=bottom] .kpt-chart__body{flex-direction:column}.kpt-chart[data-legend=top] .kpt-chart-legend-host,.kpt-chart[data-legend=left] .kpt-chart-legend-host{order:-1}.kpt-chart__canvas{position:relative;flex:1 1 auto;min-width:0;width:100%}.kpt-chart__svg{display:block;width:100%;height:auto}.kpt-chart__gridline{stroke:var(--kpt-color-border);stroke-width:1;opacity:.7}.kpt-chart__ring{fill:none;stroke:var(--kpt-color-border);stroke-width:1;opacity:.7}.kpt-chart__axis text{fill:var(--kpt-color-on-surface-muted, #6b7280);font-size:11px;font-family:var(--kpt-font-family-sans)}.kpt-chart__crosshair{stroke:var(--kpt-color-border-strong);stroke-width:1;stroke-dasharray:4 4;opacity:.75;pointer-events:none}.kpt-chart__cat-active{pointer-events:none}.kpt-chart__cat-active rect{fill:var(--kpt-color-on-surface)}.kpt-chart__cat-active text{fill:var(--kpt-color-surface);font-size:11px;font-weight:var(--kpt-font-weight-medium, 500);font-family:var(--kpt-font-family-sans)}.kpt-chart__series,.kpt-chart__bar,.kpt-chart__slice{transition:opacity .2s var(--kpt-chart-ease)}.kpt-chart__series--dim{opacity:.22}.kpt-chart__line{fill:none;stroke-width:2.5;stroke-linecap:round;stroke-linejoin:round}.kpt-chart__area{stroke:none;opacity:.15}.kpt-chart__point{stroke:var(--kpt-color-surface);stroke-width:2;cursor:pointer;transition:transform .15s var(--kpt-chart-ease);transform-box:fill-box;transform-origin:center}.kpt-chart__point:hover{transform:scale(1.4)}.kpt-chart__point--active{transform:scale(1.5)}.kpt-chart__hit{fill:transparent;pointer-events:all;cursor:pointer}.kpt-chart__bar{cursor:pointer}.kpt-chart__slice{cursor:pointer;stroke:var(--kpt-color-surface);stroke-width:1.5}.kpt-chart__radar-area{fill-opacity:.18;stroke-width:2;stroke-linejoin:round}.kpt-chart__legend-title{font-size:var(--kpt-font-size-sm, .875rem);font-weight:var(--kpt-font-weight-medium, 500);color:var(--kpt-color-on-surface-muted);margin-bottom:var(--kpt-space-1, .25rem)}.kpt-chart__legend-list{display:flex;flex-wrap:wrap;gap:var(--kpt-space-2, .5rem) var(--kpt-space-4, 1rem);align-items:center;justify-content:center;margin:0;padding:0;list-style:none}.kpt-chart__legend[data-align=start] .kpt-chart__legend-list{justify-content:flex-start}.kpt-chart__legend[data-align=end] .kpt-chart__legend-list{justify-content:flex-end}.kpt-chart__legend[data-position=left] .kpt-chart__legend-list,.kpt-chart__legend[data-position=right] .kpt-chart__legend-list{flex-direction:column;align-items:flex-start}.kpt-chart__legend-item{display:inline-flex;align-items:center;gap:var(--kpt-space-2, .5rem);padding:0;border:0;background:none;cursor:pointer;font:inherit;font-size:var(--kpt-font-size-sm, .875rem);color:var(--kpt-color-on-surface)}.kpt-chart__legend-item--hidden{opacity:.45}.kpt-chart__legend-item--hidden .kpt-chart__legend-label{text-decoration:line-through}.kpt-chart__legend-marker{display:inline-block;width:12px;height:12px;background:var(--kpt-chart-legend-color);flex:none}.kpt-chart__legend-marker[data-style=circle]{border-radius:var(--kpt-radius-full, 9999px)}.kpt-chart__legend-marker[data-style=rect]{border-radius:var(--kpt-radius-sm, .25rem)}.kpt-chart__legend-marker[data-style=rectRot]{transform:rotate(45deg);border-radius:2px}.kpt-chart__legend-marker[data-style=triangle]{background:none;width:0;height:0;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:12px solid var(--kpt-chart-legend-color)}.kpt-chart__legend-marker[data-style=line]{height:3px;border-radius:var(--kpt-radius-full, 9999px)}.kpt-chart__tooltip{position:absolute;transform:translate(-50%,calc(-100% - 10px));pointer-events:none;background:var(--kpt-color-surface-raised, var(--kpt-color-surface));color:var(--kpt-color-on-surface);border:1px solid var(--kpt-color-border);border-radius:var(--kpt-radius-md, .5rem);box-shadow:0 4px 16px #0000001f;padding:var(--kpt-space-2, .5rem) var(--kpt-space-3, .75rem);font-size:var(--kpt-font-size-sm, .875rem);white-space:nowrap;z-index:2}.kpt-chart__tooltip--flip{transform:translate(-50%,12px)}.kpt-chart__tooltip-title{font-weight:var(--kpt-font-weight-semibold, 600);margin-bottom:2px}.kpt-chart__tooltip-row{display:flex;align-items:center;gap:var(--kpt-space-2, .5rem)}.kpt-chart__tooltip-dot{width:8px;height:8px;border-radius:var(--kpt-radius-full, 9999px);flex:none}.kpt-chart__tooltip-name{color:var(--kpt-color-on-surface-muted)}.kpt-chart__tooltip-val{margin-left:auto;font-weight:var(--kpt-font-weight-medium, 500)}.kpt-chart__line{stroke-dasharray:1;stroke-dashoffset:1;animation:kpt-chart-draw var(--kpt-chart-duration) var(--kpt-chart-ease) var(--kpt-chart-delay) forwards}.kpt-chart__area{animation:kpt-chart-fade var(--kpt-chart-duration) var(--kpt-chart-ease) var(--kpt-chart-delay) backwards}.kpt-chart__point{animation:kpt-chart-fade var(--kpt-chart-duration) var(--kpt-chart-ease) calc(var(--kpt-chart-delay) + var(--kpt-chart-duration) + var(--kpt-chart-i, 0) * var(--kpt-chart-stagger)) backwards}.kpt-chart__bars[data-orientation=vertical] .kpt-chart__bar{transform-box:fill-box;transform-origin:bottom;animation:kpt-chart-grow-y var(--kpt-chart-duration) var(--kpt-chart-ease) calc(var(--kpt-chart-delay) + var(--kpt-chart-i, 0) * var(--kpt-chart-stagger)) backwards}.kpt-chart__bars[data-orientation=horizontal] .kpt-chart__bar{transform-box:fill-box;transform-origin:left;animation:kpt-chart-grow-x var(--kpt-chart-duration) var(--kpt-chart-ease) calc(var(--kpt-chart-delay) + var(--kpt-chart-i, 0) * var(--kpt-chart-stagger)) backwards}.kpt-chart__slice,.kpt-chart__radar-area{transform-box:fill-box;transform-origin:center}.kpt-chart__slice{animation:kpt-chart-pop var(--kpt-chart-duration) var(--kpt-chart-ease) calc(var(--kpt-chart-delay) + var(--kpt-chart-i, 0) * var(--kpt-chart-stagger)) backwards}.kpt-chart__radar-area{animation:kpt-chart-pop var(--kpt-chart-duration) var(--kpt-chart-ease) var(--kpt-chart-delay) backwards}@keyframes kpt-chart-draw{to{stroke-dashoffset:0}}@keyframes kpt-chart-fade{0%{opacity:0}}@keyframes kpt-chart-grow-y{0%{transform:scaleY(0)}}@keyframes kpt-chart-grow-x{0%{transform:scaleX(0)}}@keyframes kpt-chart-pop{0%{transform:scale(0);opacity:0}}.kpt-chart--no-anim :is(.kpt-chart__line,.kpt-chart__area,.kpt-chart__point,.kpt-chart__bar,.kpt-chart__slice,.kpt-chart__radar-area){animation:none;stroke-dashoffset:0}@media(prefers-reduced-motion:reduce){.kpt-chart :is(.kpt-chart__line,.kpt-chart__area,.kpt-chart__point,.kpt-chart__bar,.kpt-chart__slice,.kpt-chart__radar-area){animation:none;stroke-dashoffset:0}}}\n"] }]
926
+ }], propDecorators: { config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: true }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }] } });
927
+ // Buduje SliceVm z pozycją tooltipa na promieniu środka wycinka.
928
+ function tipSlice(entry, midAngle, tipRadius, cx, cy, path) {
929
+ const p = polarToCartesian(cx, cy, tipRadius, midAngle);
930
+ return { key: 'cat:' + entry.i, path, color: entry.color, label: entry.label, value: entry.value, tipX: p.x, tipY: p.y };
931
+ }
932
+ function styleFor(align) {
933
+ if (align === 'center')
934
+ return 'center';
935
+ if (align === 'end')
936
+ return 'right';
937
+ return 'left';
938
+ }
939
+
940
+ /*
941
+ * Rdzeń wykresów — czyste typy TypeScript (bez zależności od DOM/frameworka).
942
+ * Współdzielone przez warstwę Angular (Faza 1) i React (Faza 2).
943
+ */
944
+
945
+ /* Rdzeń wykresów (pure TS) — barrel eksportów. */
946
+
947
+ /*
948
+ * Public API — @konce-pt/angular/charts
949
+ * Wykresy SVG dla Angulara (sygnały, zoneless). Rdzeń obliczeń: pure TS.
950
+ */
951
+
952
+ /**
953
+ * Generated bundle index. Do not edit.
954
+ */
955
+
956
+ export { KPT_CHART_PALETTE, KptChart, areaPath, barLayout, linePath, niceScale, pieSlices, polarSlices, radarGrid, radarPoints, seriesColor };
957
+ //# sourceMappingURL=konce-pt-angular-charts.mjs.map