@konce-pt/react 0.18.1 → 0.18.3
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/app-shell/app-shell.d.ts +3 -2
- package/dist/app-shell/llms.txt +64 -63
- package/dist/charts/chart.d.ts +7 -3
- package/dist/charts/chart.js +57 -28
- package/dist/charts/llms.txt +102 -95
- package/package.json +14 -14
|
@@ -35,9 +35,10 @@ export interface KptAppShellProps extends Omit<ComponentPropsWithRef<'div'>, 'ch
|
|
|
35
35
|
/**
|
|
36
36
|
* Włącza szynę ikon w paśmie od `sidenavBreakpoint` do tego breakpointa: zamknięty panel
|
|
37
37
|
* nie chowa się wtedy całkowicie, tylko zwęża do `--kpt-sidenav-rail-width`. Wartość musi
|
|
38
|
-
* być wyższa niż `sidenavBreakpoint`. `
|
|
38
|
+
* być wyższa niż `sidenavBreakpoint`. `'always'` - szyna bez górnej granicy: od
|
|
39
|
+
* `sidenavBreakpoint` w górę zamknięcie zawsze zwija panel do ikon. `null` (domyślnie) wyłącza szynę.
|
|
39
40
|
*/
|
|
40
|
-
sidenavRailBreakpoint?: KptBreakpoint | null;
|
|
41
|
+
sidenavRailBreakpoint?: KptBreakpoint | 'always' | null;
|
|
41
42
|
/** Etykieta dostępności panelu; puste = `appShell.navigation` ze słownika. */
|
|
42
43
|
sidenavLabel?: string;
|
|
43
44
|
}
|
package/dist/app-shell/llms.txt
CHANGED
|
@@ -1,63 +1,64 @@
|
|
|
1
|
-
# KptAppShell (@konce-pt/react)
|
|
2
|
-
|
|
3
|
-
The application skeleton: a bar on top, a side panel and the content.
|
|
4
|
-
Import: `import { KptAppShell, useKptShellNavToggle } from '@konce-pt/react';`
|
|
5
|
-
|
|
6
|
-
## DOM
|
|
7
|
-
<div class="kpt-app-shell kpt-app-shell--nav-open"
|
|
8
|
-
data-sidenav-mode="auto" data-sidenav-breakpoint="lg">
|
|
9
|
-
...toolbar...
|
|
10
|
-
<div class="kpt-app-shell__body">
|
|
11
|
-
<nav class="kpt-app-shell__sidenav" tabindex="-1" id="...">...sidenav...</nav>
|
|
12
|
-
<div class="kpt-app-shell__scrim" aria-hidden="true"></div>
|
|
13
|
-
<main class="kpt-app-shell__content">...children...</main>
|
|
14
|
-
</div>
|
|
15
|
-
</div>
|
|
16
|
-
|
|
17
|
-
## Props
|
|
18
|
-
- `toolbar`: ReactNode - the slot above the content area (usually `<KptToolbar>`)
|
|
19
|
-
- `sidenav`: ReactNode - the side panel slot (usually `<KptSidenav>`)
|
|
20
|
-
- `children`: ReactNode - the application content
|
|
21
|
-
- `sidenavOpen` / `defaultSidenavOpen` / `onSidenavOpenChange` - the docked layout's open state
|
|
22
|
-
(default open)
|
|
23
|
-
- `sidenavCompactOpen` / `defaultSidenavCompactOpen` / `onSidenavCompactOpenChange` - the overlay's
|
|
24
|
-
open state (default closed)
|
|
25
|
-
- `sidenavMode`: 'auto' | 'side' | 'over' (default 'auto')
|
|
26
|
-
- `sidenavBreakpoint`: 'sm' | 'md' | 'lg' | 'xl' | '2xl' (default 'lg')
|
|
27
|
-
- `sidenavRailBreakpoint`: the same union or null (default null)
|
|
28
|
-
|
|
29
|
-
- `
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
`--kpt-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
-
|
|
59
|
-
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
1
|
+
# KptAppShell (@konce-pt/react)
|
|
2
|
+
|
|
3
|
+
The application skeleton: a bar on top, a side panel and the content.
|
|
4
|
+
Import: `import { KptAppShell, useKptShellNavToggle } from '@konce-pt/react';`
|
|
5
|
+
|
|
6
|
+
## DOM
|
|
7
|
+
<div class="kpt-app-shell kpt-app-shell--nav-open"
|
|
8
|
+
data-sidenav-mode="auto" data-sidenav-breakpoint="lg">
|
|
9
|
+
...toolbar...
|
|
10
|
+
<div class="kpt-app-shell__body">
|
|
11
|
+
<nav class="kpt-app-shell__sidenav" tabindex="-1" id="...">...sidenav...</nav>
|
|
12
|
+
<div class="kpt-app-shell__scrim" aria-hidden="true"></div>
|
|
13
|
+
<main class="kpt-app-shell__content">...children...</main>
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
## Props
|
|
18
|
+
- `toolbar`: ReactNode - the slot above the content area (usually `<KptToolbar>`)
|
|
19
|
+
- `sidenav`: ReactNode - the side panel slot (usually `<KptSidenav>`)
|
|
20
|
+
- `children`: ReactNode - the application content
|
|
21
|
+
- `sidenavOpen` / `defaultSidenavOpen` / `onSidenavOpenChange` - the docked layout's open state
|
|
22
|
+
(default open)
|
|
23
|
+
- `sidenavCompactOpen` / `defaultSidenavCompactOpen` / `onSidenavCompactOpenChange` - the overlay's
|
|
24
|
+
open state (default closed)
|
|
25
|
+
- `sidenavMode`: 'auto' | 'side' | 'over' (default 'auto')
|
|
26
|
+
- `sidenavBreakpoint`: 'sm' | 'md' | 'lg' | 'xl' | '2xl' (default 'lg')
|
|
27
|
+
- `sidenavRailBreakpoint`: the same union, 'always' or null (default null) - `'always'` keeps the icon rail
|
|
28
|
+
at every width from `sidenavBreakpoint` up: the toggle switches between the full panel and the icons
|
|
29
|
+
- `sidenavLabel`: string - empty falls back to `appShell.navigation` from the dictionary
|
|
30
|
+
- `locale`, `dictionary` - i18n overrides for this component only
|
|
31
|
+
|
|
32
|
+
## Two open states, on purpose
|
|
33
|
+
The docked layout and the overlay keep separate state. If they shared one, narrowing the window
|
|
34
|
+
would have to overwrite the consumer's state, and widening it back would have nothing to restore.
|
|
35
|
+
`useKptShellNavToggle` hits whichever state governs the current layout, so one button serves both.
|
|
36
|
+
|
|
37
|
+
## The nav toggle
|
|
38
|
+
const toggle = useKptShellNavToggle(); // optional { label }
|
|
39
|
+
<KptButton variant="text" {...toggle}>Menu</KptButton>
|
|
40
|
+
|
|
41
|
+
It returns `aria-expanded`, `aria-controls`, `aria-label` and `onClick`. Spread them on a native
|
|
42
|
+
`<button>` or on `<KptButton>` (which forwards them to its inner `<button>`) - ARIA attributes must
|
|
43
|
+
land on the element that carries the role, never on a wrapper.
|
|
44
|
+
|
|
45
|
+
`useKptAppShell()` exposes the same state directly: `{ isSidenavOpen, isOverlay, sidenavId,
|
|
46
|
+
toggleSidenav, openSidenav, closeSidenav }`. It throws outside `<KptAppShell>`.
|
|
47
|
+
|
|
48
|
+
## Layout is CSS, behaviour is JS
|
|
49
|
+
Which layout applies is decided entirely by the stylesheet from the `data-*` attributes, so nothing
|
|
50
|
+
flickers before hydration and the page is correct with JavaScript disabled. The breakpoint hook only
|
|
51
|
+
decides WHICH state the button flips. The scrim is always in the DOM; CSS controls its visibility.
|
|
52
|
+
|
|
53
|
+
## Tokens
|
|
54
|
+
`--kpt-sidenav-width` (16rem), `--kpt-sidenav-rail-width` (4rem), `--kpt-z-overlay`,
|
|
55
|
+
`--kpt-color-overlay-scrim`, `--kpt-motion-duration-base`, `--kpt-motion-easing-standard`.
|
|
56
|
+
|
|
57
|
+
## Accessibility
|
|
58
|
+
- The panel is a `<nav>` with a label and an `id` for `aria-controls`.
|
|
59
|
+
- A closed docked panel leaves the tab order through `visibility`, correct on the first frame.
|
|
60
|
+
- While the overlay is open the content gets `inert`, and Escape closes the panel - unless something
|
|
61
|
+
opened above the shell (a dialog, menu or popover) already handled it.
|
|
62
|
+
- Opening the overlay moves focus into the panel (its first focusable element, or the `<nav>`
|
|
63
|
+
itself); closing it returns focus to where it came from - usually the menu button - unless the user
|
|
64
|
+
has already moved it elsewhere. An overlay that is open when the page loads does not take focus.
|
package/dist/charts/chart.d.ts
CHANGED
|
@@ -3,9 +3,13 @@ import { type KptChartConfig } from '@konce-pt/chart';
|
|
|
3
3
|
import { type KptI18nOverrides } from '../i18n/i18n-context.tsx';
|
|
4
4
|
export interface KptChartProps extends Omit<ComponentPropsWithRef<'div'>, 'children'>, KptI18nOverrides {
|
|
5
5
|
config: KptChartConfig;
|
|
6
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* Szerokość projektowa. Wykres rysuje się w pikselach swojego kontenera (mierzy go
|
|
8
|
+
* `ResizeObserver`), więc `width`/`height` wyznaczają proporcję (wysokość = szerokość kontenera
|
|
9
|
+
* x height / width) i rozmiar pierwszej klatki, zanim pomiar dotrze (SSR).
|
|
10
|
+
*/
|
|
7
11
|
width?: number;
|
|
8
|
-
/** Wysokość
|
|
12
|
+
/** Wysokość projektowa - patrz `width`. */
|
|
9
13
|
height?: number;
|
|
10
14
|
}
|
|
11
15
|
/**
|
|
@@ -16,4 +20,4 @@ export interface KptChartProps extends Omit<ComponentPropsWithRef<'div'>, 'child
|
|
|
16
20
|
* @example
|
|
17
21
|
* <KptChart config={{ type: 'line', labels: ['Sty', 'Lut'], series: [{ name: 'Sprzedaż', data: [12, 19] }] }} />
|
|
18
22
|
*/
|
|
19
|
-
export declare function KptChart({ config, width, height, locale, dictionary, className, style, ...rest }: Readonly<KptChartProps>): import("react").JSX.Element;
|
|
23
|
+
export declare function KptChart({ config, width: designWidth, height: designHeight, locale, dictionary, className, style, ...rest }: Readonly<KptChartProps>): import("react").JSX.Element;
|
package/dist/charts/chart.js
CHANGED
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { useEffect, useId, useRef, useState } from 'react';
|
|
4
|
-
import { KptChartMotion, areaPath, axisBase, axisLabelIndices, bandPath, bandScale, barLayout, chartScale, chartAccessibleName, chartDataTable, clampLabel, dataScale, deltaKind, domainScale, estimateLabelWidth, formatNumber, linePath, linearScale, pieSlices, plotArea, polarSlices, polarToCartesian, polygonPath, radarGrid, radarPoints, radialArea, sameChartShape, scaleRows, seriesColor, significantDelta, stackSeries, } from '@konce-pt/chart';
|
|
4
|
+
import { KptChartMotion, areaPath, axisBase, axisLabelIndices, bandPath, bandScale, barLayout, chartScale, chartAccessibleName, chartDataTable, chartMetrics, clampLabel, dataScale, deltaKind, domainScale, estimateLabelWidth, formatNumber, linePath, linearScale, pieSlices, plotArea, polarSlices, polarToCartesian, polygonPath, radarGrid, radarPoints, radialArea, sameChartShape, scaleRows, seriesColor, significantDelta, stackSeries, } from '@konce-pt/chart';
|
|
5
5
|
import { cn } from "../internal/cn.js";
|
|
6
6
|
import { useKptMotion } from "../motion/motion-context.js";
|
|
7
7
|
import { useKptMessages } from "../i18n/i18n-context.js";
|
|
8
8
|
import { KptChartLegend } from "./chart-legend.js";
|
|
9
9
|
import { KptChartTooltip } from "./chart-tooltip.js";
|
|
10
|
-
/** Wysokość linii etykiety kategorii na osi pionowej (słupki poziome) - jednostki viewBoxa. */
|
|
11
|
-
const CATEGORY_LINE = 12;
|
|
12
10
|
/**
|
|
13
11
|
* Etykiety osi poziomej: wszystkie, jeśli się mieszczą, inaczej równy podział 9/5/3/2
|
|
14
12
|
* (`axisLabelIndices`). Pełna nazwa każdej kategorii zostaje w pigułce prowadnicy i w dymku.
|
|
15
13
|
*/
|
|
16
|
-
function bottomLabels(labels, centers, y, width) {
|
|
17
|
-
const sizes = labels.map((l) => estimateLabelWidth(l));
|
|
14
|
+
function bottomLabels(labels, centers, y, width, m) {
|
|
15
|
+
const sizes = labels.map((l) => estimateLabelWidth(l, m.fontSize));
|
|
18
16
|
const shown = new Set(axisLabelIndices(centers, sizes));
|
|
19
17
|
return labels.map((text, i) => ({
|
|
20
18
|
at: centers[i] ?? 0,
|
|
@@ -26,12 +24,10 @@ function bottomLabels(labels, centers, y, width) {
|
|
|
26
24
|
}));
|
|
27
25
|
}
|
|
28
26
|
/** Etykiety osi pionowej (słupki poziome): ten sam podział, rozmiarem jest wysokość linii. */
|
|
29
|
-
function sideLabels(labels, centers, x) {
|
|
30
|
-
const shown = new Set(axisLabelIndices(centers, centers.map(() =>
|
|
31
|
-
return labels.map((text, i) => ({ at: centers[i] ?? 0, x, y: (centers[i] ?? 0) +
|
|
27
|
+
function sideLabels(labels, centers, x, m) {
|
|
28
|
+
const shown = new Set(axisLabelIndices(centers, centers.map(() => m.categoryLine), 2));
|
|
29
|
+
return labels.map((text, i) => ({ at: centers[i] ?? 0, x, y: (centers[i] ?? 0) + m.baselineShift, text, anchor: 'end', visible: shown.has(i) }));
|
|
32
30
|
}
|
|
33
|
-
const VERTICAL_INSETS = { top: 12, right: 16, bottom: 28, left: 46 };
|
|
34
|
-
const HORIZONTAL_INSETS = { top: 12, right: 16, bottom: 28, left: 76 };
|
|
35
31
|
/** Szerokość pasma smugi światła (jednostki viewBoxa) - połowa po każdej stronie czoła fali. */
|
|
36
32
|
const SHEEN_HALF = 90;
|
|
37
33
|
/** Zjechanie z wykresu: dymek czeka chwilę, zanim zgaśnie (powrót kursora go zatrzymuje). */
|
|
@@ -72,13 +68,46 @@ function styleFor(align) {
|
|
|
72
68
|
* @example
|
|
73
69
|
* <KptChart config={{ type: 'line', labels: ['Sty', 'Lut'], series: [{ name: 'Sprzedaż', data: [12, 19] }] }} />
|
|
74
70
|
*/
|
|
75
|
-
export function KptChart({ config, width = 600, height = 360, locale, dictionary, className, style, ...rest }) {
|
|
71
|
+
export function KptChart({ config, width: designWidth = 600, height: designHeight = 360, locale, dictionary, className, style, ...rest }) {
|
|
76
72
|
const { t, locale: activeLocale } = useKptMessages({ locale, dictionary });
|
|
77
73
|
const descId = `${useId()}-desc`;
|
|
78
74
|
// Alternatywa tekstowa (WCAG 1.1.1): nazwa SVG (`ariaLabel` -> tytuł -> i18n) i dane w ukrytej
|
|
79
75
|
// wizualnie tabeli - dymek na najechanie jest dostępny tylko myszą.
|
|
80
76
|
const accessibleName = chartAccessibleName(config, t('chart.label'));
|
|
81
77
|
const dataTable = chartDataTable(config, activeLocale);
|
|
78
|
+
// viewBox równy pikselom ekranu: długości rosną z kontenerem, a czcionka (11px) i grubość kresek
|
|
79
|
+
// zostają prawdziwymi pikselami. Stały viewBox skalował się w całości - na szerokim ekranie
|
|
80
|
+
// etykiety puchły i rozmywały się, w wąskiej kolumnie malały do nieczytelności.
|
|
81
|
+
const canvasRef = useRef(null);
|
|
82
|
+
const [measured, setMeasured] = useState(null);
|
|
83
|
+
// Rozmiar tekstu osi z CSS płótna (`--kpt-chart-font-size`, domyślnie `--kpt-font-size-sm`).
|
|
84
|
+
// Tekst w SVG go dziedziczy, a marginesy, odstęp etykiet od osi, pigułka prowadnicy
|
|
85
|
+
// i przerzedzanie etykiet liczą się z niego. 14 to wartość domyślnego tokenu.
|
|
86
|
+
const [fontPx, setFontPx] = useState(14);
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
const el = canvasRef.current;
|
|
89
|
+
if (!el || typeof ResizeObserver === 'undefined')
|
|
90
|
+
return;
|
|
91
|
+
const readFont = () => {
|
|
92
|
+
const px = parseFloat(getComputedStyle(el).fontSize);
|
|
93
|
+
if (px > 0)
|
|
94
|
+
setFontPx(px);
|
|
95
|
+
};
|
|
96
|
+
readFont();
|
|
97
|
+
// Wysokość wynika z szerokości, a szerokości kontenera nie zmienia wysokość wykresu - bez pętli.
|
|
98
|
+
// Rozmiar tekstu czytany przy każdej zmianie szerokości - zmiana tokenu zwykle idzie w parze ze zmianą układu.
|
|
99
|
+
const observer = new ResizeObserver(([entry]) => {
|
|
100
|
+
const w = Math.round(entry?.contentRect.width ?? 0);
|
|
101
|
+
if (w > 0)
|
|
102
|
+
setMeasured(w);
|
|
103
|
+
readFont();
|
|
104
|
+
});
|
|
105
|
+
observer.observe(el);
|
|
106
|
+
return () => observer.disconnect();
|
|
107
|
+
}, []);
|
|
108
|
+
const width = measured ?? designWidth;
|
|
109
|
+
const height = Math.round((width * designHeight) / designWidth);
|
|
110
|
+
const m = chartMetrics(fontPx);
|
|
82
111
|
const [hidden, setHidden] = useState(new Set());
|
|
83
112
|
const [highlighted, setHighlighted] = useState(null);
|
|
84
113
|
/** Indeks kategorii pod kursorem - prowadnica crosshair w wykresie liniowym. */
|
|
@@ -148,7 +177,7 @@ export function KptChart({ config, width = 600, height = 360, locale, dictionary
|
|
|
148
177
|
const sheen = (() => {
|
|
149
178
|
if (!frame.sheen)
|
|
150
179
|
return null;
|
|
151
|
-
const plot = plotArea(width, height, horizontal ?
|
|
180
|
+
const plot = plotArea(width, height, horizontal ? m.horizontalInsets : m.verticalInsets);
|
|
152
181
|
const start = horizontal ? plot.y : plot.x;
|
|
153
182
|
const length = horizontal ? plot.height : plot.width;
|
|
154
183
|
const mid = start - SHEEN_HALF + (length + 2 * SHEEN_HALF) * frame.sheen.progress;
|
|
@@ -236,7 +265,7 @@ export function KptChart({ config, width = 600, height = 360, locale, dictionary
|
|
|
236
265
|
: chartScale(rows, { valueAxis: view.valueAxis, stacked: view.stacked });
|
|
237
266
|
// Stos: punkty serii na narastającej sumie, pasmo od linii serii pod nią.
|
|
238
267
|
const stack = view.stacked ? stackSeries(rows) : null;
|
|
239
|
-
const plot = plotArea(width, height,
|
|
268
|
+
const plot = plotArea(width, height, m.verticalInsets);
|
|
240
269
|
const yScale = linearScale(scale.min, scale.max, plot.y + plot.height, plot.y);
|
|
241
270
|
const count = view.labels.length;
|
|
242
271
|
const xAt = (i) => count > 1 ? plot.x + (i / (count - 1)) * plot.width : plot.x + plot.width / 2;
|
|
@@ -252,12 +281,12 @@ export function KptChart({ config, width = 600, height = 360, locale, dictionary
|
|
|
252
281
|
y2: yScale(t),
|
|
253
282
|
}));
|
|
254
283
|
const valueLabels = scale.ticks.map((t) => ({
|
|
255
|
-
x: plot.x -
|
|
256
|
-
y: yScale(t) +
|
|
284
|
+
x: plot.x - m.sideGap,
|
|
285
|
+
y: yScale(t) + m.baselineShift,
|
|
257
286
|
text: formatNumber(t),
|
|
258
287
|
anchor: 'end',
|
|
259
288
|
}));
|
|
260
|
-
const catLabels = bottomLabels(view.labels, view.labels.map((_, i) => xAt(i)), plot.y + plot.height +
|
|
289
|
+
const catLabels = bottomLabels(view.labels, view.labels.map((_, i) => xAt(i)), plot.y + plot.height + m.labelOffset, width, m);
|
|
261
290
|
const catTicks = view.labels.map((_, i) => ({ x: xAt(i), y: plot.y + plot.height }));
|
|
262
291
|
const series = active.map(({ s, i }, k) => ({
|
|
263
292
|
key: `ser:${s.name}`,
|
|
@@ -277,7 +306,7 @@ export function KptChart({ config, width = 600, height = 360, locale, dictionary
|
|
|
277
306
|
const crosshairCat = config.crosshair && activeIndex !== null ? catLabels[activeIndex] : undefined;
|
|
278
307
|
// Prowadnica i pigułka jadą razem z dymkiem.
|
|
279
308
|
const crossX = crosshairCat ? (frame.tip?.x ?? crosshairCat.at) : 0;
|
|
280
|
-
const pillWidth = crosshairCat ? Math.max(
|
|
309
|
+
const pillWidth = crosshairCat ? Math.max(m.pill.minWidth, estimateLabelWidth(crosshairCat.text, m.fontSize) + 2 * m.pill.padding) : 0;
|
|
281
310
|
// Pigułka przy skrajnej kategorii przesuwa się do środka zamiast wyjść poza viewBox.
|
|
282
311
|
const pillX = crosshairCat ? clampLabel(crossX, pillWidth, 0, width) : 0;
|
|
283
312
|
/** Najbliższa kategoria w całym obszarze wykresu: wiersze wszystkich serii, dymek nad najwyższym punktem. */
|
|
@@ -299,7 +328,7 @@ export function KptChart({ config, width = 600, height = 360, locale, dictionary
|
|
|
299
328
|
if (moved && tipMotion())
|
|
300
329
|
setRipple({ id: ++rippleId.current, x: top.pt.x, y: top.pt.y, color: top.s.color });
|
|
301
330
|
};
|
|
302
|
-
return (_jsxs(_Fragment, { children: [_jsx("g", { className: "kpt-chart__grid", children: grid.map((g, i) => (_jsx("line", { className: "kpt-chart__gridline", x1: g.x1, y1: g.y1, x2: g.x2, y2: g.y2 }, i))) }), crosshairCat ? (_jsx("line", { className: "kpt-chart__crosshair", x1: crossX, y1: plot.y, x2: crossX, y2: plot.y + plot.height })) : null, _jsx("g", { className: "kpt-chart__axis", children: [...valueLabels, ...catLabels.filter((l) => l.visible)].map((l, i) => (_jsx("text", { x: l.x, y: l.y, textAnchor: l.anchor, children: l.text }, i))) }), _jsx("g", { className: "kpt-chart__cat-ticks", "aria-hidden": "true", children: catTicks.map((tk, i) => (_jsx("circle", { className: cn('kpt-chart__cat-tick', activeIndex === i && 'kpt-chart__cat-tick--active'), r: 2, cx: tk.x, cy: tk.y }, i))) }), crosshairCat ? (_jsxs("g", { className: "kpt-chart__cat-active", children: [_jsx("rect", { x: pillX - pillWidth / 2, y: crosshairCat.y -
|
|
331
|
+
return (_jsxs(_Fragment, { children: [_jsx("g", { className: "kpt-chart__grid", children: grid.map((g, i) => (_jsx("line", { className: "kpt-chart__gridline", x1: g.x1, y1: g.y1, x2: g.x2, y2: g.y2 }, i))) }), crosshairCat ? (_jsx("line", { className: "kpt-chart__crosshair", x1: crossX, y1: plot.y, x2: crossX, y2: plot.y + plot.height })) : null, _jsx("g", { className: "kpt-chart__axis", children: [...valueLabels, ...catLabels.filter((l) => l.visible)].map((l, i) => (_jsx("text", { x: l.x, y: l.y, textAnchor: l.anchor, children: l.text }, i))) }), _jsx("g", { className: "kpt-chart__cat-ticks", "aria-hidden": "true", children: catTicks.map((tk, i) => (_jsx("circle", { className: cn('kpt-chart__cat-tick', activeIndex === i && 'kpt-chart__cat-tick--active'), r: 2, cx: tk.x, cy: tk.y }, i))) }), crosshairCat ? (_jsxs("g", { className: "kpt-chart__cat-active", children: [_jsx("rect", { x: pillX - pillWidth / 2, y: crosshairCat.y - m.pill.rise, width: pillWidth, height: m.pill.height, rx: m.pill.height / 2 }), _jsx("text", { x: pillX, y: crosshairCat.y, textAnchor: "middle", children: crosshairCat.text })] })) : null, ghostState
|
|
303
332
|
? series.map((s) => {
|
|
304
333
|
const prev = ghostState.prev[s.i] ?? [];
|
|
305
334
|
const next = ghostState.next[s.i] ?? [];
|
|
@@ -334,7 +363,7 @@ export function KptChart({ config, width = 600, height = 360, locale, dictionary
|
|
|
334
363
|
const orientation = view.orientation ?? 'vertical';
|
|
335
364
|
const vertical = orientation === 'vertical';
|
|
336
365
|
const active = view.series.map((s, i) => ({ s, i })).filter(({ s }) => !hidden.has(`ser:${s.name}`));
|
|
337
|
-
const plot = plotArea(width, height, vertical ?
|
|
366
|
+
const plot = plotArea(width, height, vertical ? m.verticalInsets : m.horizontalInsets);
|
|
338
367
|
const radius = view.borderRadius ?? 4;
|
|
339
368
|
const layout = barLayout(active.map(({ s }) => s.data), view.labels.length, plot, orientation, radius, frame.domain ? domainScale(frame.domain.target, frame.domain.min, frame.domain.max) : undefined, { stacked: view.stacked, valueAxis: view.valueAxis });
|
|
340
369
|
const colors = active.map(({ s, i }) => seriesColor(s.color, i));
|
|
@@ -346,17 +375,17 @@ export function KptChart({ config, width = 600, height = 360, locale, dictionary
|
|
|
346
375
|
? layout.ticks.map((t) => ({ x1: plot.x, y1: t.pos, x2: plot.x + plot.width, y2: t.pos }))
|
|
347
376
|
: layout.ticks.map((t) => ({ x1: t.pos, y1: plot.y, x2: t.pos, y2: plot.y + plot.height }));
|
|
348
377
|
const valueLabels = vertical
|
|
349
|
-
? layout.ticks.map((t) => ({ x: plot.x -
|
|
378
|
+
? layout.ticks.map((t) => ({ x: plot.x - m.sideGap, y: t.pos + m.baselineShift, text: formatNumber(t.value), anchor: 'end' }))
|
|
350
379
|
: layout.ticks.map((t) => ({
|
|
351
380
|
x: t.pos,
|
|
352
|
-
y: plot.y + plot.height +
|
|
381
|
+
y: plot.y + plot.height + m.labelOffset,
|
|
353
382
|
text: formatNumber(t.value),
|
|
354
383
|
anchor: 'middle',
|
|
355
384
|
}));
|
|
356
385
|
const centers = view.labels.map((_, i) => cat.center(i));
|
|
357
386
|
const catLabels = vertical
|
|
358
|
-
? bottomLabels(view.labels, centers, plot.y + plot.height +
|
|
359
|
-
: sideLabels(view.labels, centers, plot.x -
|
|
387
|
+
? bottomLabels(view.labels, centers, plot.y + plot.height + m.labelOffset, width, m)
|
|
388
|
+
: sideLabels(view.labels, centers, plot.x - m.sideGap, m);
|
|
360
389
|
const bars = layout.bars.map((b, order) => ({
|
|
361
390
|
b,
|
|
362
391
|
order,
|
|
@@ -416,7 +445,7 @@ export function KptChart({ config, width = 600, height = 360, locale, dictionary
|
|
|
416
445
|
const first = view.series[0];
|
|
417
446
|
if (!first)
|
|
418
447
|
return null;
|
|
419
|
-
const { cx, cy, radius } = radialArea(width, height,
|
|
448
|
+
const { cx, cy, radius } = radialArea(width, height, m.radialPadding.plain);
|
|
420
449
|
const entries = config.labels
|
|
421
450
|
.map((label, i) => ({ label, i, value: first.data[i] ?? 0, color: seriesColor(first.color, i) }))
|
|
422
451
|
.filter((entry) => !hidden.has(`cat:${entry.i}`));
|
|
@@ -438,7 +467,7 @@ export function KptChart({ config, width = 600, height = 360, locale, dictionary
|
|
|
438
467
|
const radarVm = (() => {
|
|
439
468
|
if (config.type !== 'radar')
|
|
440
469
|
return null;
|
|
441
|
-
const { cx, cy, radius } = radialArea(width, height,
|
|
470
|
+
const { cx, cy, radius } = radialArea(width, height, m.radialPadding.labelled);
|
|
442
471
|
const grid = radarGrid(config.labels.length, cx, cy, radius, 4);
|
|
443
472
|
const active = view.series.map((s, i) => ({ s, i })).filter(({ s }) => !hidden.has(`ser:${s.name}`));
|
|
444
473
|
let max = 1;
|
|
@@ -446,9 +475,9 @@ export function KptChart({ config, width = 600, height = 360, locale, dictionary
|
|
|
446
475
|
for (const v of s.data)
|
|
447
476
|
max = Math.max(max, v);
|
|
448
477
|
return (_jsxs(_Fragment, { children: [_jsxs("g", { className: "kpt-chart__grid", children: [grid.rings.map((ring, i) => (_jsx("path", { className: "kpt-chart__ring", d: ring }, i))), grid.axes.map((axis, i) => (_jsx("line", { className: "kpt-chart__gridline", x1: cx, y1: cy, x2: axis.end.x, y2: axis.end.y }, i)))] }), _jsx("g", { className: "kpt-chart__axis", children: grid.axes.map((axis, i) => {
|
|
449
|
-
const p = polarToCartesian(cx, cy, radius +
|
|
478
|
+
const p = polarToCartesian(cx, cy, radius + m.radarLabelGap, axis.angle);
|
|
450
479
|
const anchor = Math.abs(p.x - cx) < 1 ? 'middle' : p.x > cx ? 'start' : 'end';
|
|
451
|
-
return (_jsx("text", { x: p.x, y: p.y +
|
|
480
|
+
return (_jsx("text", { x: p.x, y: p.y + m.baselineShift, textAnchor: anchor, children: config.labels[axis.labelIndex] ?? '' }, i));
|
|
452
481
|
}) }), active.map(({ s, i }) => {
|
|
453
482
|
const key = `ser:${s.name}`;
|
|
454
483
|
const color = seriesColor(s.color, i);
|
|
@@ -477,5 +506,5 @@ export function KptChart({ config, width = 600, height = 360, locale, dictionary
|
|
|
477
506
|
fontWeight: config.subtitle.font?.weight,
|
|
478
507
|
paddingTop: config.subtitle.padding?.top,
|
|
479
508
|
paddingBottom: config.subtitle.padding?.bottom,
|
|
480
|
-
}, children: config.subtitle.text })) : null, _jsxs("div", { className: "kpt-chart__body", children: [_jsxs("div", { className: "kpt-chart__canvas", children: [_jsxs("svg", { className: "kpt-chart__svg", viewBox: `0 0 ${width} ${height}`, role: "img", "aria-label": accessibleName, "aria-describedby": config.description ? descId : undefined, preserveAspectRatio: "xMidYMid meet", children: [sheen ? (_jsx("defs", { children: _jsxs("linearGradient", { id: sheenId, gradientUnits: "userSpaceOnUse", x1: sheen.x1, x2: sheen.x2, y1: sheen.y1, y2: sheen.y2, children: [_jsx("stop", { offset: "0", className: "kpt-chart__sheen-stop", stopOpacity: 0 }), _jsx("stop", { offset: "0.5", className: "kpt-chart__sheen-stop", stopOpacity: 0.95 }), _jsx("stop", { offset: "1", className: "kpt-chart__sheen-stop", stopOpacity: 0 })] }) })) : null, lineVm, barVm, radarVm, pieVm] }), config.description ? (_jsx("p", { className: "kpt-chart__sr", id: descId, children: config.description })) : null, _jsxs("table", { className: "kpt-chart__sr", children: [_jsxs("caption", { children: [t('chart.dataTable'), ": ", accessibleName] }), _jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { scope: "col", children: t('chart.category') }), dataTable.columns.map((col, i) => (_jsx("th", { scope: "col", children: col }, i)))] }) }), _jsx("tbody", { children: dataTable.rows.map((row, i) => (_jsxs("tr", { children: [_jsx("th", { scope: "row", children: row.label }), row.values.map((value, j) => (_jsx("td", { children: value }, j)))] }, i))) })] }), _jsx(KptChartTooltip, { state: tooltip })] }), legendOpts.display ? (_jsx(KptChartLegend, { items: legendItems, position: legendOpts.position, align: legendOpts.align, pointStyle: legendOpts.pointStyle, title: legendOpts.title, onToggle: toggle, onHighlight: setHighlighted })) : null] })] }) }));
|
|
509
|
+
}, children: config.subtitle.text })) : null, _jsxs("div", { className: "kpt-chart__body", children: [_jsxs("div", { className: "kpt-chart__canvas", ref: canvasRef, children: [_jsxs("svg", { className: "kpt-chart__svg", viewBox: `0 0 ${width} ${height}`, role: "img", "aria-label": accessibleName, "aria-describedby": config.description ? descId : undefined, preserveAspectRatio: "xMidYMid meet", children: [sheen ? (_jsx("defs", { children: _jsxs("linearGradient", { id: sheenId, gradientUnits: "userSpaceOnUse", x1: sheen.x1, x2: sheen.x2, y1: sheen.y1, y2: sheen.y2, children: [_jsx("stop", { offset: "0", className: "kpt-chart__sheen-stop", stopOpacity: 0 }), _jsx("stop", { offset: "0.5", className: "kpt-chart__sheen-stop", stopOpacity: 0.95 }), _jsx("stop", { offset: "1", className: "kpt-chart__sheen-stop", stopOpacity: 0 })] }) })) : null, lineVm, barVm, radarVm, pieVm] }), config.description ? (_jsx("p", { className: "kpt-chart__sr", id: descId, children: config.description })) : null, _jsxs("table", { className: "kpt-chart__sr", children: [_jsxs("caption", { children: [t('chart.dataTable'), ": ", accessibleName] }), _jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { scope: "col", children: t('chart.category') }), dataTable.columns.map((col, i) => (_jsx("th", { scope: "col", children: col }, i)))] }) }), _jsx("tbody", { children: dataTable.rows.map((row, i) => (_jsxs("tr", { children: [_jsx("th", { scope: "row", children: row.label }), row.values.map((value, j) => (_jsx("td", { children: value }, j)))] }, i))) })] }), _jsx(KptChartTooltip, { state: tooltip })] }), legendOpts.display ? (_jsx(KptChartLegend, { items: legendItems, position: legendOpts.position, align: legendOpts.align, pointStyle: legendOpts.pointStyle, title: legendOpts.title, onToggle: toggle, onHighlight: setHighlighted })) : null] })] }) }));
|
|
481
510
|
}
|
package/dist/charts/llms.txt
CHANGED
|
@@ -1,95 +1,102 @@
|
|
|
1
|
-
# KptChart (@konce-pt/react/charts)
|
|
2
|
-
|
|
3
|
-
An SVG chart driven by a single `config` object: line, bar, pie, doughnut, polarArea, radar.
|
|
4
|
-
Import: `import { KptChart, type KptChartConfig } from '@konce-pt/react/charts';`
|
|
5
|
-
|
|
6
|
-
A separate subpath, because the geometry and the chart styles are heavy next to the library core
|
|
7
|
-
and not every app needs them.
|
|
8
|
-
|
|
9
|
-
## Props
|
|
10
|
-
- `config`: `KptChartConfig` (required)
|
|
11
|
-
- `width`, `height` - the
|
|
12
|
-
|
|
13
|
-
The
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
- `className`, `ref` and every other `<div>` prop go to the host
|
|
19
|
-
|
|
20
|
-
## The config is the API
|
|
21
|
-
`{ type, labels, series, points?, area?, interpolation?, orientation?, borderRadius?, innerRadius?,
|
|
22
|
-
valueAxis?, stacked?, crosshair?, tooltip?, legend?, title?, subtitle?, animation?, ariaLabel?, description? }` - the same shape the Angular port
|
|
23
|
-
takes, because both read it from `@konce-pt/chart`.
|
|
24
|
-
|
|
25
|
-
## Value axis and stacking (line, bar)
|
|
26
|
-
- `valueAxis?: { zero?, min?, max? }` - the value axis starts at zero by default, so a bar and the area under a line
|
|
27
|
-
show the size directly. `zero: false` fits the axis to the data (a weight of 79-90 kg is no longer a flat line under
|
|
28
|
-
the ceiling); `min` / `max` pin an end of the axis exactly and win over `zero` - a value beyond a pinned end is
|
|
29
|
-
cut at the edge. Bars and areas grow from zero, or from the end of the axis nearer to it
|
|
30
|
-
- `stacked?: boolean` - stacked series: bars on top of each other in one band per category (positive values up,
|
|
31
|
-
negative down; only the outer segment is rounded), lines as running totals with a band under each one (with
|
|
32
|
-
`area`). The axis covers the totals; the tooltip shows the values of the series, not the totals
|
|
33
|
-
- The rules live in `@konce-pt/chart`: `axisScale`, `chartScale`, `stackSeries`, `bandPath` (tested)
|
|
34
|
-
|
|
35
|
-
## Category axis (line, bar)
|
|
36
|
-
- When the category labels do not fit, the axis shows an even subset instead of overlapping text: all of
|
|
37
|
-
them if they fit, otherwise the densest split that fits - 9 labels (eighths of the axis), 5 (first, 1/4,
|
|
38
|
-
middle, 3/4, last), 3, and finally the two ends only. Horizontal bars apply the same rule to the rows
|
|
39
|
-
- The choice is made in
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
`animation.
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
`--kpt-chart-
|
|
88
|
-
`
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
1
|
+
# KptChart (@konce-pt/react/charts)
|
|
2
|
+
|
|
3
|
+
An SVG chart driven by a single `config` object: line, bar, pie, doughnut, polarArea, radar.
|
|
4
|
+
Import: `import { KptChart, type KptChartConfig } from '@konce-pt/react/charts';`
|
|
5
|
+
|
|
6
|
+
A separate subpath, because the geometry and the chart styles are heavy next to the library core
|
|
7
|
+
and not every app needs them.
|
|
8
|
+
|
|
9
|
+
## Props
|
|
10
|
+
- `config`: `KptChartConfig` (required)
|
|
11
|
+
- `width`, `height` - the aspect ratio (default 600 x 360); the chart fills the width of its container
|
|
12
|
+
|
|
13
|
+
The chart draws in the pixels of its container: a `ResizeObserver` measures the canvas width and the
|
|
14
|
+
viewBox follows it, the height comes from the `width`/`height` ratio. Lengths grow and shrink with the
|
|
15
|
+
container, while the text (axis labels, 14px by default) and the line weights stay real pixels - the
|
|
16
|
+
same in a 300px column and on a 3000px screen. `width`/`height` are therefore only the aspect ratio and the
|
|
17
|
+
size of the first frame before the measurement arrives (SSR); there is no need to scale them by hand.
|
|
18
|
+
- `className`, `ref` and every other `<div>` prop go to the host
|
|
19
|
+
|
|
20
|
+
## The config is the API
|
|
21
|
+
`{ type, labels, series, points?, area?, interpolation?, orientation?, borderRadius?, innerRadius?,
|
|
22
|
+
valueAxis?, stacked?, crosshair?, tooltip?, legend?, title?, subtitle?, animation?, ariaLabel?, description? }` - the same shape the Angular port
|
|
23
|
+
takes, because both read it from `@konce-pt/chart`.
|
|
24
|
+
|
|
25
|
+
## Value axis and stacking (line, bar)
|
|
26
|
+
- `valueAxis?: { zero?, min?, max? }` - the value axis starts at zero by default, so a bar and the area under a line
|
|
27
|
+
show the size directly. `zero: false` fits the axis to the data (a weight of 79-90 kg is no longer a flat line under
|
|
28
|
+
the ceiling); `min` / `max` pin an end of the axis exactly and win over `zero` - a value beyond a pinned end is
|
|
29
|
+
cut at the edge. Bars and areas grow from zero, or from the end of the axis nearer to it
|
|
30
|
+
- `stacked?: boolean` - stacked series: bars on top of each other in one band per category (positive values up,
|
|
31
|
+
negative down; only the outer segment is rounded), lines as running totals with a band under each one (with
|
|
32
|
+
`area`). The axis covers the totals; the tooltip shows the values of the series, not the totals
|
|
33
|
+
- The rules live in `@konce-pt/chart`: `axisScale`, `chartScale`, `stackSeries`, `bandPath` (tested)
|
|
34
|
+
|
|
35
|
+
## Category axis (line, bar)
|
|
36
|
+
- When the category labels do not fit, the axis shows an even subset instead of overlapping text: all of
|
|
37
|
+
them if they fit, otherwise the densest split that fits - 9 labels (eighths of the axis), 5 (first, 1/4,
|
|
38
|
+
middle, 3/4, last), 3, and finally the two ends only. Horizontal bars apply the same rule to the rows
|
|
39
|
+
- The choice is made in on-screen pixels (the chart measures its container), so a narrow column shows fewer
|
|
40
|
+
labels and a wide one more; the outer labels and the
|
|
41
|
+
crosshair pill are shifted inward so they never leave the chart
|
|
42
|
+
- A line chart marks every category with a small dot on the axis; the active one grows on hover. The full
|
|
43
|
+
name of any category stays in the crosshair pill and the tooltip
|
|
44
|
+
- The rules live in `@konce-pt/chart`: `axisLabelIndices`, `estimateLabelWidth`, `clampLabel` (tested)
|
|
45
|
+
|
|
46
|
+
## The geometry lives in a package
|
|
47
|
+
Scales, paths, bar layout and arcs come from `@konce-pt/chart` - pure functions with `node --test`
|
|
48
|
+
coverage, shared with the Angular port. The component only turns them into SVG elements; there is
|
|
49
|
+
no DOM manipulation and no canvas.
|
|
50
|
+
|
|
51
|
+
## Hover covers the whole plot
|
|
52
|
+
Line and bar charts listen on an invisible surface over the plot: the pointer anywhere picks the nearest
|
|
53
|
+
category, the tooltip lists every visible series there and travels from category to category without
|
|
54
|
+
fading out in between. Slices and radar points react to the element itself. The bubble never leaves the
|
|
55
|
+
chart - it is clamped by its real width.
|
|
56
|
+
|
|
57
|
+
## Motion: data updates and the tooltip
|
|
58
|
+
A new `config` with the same type, categories and series names is a data update: the values travel as a
|
|
59
|
+
wave from left to right on springs, with a light sheen at the wave front. Line and bar charts keep the
|
|
60
|
+
previous state for 1.7 s - a dashed ghost, up/down segments (`--kpt-chart-delta-up` / `--kpt-chart-delta-down`)
|
|
61
|
+
and the difference above points that changed by 2% or more - and the value axis waits for the ghost to
|
|
62
|
+
fade before zooming to the new data. The tooltip, the crosshair and the category pill travel between
|
|
63
|
+
categories, the digits roll on reels and the category name slides in from the direction of travel.
|
|
64
|
+
`animation.update: false`, `animation.delta: false` and `tooltip.motion: false` switch the parts off;
|
|
65
|
+
`animation.enabled: false`, `<KptMotionProvider enabled={false}>` and the system "reduce motion" setting
|
|
66
|
+
switch off all code-driven motion. The frame loop and its numbers live in `@konce-pt/chart`
|
|
67
|
+
(`KptChartMotion`, `springStep`, `reelCells`), shared with the Angular port.
|
|
68
|
+
|
|
69
|
+
## Interaction is local state
|
|
70
|
+
Hidden series, the highlighted one, the tooltip, the active category and the motion frame are
|
|
71
|
+
`useState` - the config stays a plain object the caller owns, and nothing is written back into it.
|
|
72
|
+
|
|
73
|
+
## Examples
|
|
74
|
+
<KptChart config={{ type: 'line', labels: ['Jan', 'Feb'], series: [{ name: 'Sales', data: [12, 19] }] }} />
|
|
75
|
+
|
|
76
|
+
<KptChart
|
|
77
|
+
config={{
|
|
78
|
+
type: 'doughnut',
|
|
79
|
+
innerRadius: 0.62,
|
|
80
|
+
labels: ['Done', 'In progress'],
|
|
81
|
+
series: [{ name: 'Tasks', data: [42, 15] }],
|
|
82
|
+
legend: { position: 'right' },
|
|
83
|
+
}}
|
|
84
|
+
/>
|
|
85
|
+
|
|
86
|
+
## Tokens
|
|
87
|
+
`--kpt-chart-{duration,stagger,delay}` for the animation; `--kpt-chart-sheen`,
|
|
88
|
+
`--kpt-chart-delta-up` and `--kpt-chart-delta-down` for the data update; series colours come from
|
|
89
|
+
`KPT_CHART_PALETTE` or `series.color`.
|
|
90
|
+
|
|
91
|
+
Axis text size: `--kpt-chart-font-size` on the chart (default `--kpt-font-size-sm`, 14px - the size of the
|
|
92
|
+
tooltip and the legend). The SVG text inherits it, and the component reads the computed value to derive
|
|
93
|
+
everything that sits next to the text - the margins for the labels, the gap between a label and its axis,
|
|
94
|
+
the crosshair pill, the label thinning (`chartMetrics` in `@konce-pt/chart`). Override it in CSS, the
|
|
95
|
+
layout follows: `.revenue .kpt-chart-host { --kpt-chart-font-size: 12px; }`.
|
|
96
|
+
|
|
97
|
+
## Accessibility
|
|
98
|
+
The `<svg>` is `role="img"` with a name: `config.ariaLabel`, else the title text, else `chart.label`
|
|
99
|
+
from i18n. `config.description` (one sentence with the takeaway) is tied to it through
|
|
100
|
+
`aria-describedby`. The data itself sits in a visually hidden `<table>` (categories in rows, series in
|
|
101
|
+
columns, numbers in the active language), so a screen reader can walk the values - the tooltip is
|
|
102
|
+
pointer-only. The legend is a list of real buttons, so series can be toggled from the keyboard.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@konce-pt/react",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.3",
|
|
4
4
|
"description": "Open-source React 19 component library on the Koncept UI design system - the same kpt-* CSS layer and --kpt-* tokens as the Angular port. Zero runtime dependencies.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "konce.pt",
|
|
@@ -65,19 +65,19 @@
|
|
|
65
65
|
"leaflet": "^1.9.0"
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
|
-
"@konce-pt/
|
|
69
|
-
"@konce-pt/
|
|
70
|
-
"@konce-pt/
|
|
71
|
-
"@konce-pt/
|
|
72
|
-
"@konce-pt/
|
|
73
|
-
"@konce-pt/
|
|
74
|
-
"@konce-pt/
|
|
75
|
-
"@konce-pt/
|
|
76
|
-
"@konce-pt/
|
|
77
|
-
"@konce-pt/
|
|
78
|
-
"@konce-pt/
|
|
79
|
-
"@konce-pt/
|
|
80
|
-
"@konce-pt/
|
|
68
|
+
"@konce-pt/chat": "0.18.3",
|
|
69
|
+
"@konce-pt/roadmap": "0.18.3",
|
|
70
|
+
"@konce-pt/i18n": "0.18.3",
|
|
71
|
+
"@konce-pt/icons": "0.18.3",
|
|
72
|
+
"@konce-pt/motion": "0.18.3",
|
|
73
|
+
"@konce-pt/mention": "0.18.3",
|
|
74
|
+
"@konce-pt/chart": "0.18.3",
|
|
75
|
+
"@konce-pt/speech": "0.18.3",
|
|
76
|
+
"@konce-pt/backdrop": "0.18.3",
|
|
77
|
+
"@konce-pt/table": "0.18.3",
|
|
78
|
+
"@konce-pt/datetime": "0.18.3",
|
|
79
|
+
"@konce-pt/tokens": "0.18.3",
|
|
80
|
+
"@konce-pt/map": "0.18.3"
|
|
81
81
|
},
|
|
82
82
|
"devDependencies": {
|
|
83
83
|
"@types/react": "^19.2.0",
|