@nexub/react 4.4.0 → 4.5.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.
- package/accessibility.json +45 -1
- package/dist/components/Chart/Chart.js +11 -8
- package/dist/components/Chart/MoreCharts.d.ts +47 -0
- package/dist/components/Chart/MoreCharts.js +90 -0
- package/dist/components/Chart/index.d.ts +2 -0
- package/dist/components/Chart/index.js +5 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +5 -0
- package/dist/styles.css +1 -1
- package/package.json +6 -6
package/accessibility.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "4.
|
|
2
|
+
"version": "4.5.0",
|
|
3
3
|
"standard": "WCAG 2.2 Level AA",
|
|
4
4
|
"method": {
|
|
5
5
|
"automated": "axe-core rules tagged wcag2a, wcag2aa, wcag21a, wcag21aa and wcag22aa, run on every Storybook story in light and dark on every pull request; a new violation fails the build.",
|
|
@@ -402,6 +402,28 @@
|
|
|
402
402
|
"keyboard": [],
|
|
403
403
|
"screenReader": []
|
|
404
404
|
},
|
|
405
|
+
{
|
|
406
|
+
"slug": "funnel",
|
|
407
|
+
"name": "Funnel",
|
|
408
|
+
"package": "@nexub/react",
|
|
409
|
+
"automated": "pass",
|
|
410
|
+
"exceptions": [],
|
|
411
|
+
"keyboard": [],
|
|
412
|
+
"screenReader": [
|
|
413
|
+
"The drawing is hidden from assistive tech; a visually hidden table or caption carries every value."
|
|
414
|
+
]
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
"slug": "gauge",
|
|
418
|
+
"name": "Gauge",
|
|
419
|
+
"package": "@nexub/react",
|
|
420
|
+
"automated": "pass",
|
|
421
|
+
"exceptions": [],
|
|
422
|
+
"keyboard": [],
|
|
423
|
+
"screenReader": [
|
|
424
|
+
"The drawing is hidden from assistive tech; a visually hidden table or caption carries every value."
|
|
425
|
+
]
|
|
426
|
+
},
|
|
405
427
|
{
|
|
406
428
|
"slug": "gradient-divider",
|
|
407
429
|
"name": "Gradient Divider",
|
|
@@ -429,6 +451,17 @@
|
|
|
429
451
|
"keyboard": [],
|
|
430
452
|
"screenReader": []
|
|
431
453
|
},
|
|
454
|
+
{
|
|
455
|
+
"slug": "heatmap",
|
|
456
|
+
"name": "Heatmap",
|
|
457
|
+
"package": "@nexub/react",
|
|
458
|
+
"automated": "pass",
|
|
459
|
+
"exceptions": [],
|
|
460
|
+
"keyboard": [],
|
|
461
|
+
"screenReader": [
|
|
462
|
+
"The drawing is hidden from assistive tech; a visually hidden table or caption carries every value."
|
|
463
|
+
]
|
|
464
|
+
},
|
|
432
465
|
{
|
|
433
466
|
"slug": "helper-text",
|
|
434
467
|
"name": "Helper Text",
|
|
@@ -481,6 +514,17 @@
|
|
|
481
514
|
"keyboard": [],
|
|
482
515
|
"screenReader": []
|
|
483
516
|
},
|
|
517
|
+
{
|
|
518
|
+
"slug": "kpi-tile",
|
|
519
|
+
"name": "KPI Tile",
|
|
520
|
+
"package": "@nexub/react",
|
|
521
|
+
"automated": "pass",
|
|
522
|
+
"exceptions": [],
|
|
523
|
+
"keyboard": [],
|
|
524
|
+
"screenReader": [
|
|
525
|
+
"The drawing is hidden from assistive tech; a visually hidden table or caption carries every value."
|
|
526
|
+
]
|
|
527
|
+
},
|
|
484
528
|
{
|
|
485
529
|
"slug": "label",
|
|
486
530
|
"name": "Label",
|
|
@@ -69,14 +69,17 @@ function Frame({
|
|
|
69
69
|
/* @__PURE__ */ jsx("line", { x1: "0", x2: VB_W, y1: zero, y2: zero, strokeWidth: "1", vectorEffect: "non-scaling-stroke", className: "stroke-[var(--color-outline)]" }),
|
|
70
70
|
children
|
|
71
71
|
] }),
|
|
72
|
-
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 grid auto-cols-fr grid-flow-col", children: data.map((row, i) => /* @__PURE__ */
|
|
73
|
-
/* @__PURE__ */ jsx("
|
|
74
|
-
|
|
75
|
-
/* @__PURE__ */ jsx("
|
|
76
|
-
/* @__PURE__ */
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
72
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 grid auto-cols-fr grid-flow-col", children: data.map((row, i) => /* @__PURE__ */ jsxs("div", { className: "group/col relative rounded-shape-tooltip transition-colors hover:bg-[color-mix(in_oklab,var(--color-on-surface)_6%,transparent)]", children: [
|
|
73
|
+
/* @__PURE__ */ jsx("span", { className: "absolute inset-y-0 start-1/2 w-px bg-[var(--color-outline)] opacity-0 transition-opacity group-hover/col:opacity-100" }),
|
|
74
|
+
/* @__PURE__ */ jsxs("div", { className: "pointer-events-none absolute top-1 start-1/2 z-10 min-w-max -translate-x-1/2 rounded-shape-tooltip bg-[var(--color-inverse-surface)] px-3 py-2 text-label-md text-[var(--color-inverse-on-surface)] opacity-0 shadow-elevation-2 transition-opacity group-hover/col:opacity-100 rtl:translate-x-1/2", children: [
|
|
75
|
+
/* @__PURE__ */ jsx("div", { className: "mb-1 font-semibold", children: String(row[index]) }),
|
|
76
|
+
series.map((s, j) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 tabular-nums", children: [
|
|
77
|
+
/* @__PURE__ */ jsx("span", { className: cn("size-2 shrink-0 rounded-full", BG[colorIndex(s.color, j)]) }),
|
|
78
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1", children: labels[j] }),
|
|
79
|
+
/* @__PURE__ */ jsx("span", { className: "ps-3", children: valueFormat(valueOf(row, s.key)) })
|
|
80
|
+
] }, s.key))
|
|
81
|
+
] })
|
|
82
|
+
] }, i)) })
|
|
80
83
|
] }),
|
|
81
84
|
/* @__PURE__ */ jsx("div", { className: "grid auto-cols-fr grid-flow-col text-center text-label-sm text-[var(--color-on-surface-muted)]", children: data.map((row, i) => /* @__PURE__ */ jsx("span", { className: "truncate px-0.5", children: String(row[index]) }, i)) })
|
|
82
85
|
] })
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { type ChartColor } from './Chart.js';
|
|
3
|
+
export interface GaugeProps extends Omit<React.HTMLAttributes<HTMLElement>, 'title' | 'color'> {
|
|
4
|
+
value: number;
|
|
5
|
+
/** @default 100 */
|
|
6
|
+
max?: number;
|
|
7
|
+
/** Accessible name, e.g. "Storage used". */
|
|
8
|
+
title: string;
|
|
9
|
+
/** Shown in the middle; defaults to the percentage. */
|
|
10
|
+
label?: React.ReactNode;
|
|
11
|
+
color?: ChartColor;
|
|
12
|
+
}
|
|
13
|
+
/** Progress toward a target on a 270° arc. */
|
|
14
|
+
export declare function Gauge({ value, max, title, label, color, className, ...props }: GaugeProps): React.JSX.Element;
|
|
15
|
+
export interface HeatmapProps extends Omit<React.HTMLAttributes<HTMLElement>, 'title'> {
|
|
16
|
+
/** Rows of values, e.g. one row per weekday, one column per week. */
|
|
17
|
+
data: number[][];
|
|
18
|
+
rowLabels: string[];
|
|
19
|
+
columnLabels?: string[];
|
|
20
|
+
title: string;
|
|
21
|
+
valueFormat?: (value: number) => string;
|
|
22
|
+
}
|
|
23
|
+
/** Intensity over a grid (activity by day and week, load by hour). Seven steps of the sequential palette. */
|
|
24
|
+
export declare function Heatmap({ data, rowLabels, columnLabels, title, valueFormat, className, ...props }: HeatmapProps): React.JSX.Element;
|
|
25
|
+
export interface FunnelProps extends Omit<React.HTMLAttributes<HTMLElement>, 'title' | 'color'> {
|
|
26
|
+
data: {
|
|
27
|
+
label: string;
|
|
28
|
+
value: number;
|
|
29
|
+
}[];
|
|
30
|
+
title: string;
|
|
31
|
+
valueFormat?: (value: number) => string;
|
|
32
|
+
color?: ChartColor;
|
|
33
|
+
}
|
|
34
|
+
/** Drop-off through ordered steps, with the conversion from the first step. */
|
|
35
|
+
export declare function Funnel({ data, title, valueFormat, color, className, ...props }: FunnelProps): React.JSX.Element;
|
|
36
|
+
export interface KpiTileProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
37
|
+
label: string;
|
|
38
|
+
value: React.ReactNode;
|
|
39
|
+
/** Change versus the previous period, e.g. 0.114 for +11.4%. */
|
|
40
|
+
change?: number;
|
|
41
|
+
/** For metrics where down is good (churn, costs). */
|
|
42
|
+
invert?: boolean;
|
|
43
|
+
/** Recent values for the sparkline, oldest first. */
|
|
44
|
+
trend?: number[];
|
|
45
|
+
}
|
|
46
|
+
/** A headline number with its change and a trend line. The change text carries good/bad colour; the trend stays the accent. */
|
|
47
|
+
export declare function KpiTile({ label, value, change, invert, trend, className, ...props }: KpiTileProps): React.JSX.Element;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { cn } from "../../utils/cn.js";
|
|
4
|
+
import { Sparkline } from "./Chart.js";
|
|
5
|
+
const FILL = ["fill-chart-1", "fill-chart-2", "fill-chart-3", "fill-chart-4", "fill-chart-5", "fill-chart-6", "fill-chart-7", "fill-chart-8"];
|
|
6
|
+
const STROKE = ["stroke-chart-1", "stroke-chart-2", "stroke-chart-3", "stroke-chart-4", "stroke-chart-5", "stroke-chart-6", "stroke-chart-7", "stroke-chart-8"];
|
|
7
|
+
const SEQ = ["fill-chart-seq-1", "fill-chart-seq-2", "fill-chart-seq-3", "fill-chart-seq-4", "fill-chart-seq-5", "fill-chart-seq-6", "fill-chart-seq-7"];
|
|
8
|
+
const ci = (c) => ((c ?? 1) - 1) % 8;
|
|
9
|
+
const fmt = (n) => n.toLocaleString();
|
|
10
|
+
function Gauge({ value, max = 100, title, label, color = 1, className, ...props }) {
|
|
11
|
+
const pct = Math.max(0, Math.min(1, value / max));
|
|
12
|
+
const r = 15.9155;
|
|
13
|
+
return /* @__PURE__ */ jsxs("figure", { "aria-label": title, className: cn("m-0 inline-flex flex-col items-center", className), ...props, children: [
|
|
14
|
+
/* @__PURE__ */ jsxs("div", { "aria-hidden": "true", className: "relative size-36", children: [
|
|
15
|
+
/* @__PURE__ */ jsxs("svg", { viewBox: "0 0 42 42", className: "size-full rotate-[135deg]", children: [
|
|
16
|
+
/* @__PURE__ */ jsx("circle", { cx: "21", cy: "21", r, fill: "none", strokeWidth: "4", strokeLinecap: "round", strokeDasharray: "75 25", className: "stroke-[var(--color-surface-container-high)]" }),
|
|
17
|
+
/* @__PURE__ */ jsx("circle", { cx: "21", cy: "21", r, fill: "none", strokeWidth: "4", strokeLinecap: "round", strokeDasharray: `${pct * 75} ${100 - pct * 75}`, className: STROKE[ci(color)] })
|
|
18
|
+
] }),
|
|
19
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center text-title-lg tabular-nums text-[var(--color-on-surface)]", children: label ?? `${Math.round(pct * 100)}%` })
|
|
20
|
+
] }),
|
|
21
|
+
/* @__PURE__ */ jsx("figcaption", { className: "sr-only", children: `${title}: ${fmt(value)} of ${fmt(max)} (${Math.round(pct * 100)}%)` })
|
|
22
|
+
] });
|
|
23
|
+
}
|
|
24
|
+
function Heatmap({ data, rowLabels, columnLabels, title, valueFormat = fmt, className, ...props }) {
|
|
25
|
+
const flat = data.flat();
|
|
26
|
+
const lo = Math.min(...flat);
|
|
27
|
+
const span = Math.max(...flat) - lo || 1;
|
|
28
|
+
const step = (v) => Math.min(SEQ.length - 1, Math.floor((v - lo) / span * SEQ.length));
|
|
29
|
+
const cols = Math.max(...data.map((r) => r.length));
|
|
30
|
+
return /* @__PURE__ */ jsxs("figure", { "aria-label": title, className: cn("m-0 flex gap-2", className), ...props, children: [
|
|
31
|
+
/* @__PURE__ */ jsx("div", { "aria-hidden": "true", className: "flex flex-col justify-around text-label-sm text-[var(--color-on-surface-muted)]", children: rowLabels.map((l) => /* @__PURE__ */ jsx("span", { children: l }, l)) }),
|
|
32
|
+
/* @__PURE__ */ jsx("svg", { "aria-hidden": "true", viewBox: `0 0 ${cols * 12} ${data.length * 12}`, className: "h-auto min-w-0 flex-1 rtl:-scale-x-100", children: data.map(
|
|
33
|
+
(row, y) => row.map((v, x) => /* @__PURE__ */ jsx("rect", { x: x * 12 + 1, y: y * 12 + 1, width: "10", height: "10", rx: "2", className: SEQ[step(v)], children: /* @__PURE__ */ jsx("title", { children: `${rowLabels[y]}${columnLabels?.[x] ? `, ${columnLabels[x]}` : ""}: ${valueFormat(v)}` }) }, `${x}-${y}`))
|
|
34
|
+
) }),
|
|
35
|
+
/* @__PURE__ */ jsxs("table", { className: "sr-only", children: [
|
|
36
|
+
/* @__PURE__ */ jsx("caption", { children: title }),
|
|
37
|
+
/* @__PURE__ */ jsx("tbody", { children: data.map((row, y) => /* @__PURE__ */ jsxs("tr", { children: [
|
|
38
|
+
/* @__PURE__ */ jsx("th", { scope: "row", children: rowLabels[y] }),
|
|
39
|
+
row.map((v, x) => /* @__PURE__ */ jsx("td", { children: valueFormat(v) }, x))
|
|
40
|
+
] }, y)) })
|
|
41
|
+
] })
|
|
42
|
+
] });
|
|
43
|
+
}
|
|
44
|
+
function Funnel({ data, title, valueFormat = fmt, color = 1, className, ...props }) {
|
|
45
|
+
const top = Math.max(1, data[0]?.value ?? 1);
|
|
46
|
+
return /* @__PURE__ */ jsxs("figure", { "aria-label": title, className: cn("m-0 flex flex-col gap-2", className), ...props, children: [
|
|
47
|
+
data.map((d) => {
|
|
48
|
+
const pct = Math.max(0, d.value) / top;
|
|
49
|
+
return /* @__PURE__ */ jsxs("div", { "aria-hidden": "true", className: "grid grid-cols-[8rem_1fr_4rem] items-center gap-3 text-body-sm", children: [
|
|
50
|
+
/* @__PURE__ */ jsx("span", { className: "truncate text-[var(--color-on-surface-variant)]", children: d.label }),
|
|
51
|
+
/* @__PURE__ */ jsx("svg", { viewBox: "0 0 100 10", preserveAspectRatio: "none", className: "h-7 w-full", children: /* @__PURE__ */ jsx("rect", { x: (100 - pct * 100) / 2, y: "0", width: pct * 100, height: "10", rx: "1.5", className: FILL[ci(color)] }) }),
|
|
52
|
+
/* @__PURE__ */ jsx("span", { className: "text-end tabular-nums text-[var(--color-on-surface)]", children: valueFormat(d.value) })
|
|
53
|
+
] }, d.label);
|
|
54
|
+
}),
|
|
55
|
+
/* @__PURE__ */ jsxs("table", { className: "sr-only", children: [
|
|
56
|
+
/* @__PURE__ */ jsx("caption", { children: title }),
|
|
57
|
+
/* @__PURE__ */ jsx("tbody", { children: data.map((d) => /* @__PURE__ */ jsxs("tr", { children: [
|
|
58
|
+
/* @__PURE__ */ jsx("th", { scope: "row", children: d.label }),
|
|
59
|
+
/* @__PURE__ */ jsx("td", { children: valueFormat(d.value) }),
|
|
60
|
+
/* @__PURE__ */ jsxs("td", { children: [
|
|
61
|
+
Math.round(Math.max(0, d.value) / top * 100),
|
|
62
|
+
"% of ",
|
|
63
|
+
data[0]?.label
|
|
64
|
+
] })
|
|
65
|
+
] }, d.label)) })
|
|
66
|
+
] })
|
|
67
|
+
] });
|
|
68
|
+
}
|
|
69
|
+
function KpiTile({ label, value, change, invert = false, trend, className, ...props }) {
|
|
70
|
+
const good = change === void 0 ? void 0 : invert ? change < 0 : change > 0;
|
|
71
|
+
const pct = change === void 0 ? "" : `${change > 0 ? "+" : change < 0 ? "\u2212" : ""}${Math.abs(change * 100).toFixed(1)}%`;
|
|
72
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex items-end justify-between gap-4 rounded-shape-container border border-[var(--color-outline-variant)] bg-[var(--color-surface-elevated)] p-4", className), ...props, children: [
|
|
73
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col gap-1", children: [
|
|
74
|
+
/* @__PURE__ */ jsx("span", { className: "text-label-md text-[var(--color-on-surface-variant)]", children: label }),
|
|
75
|
+
/* @__PURE__ */ jsx("span", { className: "text-headline-sm tabular-nums text-[var(--color-on-surface)]", children: value }),
|
|
76
|
+
change !== void 0 && /* @__PURE__ */ jsxs("span", { className: cn("text-label-md tabular-nums", good ? "text-[var(--color-success)]" : change === 0 ? "text-[var(--color-on-surface-variant)]" : "text-[var(--color-error)]"), children: [
|
|
77
|
+
pct,
|
|
78
|
+
" ",
|
|
79
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: "versus last period" })
|
|
80
|
+
] })
|
|
81
|
+
] }),
|
|
82
|
+
trend && trend.length > 1 && /* @__PURE__ */ jsx(Sparkline, { values: trend, area: true, color: 1, className: "h-10 w-28 shrink-0" })
|
|
83
|
+
] });
|
|
84
|
+
}
|
|
85
|
+
export {
|
|
86
|
+
Funnel,
|
|
87
|
+
Gauge,
|
|
88
|
+
Heatmap,
|
|
89
|
+
KpiTile
|
|
90
|
+
};
|
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
export { BarChart, LineChart, DonutChart, Sparkline, ChartLegend, niceMax, niceDomain } from './Chart.js';
|
|
2
|
+
export { Gauge, Heatmap, Funnel, KpiTile } from './MoreCharts.js';
|
|
3
|
+
export type { GaugeProps, HeatmapProps, FunnelProps, KpiTileProps } from './MoreCharts.js';
|
|
2
4
|
export type { BarChartProps, LineChartProps, DonutChartProps, SparklineProps, ChartLegendProps, ChartSeries, ChartColor } from './Chart.js';
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { BarChart, LineChart, DonutChart, Sparkline, ChartLegend, niceMax, niceDomain } from "./Chart.js";
|
|
2
|
+
import { Gauge, Heatmap, Funnel, KpiTile } from "./MoreCharts.js";
|
|
2
3
|
export {
|
|
3
4
|
BarChart,
|
|
4
5
|
ChartLegend,
|
|
5
6
|
DonutChart,
|
|
7
|
+
Funnel,
|
|
8
|
+
Gauge,
|
|
9
|
+
Heatmap,
|
|
10
|
+
KpiTile,
|
|
6
11
|
LineChart,
|
|
7
12
|
Sparkline,
|
|
8
13
|
niceDomain,
|
package/dist/index.d.ts
CHANGED
|
@@ -47,6 +47,8 @@ export type { CelebrateProps } from './components/Celebrate/index.js';
|
|
|
47
47
|
export { Aurora } from './components/Aurora/index.js';
|
|
48
48
|
export type { AuroraProps } from './components/Aurora/index.js';
|
|
49
49
|
export { BarChart, LineChart, DonutChart, Sparkline, ChartLegend, niceMax, niceDomain } from './components/Chart/index.js';
|
|
50
|
+
export { Gauge, Heatmap, Funnel, KpiTile } from './components/Chart/index.js';
|
|
51
|
+
export type { GaugeProps, HeatmapProps, FunnelProps, KpiTileProps } from './components/Chart/index.js';
|
|
50
52
|
export type { BarChartProps, LineChartProps, DonutChartProps, SparklineProps, ChartLegendProps, ChartSeries, ChartColor } from './components/Chart/index.js';
|
|
51
53
|
export type { AmountProps } from './components/Amount/index.js';
|
|
52
54
|
export { NumberTicker } from './components/NumberTicker/index.js';
|
package/dist/index.js
CHANGED
|
@@ -27,6 +27,7 @@ import { FloatingInput } from "./components/FloatingInput/index.js";
|
|
|
27
27
|
import { Celebrate } from "./components/Celebrate/index.js";
|
|
28
28
|
import { Aurora } from "./components/Aurora/index.js";
|
|
29
29
|
import { BarChart, LineChart, DonutChart, Sparkline, ChartLegend, niceMax, niceDomain } from "./components/Chart/index.js";
|
|
30
|
+
import { Gauge, Heatmap, Funnel, KpiTile } from "./components/Chart/index.js";
|
|
30
31
|
import { NumberTicker } from "./components/NumberTicker/index.js";
|
|
31
32
|
import { SearchInput } from "./components/SearchInput/index.js";
|
|
32
33
|
import { PasswordInput } from "./components/PasswordInput/index.js";
|
|
@@ -170,15 +171,19 @@ export {
|
|
|
170
171
|
FloatingInput,
|
|
171
172
|
FormError,
|
|
172
173
|
FormField,
|
|
174
|
+
Funnel,
|
|
175
|
+
Gauge,
|
|
173
176
|
GradientDivider,
|
|
174
177
|
Grid,
|
|
175
178
|
Heading,
|
|
179
|
+
Heatmap,
|
|
176
180
|
HelperText,
|
|
177
181
|
HoverCard,
|
|
178
182
|
IconButton,
|
|
179
183
|
Input,
|
|
180
184
|
Kbd,
|
|
181
185
|
KeyboardShortcut,
|
|
186
|
+
KpiTile,
|
|
182
187
|
Label,
|
|
183
188
|
LineChart,
|
|
184
189
|
Link,
|