@kinetixui/ui 0.4.0 → 0.4.2
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/index.d.ts +11 -0
- package/dist/index.js +28 -4
- package/package.json +3 -3
- package/src/components/chart.tsx +41 -2
- package/src/components/number-input.tsx +5 -2
package/dist/index.d.ts
CHANGED
|
@@ -474,6 +474,14 @@ type ChartConfig = {
|
|
|
474
474
|
declare const ChartContainer: React$1.ForwardRefExoticComponent<Omit<React$1.ClassAttributes<HTMLDivElement> & React$1.HTMLAttributes<HTMLDivElement> & {
|
|
475
475
|
config: ChartConfig;
|
|
476
476
|
children: React$1.ComponentProps<typeof RechartsPrimitive.ResponsiveContainer>["children"];
|
|
477
|
+
/**
|
|
478
|
+
* Text alternative for the chart (WCAG 1.1.1). A chart conveys its data by
|
|
479
|
+
* colour + position, which a screen reader can't relay — pass a one-line
|
|
480
|
+
* summary of what it shows and the trend. Falls back to "Chart".
|
|
481
|
+
* Cartesian charts should also get `accessibilityLayer` on the Recharts
|
|
482
|
+
* primitive for keyboard data-point navigation.
|
|
483
|
+
*/
|
|
484
|
+
label?: string;
|
|
477
485
|
}, "ref"> & React$1.RefAttributes<HTMLDivElement>>;
|
|
478
486
|
declare const ChartStyle: ({ id, config }: {
|
|
479
487
|
id: string;
|
|
@@ -1120,6 +1128,9 @@ declare const Metric: React$1.ForwardRefExoticComponent<MetricProps & React$1.Re
|
|
|
1120
1128
|
|
|
1121
1129
|
/**
|
|
1122
1130
|
* NumberInput — a numeric field with increment / decrement controls.
|
|
1131
|
+
* The wrapper shows `--shadow-focus` on `focus-within`; each stepper also
|
|
1132
|
+
* draws its own `--ring` inset outline on `:focus-visible` so keyboard users
|
|
1133
|
+
* can tell which control is focused.
|
|
1123
1134
|
*/
|
|
1124
1135
|
interface NumberInputProps extends Omit<React$1.InputHTMLAttributes<HTMLInputElement>, "type" | "value" | "defaultValue" | "onChange"> {
|
|
1125
1136
|
value?: number;
|
package/dist/index.js
CHANGED
|
@@ -1545,14 +1545,38 @@ function useChart() {
|
|
|
1545
1545
|
if (!context) throw new Error("useChart must be used within a <ChartContainer />");
|
|
1546
1546
|
return context;
|
|
1547
1547
|
}
|
|
1548
|
-
var ChartContainer = React32.forwardRef(({ id, className, children, config, ...props }, ref) => {
|
|
1548
|
+
var ChartContainer = React32.forwardRef(({ id, className, children, config, label, role, "aria-label": ariaLabel, ...props }, ref) => {
|
|
1549
1549
|
const uniqueId = React32.useId();
|
|
1550
1550
|
const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`;
|
|
1551
|
+
const name = ariaLabel ?? label ?? "Chart";
|
|
1552
|
+
const innerRef = React32.useRef(null);
|
|
1553
|
+
const setRef = React32.useCallback(
|
|
1554
|
+
(node) => {
|
|
1555
|
+
innerRef.current = node;
|
|
1556
|
+
if (typeof ref === "function") ref(node);
|
|
1557
|
+
else if (ref) ref.current = node;
|
|
1558
|
+
},
|
|
1559
|
+
[ref]
|
|
1560
|
+
);
|
|
1561
|
+
React32.useEffect(() => {
|
|
1562
|
+
const el = innerRef.current;
|
|
1563
|
+
if (!el) return;
|
|
1564
|
+
const scrub = () => {
|
|
1565
|
+
el.querySelector(".recharts-surface")?.setAttribute("aria-label", name);
|
|
1566
|
+
el.querySelectorAll('[role="img"]:not([data-chart])').forEach((n) => n.removeAttribute("role"));
|
|
1567
|
+
};
|
|
1568
|
+
scrub();
|
|
1569
|
+
const mo = new MutationObserver(scrub);
|
|
1570
|
+
mo.observe(el, { childList: true, subtree: true });
|
|
1571
|
+
return () => mo.disconnect();
|
|
1572
|
+
}, [name]);
|
|
1551
1573
|
return /* @__PURE__ */ jsx37(ChartContext.Provider, { value: { config }, children: /* @__PURE__ */ jsxs15(
|
|
1552
1574
|
"div",
|
|
1553
1575
|
{
|
|
1554
1576
|
"data-chart": chartId,
|
|
1555
|
-
ref,
|
|
1577
|
+
ref: setRef,
|
|
1578
|
+
role: role ?? "img",
|
|
1579
|
+
"aria-label": name,
|
|
1556
1580
|
className: cn(
|
|
1557
1581
|
"flex aspect-video justify-center text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-none [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-sector]:outline-none [&_.recharts-surface]:outline-none",
|
|
1558
1582
|
className
|
|
@@ -3530,7 +3554,7 @@ var NumberInput = React60.forwardRef(
|
|
|
3530
3554
|
"aria-label": "Decrease",
|
|
3531
3555
|
disabled: disabled || min != null && current <= min,
|
|
3532
3556
|
onClick: () => set(current - step),
|
|
3533
|
-
className: "flex w-9 shrink-0 items-center justify-center text-muted-foreground outline-none transition-colors hover:bg-accent hover:text-foreground disabled:pointer-events-none disabled:opacity-50",
|
|
3557
|
+
className: "flex w-9 shrink-0 items-center justify-center text-muted-foreground outline-none transition-colors hover:bg-accent hover:text-foreground focus-visible:bg-accent focus-visible:text-foreground focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
|
|
3534
3558
|
children: /* @__PURE__ */ jsx66(Minus3, { className: "size-4" })
|
|
3535
3559
|
}
|
|
3536
3560
|
),
|
|
@@ -3560,7 +3584,7 @@ var NumberInput = React60.forwardRef(
|
|
|
3560
3584
|
"aria-label": "Increase",
|
|
3561
3585
|
disabled: disabled || max != null && current >= max,
|
|
3562
3586
|
onClick: () => set(current + step),
|
|
3563
|
-
className: "flex w-9 shrink-0 items-center justify-center text-muted-foreground outline-none transition-colors hover:bg-accent hover:text-foreground disabled:pointer-events-none disabled:opacity-50",
|
|
3587
|
+
className: "flex w-9 shrink-0 items-center justify-center text-muted-foreground outline-none transition-colors hover:bg-accent hover:text-foreground focus-visible:bg-accent focus-visible:text-foreground focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
|
|
3564
3588
|
children: /* @__PURE__ */ jsx66(Plus, { className: "size-4" })
|
|
3565
3589
|
}
|
|
3566
3590
|
)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kinetixui/ui",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "KinetixUI React components — CVA + Radix + Tailwind on the KinetixUI token contract. Also distributed via the kinetixui CLI as a component registry.",
|
|
6
6
|
"keywords": [
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"homepage": "https://kinetixui.com",
|
|
18
18
|
"repository": {
|
|
19
19
|
"type": "git",
|
|
20
|
-
"url": "https://github.com/
|
|
20
|
+
"url": "https://github.com/zedalleys/kinetixui.git",
|
|
21
21
|
"directory": "packages/ui"
|
|
22
22
|
},
|
|
23
23
|
"type": "module",
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
"react-dom": ">=18"
|
|
90
90
|
},
|
|
91
91
|
"devDependencies": {
|
|
92
|
-
"@kinetixui/tokens": "0.4.
|
|
92
|
+
"@kinetixui/tokens": "0.4.2",
|
|
93
93
|
"@testing-library/jest-dom": "^6.6.3",
|
|
94
94
|
"@testing-library/react": "^16.1.0",
|
|
95
95
|
"@testing-library/user-event": "^14.5.2",
|
package/src/components/chart.tsx
CHANGED
|
@@ -27,16 +27,55 @@ const ChartContainer = React.forwardRef<
|
|
|
27
27
|
React.ComponentProps<"div"> & {
|
|
28
28
|
config: ChartConfig;
|
|
29
29
|
children: React.ComponentProps<typeof RechartsPrimitive.ResponsiveContainer>["children"];
|
|
30
|
+
/**
|
|
31
|
+
* Text alternative for the chart (WCAG 1.1.1). A chart conveys its data by
|
|
32
|
+
* colour + position, which a screen reader can't relay — pass a one-line
|
|
33
|
+
* summary of what it shows and the trend. Falls back to "Chart".
|
|
34
|
+
* Cartesian charts should also get `accessibilityLayer` on the Recharts
|
|
35
|
+
* primitive for keyboard data-point navigation.
|
|
36
|
+
*/
|
|
37
|
+
label?: string;
|
|
30
38
|
}
|
|
31
|
-
>(({ id, className, children, config, ...props }, ref) => {
|
|
39
|
+
>(({ id, className, children, config, label, role, "aria-label": ariaLabel, ...props }, ref) => {
|
|
32
40
|
const uniqueId = React.useId();
|
|
33
41
|
const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`;
|
|
42
|
+
const name = ariaLabel ?? label ?? "Chart";
|
|
43
|
+
|
|
44
|
+
const innerRef = React.useRef<HTMLDivElement | null>(null);
|
|
45
|
+
const setRef = React.useCallback(
|
|
46
|
+
(node: HTMLDivElement | null) => {
|
|
47
|
+
innerRef.current = node;
|
|
48
|
+
if (typeof ref === "function") ref(node);
|
|
49
|
+
else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = node;
|
|
50
|
+
},
|
|
51
|
+
[ref],
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
// The container carries the text alternative (role="img" + aria-label).
|
|
55
|
+
// Recharts additionally (a) tags individual sector / dot <path>s with
|
|
56
|
+
// role="img" and no <title> — redundant noise that fails axe's svg-img-alt —
|
|
57
|
+
// and (b) leaves its accessibilityLayer <svg role="application"> unnamed.
|
|
58
|
+
// Fix both on the rendered output; re-run on Recharts re-layout.
|
|
59
|
+
React.useEffect(() => {
|
|
60
|
+
const el = innerRef.current;
|
|
61
|
+
if (!el) return;
|
|
62
|
+
const scrub = () => {
|
|
63
|
+
el.querySelector(".recharts-surface")?.setAttribute("aria-label", name);
|
|
64
|
+
el.querySelectorAll('[role="img"]:not([data-chart])').forEach((n) => n.removeAttribute("role"));
|
|
65
|
+
};
|
|
66
|
+
scrub();
|
|
67
|
+
const mo = new MutationObserver(scrub);
|
|
68
|
+
mo.observe(el, { childList: true, subtree: true });
|
|
69
|
+
return () => mo.disconnect();
|
|
70
|
+
}, [name]);
|
|
34
71
|
|
|
35
72
|
return (
|
|
36
73
|
<ChartContext.Provider value={{ config }}>
|
|
37
74
|
<div
|
|
38
75
|
data-chart={chartId}
|
|
39
|
-
ref={
|
|
76
|
+
ref={setRef}
|
|
77
|
+
role={role ?? "img"}
|
|
78
|
+
aria-label={name}
|
|
40
79
|
className={cn(
|
|
41
80
|
"flex aspect-video justify-center text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-none [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-sector]:outline-none [&_.recharts-surface]:outline-none",
|
|
42
81
|
className,
|
|
@@ -6,6 +6,9 @@ import { cn } from "../lib/utils";
|
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* NumberInput — a numeric field with increment / decrement controls.
|
|
9
|
+
* The wrapper shows `--shadow-focus` on `focus-within`; each stepper also
|
|
10
|
+
* draws its own `--ring` inset outline on `:focus-visible` so keyboard users
|
|
11
|
+
* can tell which control is focused.
|
|
9
12
|
*/
|
|
10
13
|
export interface NumberInputProps
|
|
11
14
|
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "type" | "value" | "defaultValue" | "onChange"> {
|
|
@@ -48,7 +51,7 @@ const NumberInput = React.forwardRef<HTMLInputElement, NumberInputProps>(
|
|
|
48
51
|
aria-label="Decrease"
|
|
49
52
|
disabled={disabled || (min != null && current <= min)}
|
|
50
53
|
onClick={() => set(current - step)}
|
|
51
|
-
className="flex w-9 shrink-0 items-center justify-center text-muted-foreground outline-none transition-colors hover:bg-accent hover:text-foreground disabled:pointer-events-none disabled:opacity-50"
|
|
54
|
+
className="flex w-9 shrink-0 items-center justify-center text-muted-foreground outline-none transition-colors hover:bg-accent hover:text-foreground focus-visible:bg-accent focus-visible:text-foreground focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50"
|
|
52
55
|
>
|
|
53
56
|
<Minus className="size-4" />
|
|
54
57
|
</button>
|
|
@@ -73,7 +76,7 @@ const NumberInput = React.forwardRef<HTMLInputElement, NumberInputProps>(
|
|
|
73
76
|
aria-label="Increase"
|
|
74
77
|
disabled={disabled || (max != null && current >= max)}
|
|
75
78
|
onClick={() => set(current + step)}
|
|
76
|
-
className="flex w-9 shrink-0 items-center justify-center text-muted-foreground outline-none transition-colors hover:bg-accent hover:text-foreground disabled:pointer-events-none disabled:opacity-50"
|
|
79
|
+
className="flex w-9 shrink-0 items-center justify-center text-muted-foreground outline-none transition-colors hover:bg-accent hover:text-foreground focus-visible:bg-accent focus-visible:text-foreground focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50"
|
|
77
80
|
>
|
|
78
81
|
<Plus className="size-4" />
|
|
79
82
|
</button>
|