@kinetixui/ui 0.16.0 → 0.17.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/dist/index.d.ts +4 -4
- package/dist/index.js +146 -36
- package/package.json +3 -2
- package/src/components/badge.tsx +1 -1
- package/src/components/banner.tsx +1 -1
- package/src/components/button.tsx +12 -8
- package/src/components/color-picker.tsx +2 -4
- package/src/components/data-grid.tsx +44 -7
- package/src/components/inform.tsx +1 -1
- package/src/components/input.tsx +1 -1
- package/src/components/kbd.tsx +1 -1
- package/src/components/modal.tsx +2 -2
- package/src/components/multi-select.tsx +14 -2
- package/src/components/native-select.tsx +1 -1
- package/src/components/select.tsx +1 -1
- package/src/components/slider.tsx +7 -2
- package/src/components/tag.tsx +1 -1
- package/src/components/textarea.tsx +1 -1
- package/src/components/tour.tsx +45 -2
- package/src/components-a11y.test.tsx +90 -0
- package/src/components-keyboard.test.tsx +571 -0
- package/src/lib/utils.test.ts +12 -0
- package/src/lib/utils.ts +22 -1
- package/src/stories/AppBar.stories.tsx +1 -1
- package/tailwind.config.ts +6 -1
package/dist/index.d.ts
CHANGED
|
@@ -86,7 +86,7 @@ declare const Button: React$1.ForwardRefExoticComponent<ButtonProps & React$1.Re
|
|
|
86
86
|
*/
|
|
87
87
|
declare const inputVariants: (props?: ({
|
|
88
88
|
state?: "Default" | "Focus" | "Disabled" | "Error" | null | undefined;
|
|
89
|
-
corners?: "
|
|
89
|
+
corners?: "rounded" | "sharp" | "default" | "pill" | null | undefined;
|
|
90
90
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
91
91
|
interface InputProps extends Omit<React$1.InputHTMLAttributes<HTMLInputElement>, "size">, VariantProps<typeof inputVariants> {
|
|
92
92
|
}
|
|
@@ -337,7 +337,7 @@ declare const SheetClose: React$1.ForwardRefExoticComponent<DialogPrimitive.Dial
|
|
|
337
337
|
declare const SheetPortal: React$1.FC<DialogPrimitive.DialogPortalProps>;
|
|
338
338
|
declare const SheetOverlay: React$1.ForwardRefExoticComponent<Omit<DialogPrimitive.DialogOverlayProps & React$1.RefAttributes<HTMLDivElement>, "ref"> & React$1.RefAttributes<HTMLDivElement>>;
|
|
339
339
|
declare const sheetVariants: (props?: ({
|
|
340
|
-
side?: "
|
|
340
|
+
side?: "bottom" | "left" | "right" | "top" | null | undefined;
|
|
341
341
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
342
342
|
interface SheetContentProps extends React$1.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>, VariantProps<typeof sheetVariants> {
|
|
343
343
|
}
|
|
@@ -1303,7 +1303,7 @@ declare const ButtonGroupText: React$1.ForwardRefExoticComponent<React$1.HTMLAtt
|
|
|
1303
1303
|
*/
|
|
1304
1304
|
declare const nativeSelectVariants: (props?: ({
|
|
1305
1305
|
state?: "Default" | "Focus" | "Disabled" | "Error" | null | undefined;
|
|
1306
|
-
corners?: "
|
|
1306
|
+
corners?: "rounded" | "sharp" | "default" | "pill" | null | undefined;
|
|
1307
1307
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
1308
1308
|
interface NativeSelectProps extends Omit<React$1.SelectHTMLAttributes<HTMLSelectElement>, "size">, VariantProps<typeof nativeSelectVariants> {
|
|
1309
1309
|
}
|
|
@@ -1617,7 +1617,7 @@ interface DataGridColumn<TData> {
|
|
|
1617
1617
|
/** fixed to this side, not draggable, and excluded from the reorder sequence */
|
|
1618
1618
|
pinned?: "left" | "right";
|
|
1619
1619
|
sortable?: boolean;
|
|
1620
|
-
/** double-click
|
|
1620
|
+
/** double-click — or Enter / F2 on the focused cell — swaps it for a text input; commit fires on Enter/blur, Escape cancels */
|
|
1621
1621
|
editable?: boolean;
|
|
1622
1622
|
onCellEdit?: (row: TData, rowIndex: number, value: string) => void;
|
|
1623
1623
|
}
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,35 @@ import { cva } from "class-variance-authority";
|
|
|
7
7
|
|
|
8
8
|
// src/lib/utils.ts
|
|
9
9
|
import { clsx } from "clsx";
|
|
10
|
-
import {
|
|
10
|
+
import { extendTailwindMerge } from "tailwind-merge";
|
|
11
|
+
var twMerge = extendTailwindMerge({
|
|
12
|
+
extend: {
|
|
13
|
+
classGroups: {
|
|
14
|
+
"font-size": [
|
|
15
|
+
{
|
|
16
|
+
text: [
|
|
17
|
+
"display-lg",
|
|
18
|
+
"display-md",
|
|
19
|
+
"display-sm",
|
|
20
|
+
"headline-lg",
|
|
21
|
+
"headline-md",
|
|
22
|
+
"headline-sm",
|
|
23
|
+
"title-lg",
|
|
24
|
+
"title-md",
|
|
25
|
+
"title-sm",
|
|
26
|
+
"title-dialog",
|
|
27
|
+
"label-lg",
|
|
28
|
+
"label-md",
|
|
29
|
+
"label-sm",
|
|
30
|
+
"body-lg",
|
|
31
|
+
"body-md",
|
|
32
|
+
"body-sm"
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
});
|
|
11
39
|
function cn(...inputs) {
|
|
12
40
|
return twMerge(clsx(inputs));
|
|
13
41
|
}
|
|
@@ -28,12 +56,16 @@ var buttonVariants = cva(
|
|
|
28
56
|
variant: {
|
|
29
57
|
// bg primary / text on-primary; hover+active step down the blue ramp; disabled -> border grey
|
|
30
58
|
// rest = --primary; hover/active dim it via opacity so it works in BOTH themes.
|
|
59
|
+
// Alphas are bounded by contrast, not taste: text on the dimmed fill must stay >= 4.5:1
|
|
60
|
+
// (light: 90% = 5.05, 85% = 4.54, 80% = 4.11 fails) — enforced by check:contrast.
|
|
31
61
|
// (the old hover:bg-[--color-blue-600] was a hard-coded dark navy — on dark mode,
|
|
32
62
|
// where --primary is light, it inverted the button into the near-black background.)
|
|
33
|
-
Primary: "bg-primary text-primary-foreground hover:bg-primary/90 active:bg-primary/
|
|
63
|
+
Primary: "bg-primary text-primary-foreground hover:bg-primary/90 active:bg-primary/85 disabled:bg-border disabled:text-muted-foreground",
|
|
34
64
|
// rest = --secondary; hover/active promote to the --secondary-foreground fill with
|
|
35
|
-
// inverted text — theme-aware, unlike the previous hard-coded green primitives
|
|
36
|
-
|
|
65
|
+
// inverted text — theme-aware, unlike the previous hard-coded green primitives.
|
|
66
|
+
// Active is solid like hover: dimming it (90% was 3.97:1) drops the text under AA,
|
|
67
|
+
// and anything above ~97% is imperceptible. Enforced by check:contrast.
|
|
68
|
+
Secondary: "bg-secondary text-secondary-foreground hover:bg-secondary-foreground hover:text-secondary active:bg-secondary-foreground active:text-secondary focus-visible:bg-secondary disabled:bg-border disabled:text-muted-foreground",
|
|
37
69
|
// 1px outline; hover fills with accent + strengthens the edge to --ring so the
|
|
38
70
|
// state stays visible on the darkest surfaces (WCAG 2.2 SC 1.4.11); active pins primary
|
|
39
71
|
Outline: "border border-input bg-transparent text-foreground hover:border-ring hover:bg-accent active:border-primary active:bg-accent active:text-primary disabled:border-input disabled:text-muted-foreground disabled:bg-transparent",
|
|
@@ -45,11 +77,11 @@ var buttonVariants = cva(
|
|
|
45
77
|
},
|
|
46
78
|
size: {
|
|
47
79
|
// padding = Figma spacing/3 + spacing/2 ; type = Label Small (11/16, +0.5 tracking)
|
|
48
|
-
sm: "px-3 py-2 text-
|
|
80
|
+
sm: "px-3 py-2 text-label-sm",
|
|
49
81
|
// spacing/4 + spacing/3 ; Label Medium (12/16, +0.5)
|
|
50
|
-
md: "px-4 py-3 text-
|
|
82
|
+
md: "px-4 py-3 text-label-md",
|
|
51
83
|
// spacing/6 + spacing/3 ; Label Large (14/20, +0.1)
|
|
52
|
-
lg: "px-6 py-3 text-
|
|
84
|
+
lg: "px-6 py-3 text-label-lg",
|
|
53
85
|
// spacing/3 all round, square
|
|
54
86
|
icon: "p-3 [&>*:not(svg)]:sr-only"
|
|
55
87
|
},
|
|
@@ -71,9 +103,9 @@ var buttonVariants = cva(
|
|
|
71
103
|
compoundVariants: [
|
|
72
104
|
{ variant: "Destructive", class: "focus-visible:shadow-focus-destructive" },
|
|
73
105
|
{ variant: "Primary", state: "Hover", class: "bg-primary/90" },
|
|
74
|
-
{ variant: "Primary", state: "Active", class: "bg-primary/
|
|
106
|
+
{ variant: "Primary", state: "Active", class: "bg-primary/85" },
|
|
75
107
|
{ variant: "Secondary", state: "Hover", class: "bg-secondary-foreground text-secondary" },
|
|
76
|
-
{ variant: "Secondary", state: "Active", class: "bg-secondary-foreground
|
|
108
|
+
{ variant: "Secondary", state: "Active", class: "bg-secondary-foreground text-secondary" },
|
|
77
109
|
{ variant: "Outline", state: "Hover", class: "border-ring bg-accent" },
|
|
78
110
|
{ variant: "Outline", state: "Active", class: "border-primary bg-accent text-primary" },
|
|
79
111
|
{ variant: "Ghost", state: "Hover", class: "bg-accent ring-1 ring-inset ring-ring" },
|
|
@@ -110,7 +142,7 @@ import { jsx as jsx2 } from "react/jsx-runtime";
|
|
|
110
142
|
var inputVariants = cva2(
|
|
111
143
|
[
|
|
112
144
|
"flex w-full border border-input bg-background px-3 py-3",
|
|
113
|
-
"font-sans text-
|
|
145
|
+
"font-sans text-body-md text-foreground",
|
|
114
146
|
"placeholder:text-muted-foreground",
|
|
115
147
|
"outline-none transition-colors",
|
|
116
148
|
"focus-visible:border-primary focus-visible:shadow-focus",
|
|
@@ -160,7 +192,7 @@ import { jsx as jsx3 } from "react/jsx-runtime";
|
|
|
160
192
|
var textareaVariants = cva3(
|
|
161
193
|
[
|
|
162
194
|
"flex w-full min-h-[100px] resize-y rounded-sm border border-input bg-background px-3 py-3",
|
|
163
|
-
"font-sans text-
|
|
195
|
+
"font-sans text-body-md text-foreground",
|
|
164
196
|
"placeholder:text-muted-foreground",
|
|
165
197
|
"outline-none transition-colors",
|
|
166
198
|
"focus-visible:border-primary focus-visible:shadow-focus",
|
|
@@ -393,7 +425,7 @@ import { Slot as Slot2 } from "@radix-ui/react-slot";
|
|
|
393
425
|
import { cva as cva5 } from "class-variance-authority";
|
|
394
426
|
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
395
427
|
var badgeVariants = cva5(
|
|
396
|
-
"inline-flex items-center rounded-full px-2.5 py-1 text-
|
|
428
|
+
"inline-flex items-center rounded-full px-2.5 py-1 text-label-md font-sans transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
|
397
429
|
{
|
|
398
430
|
variants: {
|
|
399
431
|
variant: {
|
|
@@ -856,7 +888,7 @@ var SelectTrigger = React21.forwardRef(({ className, children, ...props }, ref)
|
|
|
856
888
|
ref,
|
|
857
889
|
className: cn(
|
|
858
890
|
"flex min-h-[44px] w-full items-center justify-between rounded-sm border border-input bg-background px-3 py-3",
|
|
859
|
-
"font-sans text-
|
|
891
|
+
"font-sans text-body-md text-foreground",
|
|
860
892
|
"data-[placeholder]:text-muted-foreground [&>span]:line-clamp-1",
|
|
861
893
|
"outline-none transition-colors",
|
|
862
894
|
"focus:border-primary focus:shadow-focus",
|
|
@@ -1041,7 +1073,7 @@ function Modal({
|
|
|
1041
1073
|
),
|
|
1042
1074
|
children: [
|
|
1043
1075
|
/* @__PURE__ */ jsxs11("header", { className: "flex items-center justify-between px-6 pb-3 pt-5", children: [
|
|
1044
|
-
/* @__PURE__ */ jsx25(DialogPrimitive2.Title, { className: "min-w-0 flex-1 text-
|
|
1076
|
+
/* @__PURE__ */ jsx25(DialogPrimitive2.Title, { className: "min-w-0 flex-1 text-title-dialog text-foreground", children: title }),
|
|
1045
1077
|
/* @__PURE__ */ jsx25(
|
|
1046
1078
|
DialogPrimitive2.Close,
|
|
1047
1079
|
{
|
|
@@ -1053,7 +1085,7 @@ function Modal({
|
|
|
1053
1085
|
] }),
|
|
1054
1086
|
/* @__PURE__ */ jsx25("div", { className: "h-px w-full bg-border" }),
|
|
1055
1087
|
/* @__PURE__ */ jsxs11("div", { className: "px-6 py-5", children: [
|
|
1056
|
-
description && /* @__PURE__ */ jsx25(DialogPrimitive2.Description, { className: "text-
|
|
1088
|
+
description && /* @__PURE__ */ jsx25(DialogPrimitive2.Description, { className: "text-body-md text-muted-foreground", children: description }),
|
|
1057
1089
|
children
|
|
1058
1090
|
] }),
|
|
1059
1091
|
/* @__PURE__ */ jsx25("div", { className: "h-px w-full bg-border" }),
|
|
@@ -1086,7 +1118,7 @@ function Skeleton({ className, ...props }) {
|
|
|
1086
1118
|
import * as React24 from "react";
|
|
1087
1119
|
import * as SliderPrimitive from "@radix-ui/react-slider";
|
|
1088
1120
|
import { jsx as jsx27, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1089
|
-
var Slider = React24.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxs12(
|
|
1121
|
+
var Slider = React24.forwardRef(({ className, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, ...props }, ref) => /* @__PURE__ */ jsxs12(
|
|
1090
1122
|
SliderPrimitive.Root,
|
|
1091
1123
|
{
|
|
1092
1124
|
ref,
|
|
@@ -1094,7 +1126,14 @@ var Slider = React24.forwardRef(({ className, ...props }, ref) => /* @__PURE__ *
|
|
|
1094
1126
|
...props,
|
|
1095
1127
|
children: [
|
|
1096
1128
|
/* @__PURE__ */ jsx27(SliderPrimitive.Track, { className: "relative h-1.5 w-full grow overflow-hidden rounded-full bg-muted", children: /* @__PURE__ */ jsx27(SliderPrimitive.Range, { className: "absolute h-full bg-primary" }) }),
|
|
1097
|
-
/* @__PURE__ */ jsx27(
|
|
1129
|
+
/* @__PURE__ */ jsx27(
|
|
1130
|
+
SliderPrimitive.Thumb,
|
|
1131
|
+
{
|
|
1132
|
+
"aria-label": ariaLabel,
|
|
1133
|
+
"aria-labelledby": ariaLabelledBy,
|
|
1134
|
+
className: "block size-4 rounded-full border border-primary/50 bg-background shadow transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-disabled"
|
|
1135
|
+
}
|
|
1136
|
+
)
|
|
1098
1137
|
]
|
|
1099
1138
|
}
|
|
1100
1139
|
));
|
|
@@ -1158,7 +1197,7 @@ import { cva as cva8 } from "class-variance-authority";
|
|
|
1158
1197
|
import { X as X4 } from "lucide-react";
|
|
1159
1198
|
import { jsx as jsx30, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1160
1199
|
var tagVariants = cva8(
|
|
1161
|
-
"inline-flex items-center gap-1 rounded-sm px-2 py-1 text-
|
|
1200
|
+
"inline-flex items-center gap-1 rounded-sm px-2 py-1 text-label-md font-sans",
|
|
1162
1201
|
{
|
|
1163
1202
|
variants: {
|
|
1164
1203
|
variant: {
|
|
@@ -2984,7 +3023,7 @@ var informVariants = cva14(
|
|
|
2984
3023
|
{
|
|
2985
3024
|
variants: {
|
|
2986
3025
|
variant: {
|
|
2987
|
-
information: "bg-info/10 text-info",
|
|
3026
|
+
information: "bg-info/10 text-info-on-container",
|
|
2988
3027
|
warning: "bg-warning/15 text-warning",
|
|
2989
3028
|
success: "bg-success/15 text-success",
|
|
2990
3029
|
error: "bg-destructive/10 text-destructive",
|
|
@@ -3890,7 +3929,7 @@ var Kbd = React66.forwardRef(({ className, asChild = false, ...props }, ref) =>
|
|
|
3890
3929
|
{
|
|
3891
3930
|
ref,
|
|
3892
3931
|
className: cn(
|
|
3893
|
-
"inline-flex h-5 min-w-5 items-center justify-center rounded-sm border border-input bg-muted px-1.5 font-sans text-
|
|
3932
|
+
"inline-flex h-5 min-w-5 items-center justify-center rounded-sm border border-input bg-muted px-1.5 font-sans text-label-sm text-muted-foreground",
|
|
3894
3933
|
className
|
|
3895
3934
|
),
|
|
3896
3935
|
...props
|
|
@@ -4003,7 +4042,7 @@ import { jsx as jsx75, jsxs as jsxs41 } from "react/jsx-runtime";
|
|
|
4003
4042
|
var nativeSelectVariants = cva19(
|
|
4004
4043
|
[
|
|
4005
4044
|
"flex w-full appearance-none border border-input bg-background px-3 py-3 pe-9",
|
|
4006
|
-
"font-sans text-
|
|
4045
|
+
"font-sans text-body-md text-foreground",
|
|
4007
4046
|
"outline-none transition-colors",
|
|
4008
4047
|
"focus-visible:border-primary focus-visible:shadow-focus",
|
|
4009
4048
|
"disabled:cursor-not-allowed disabled:opacity-disabled",
|
|
@@ -4104,7 +4143,7 @@ var bannerVariants = cva20(
|
|
|
4104
4143
|
{
|
|
4105
4144
|
variants: {
|
|
4106
4145
|
variant: {
|
|
4107
|
-
information: "border-info/20 bg-info/10 text-info",
|
|
4146
|
+
information: "border-info/20 bg-info/10 text-info-on-container",
|
|
4108
4147
|
warning: "border-warning/25 bg-warning/15 text-warning",
|
|
4109
4148
|
success: "border-success/25 bg-success/15 text-success",
|
|
4110
4149
|
error: "border-destructive/20 bg-destructive/10 text-destructive",
|
|
@@ -4627,6 +4666,12 @@ var MultiSelect = React79.forwardRef(
|
|
|
4627
4666
|
const current = value ?? internal;
|
|
4628
4667
|
const [open, setOpen] = React79.useState(false);
|
|
4629
4668
|
const [query, setQuery] = React79.useState("");
|
|
4669
|
+
const anchorRef = React79.useRef(null);
|
|
4670
|
+
const setRefs = (node) => {
|
|
4671
|
+
anchorRef.current = node;
|
|
4672
|
+
if (typeof ref === "function") ref(node);
|
|
4673
|
+
else if (ref) ref.current = node;
|
|
4674
|
+
};
|
|
4630
4675
|
const set = (next) => {
|
|
4631
4676
|
setInternal(next);
|
|
4632
4677
|
onValueChange?.(next);
|
|
@@ -4641,7 +4686,7 @@ var MultiSelect = React79.forwardRef(
|
|
|
4641
4686
|
/* @__PURE__ */ jsx85(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsxs50(
|
|
4642
4687
|
"div",
|
|
4643
4688
|
{
|
|
4644
|
-
ref,
|
|
4689
|
+
ref: setRefs,
|
|
4645
4690
|
role: "combobox",
|
|
4646
4691
|
"aria-expanded": open,
|
|
4647
4692
|
"aria-haspopup": "listbox",
|
|
@@ -4671,7 +4716,10 @@ var MultiSelect = React79.forwardRef(
|
|
|
4671
4716
|
{
|
|
4672
4717
|
align: "start",
|
|
4673
4718
|
className: "w-[var(--radix-popover-trigger-width)] p-0",
|
|
4674
|
-
|
|
4719
|
+
onCloseAutoFocus: (e) => {
|
|
4720
|
+
e.preventDefault();
|
|
4721
|
+
anchorRef.current?.focus();
|
|
4722
|
+
},
|
|
4675
4723
|
children: /* @__PURE__ */ jsxs50(Command, { shouldFilter: !creatable, children: [
|
|
4676
4724
|
/* @__PURE__ */ jsx85(CommandInput, { placeholder: "Search\u2026", value: query, onValueChange: setQuery }),
|
|
4677
4725
|
/* @__PURE__ */ jsxs50(CommandList, { children: [
|
|
@@ -4778,6 +4826,36 @@ var Tour = ({
|
|
|
4778
4826
|
const [rect, setRect] = React81.useState(null);
|
|
4779
4827
|
const step = steps[stepIndex];
|
|
4780
4828
|
React81.useEffect(() => setMounted(true), []);
|
|
4829
|
+
const titleId = React81.useId();
|
|
4830
|
+
const dialogRef = React81.useRef(null);
|
|
4831
|
+
const returnFocusRef = React81.useRef(null);
|
|
4832
|
+
React81.useEffect(() => {
|
|
4833
|
+
if (!open) return;
|
|
4834
|
+
returnFocusRef.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
4835
|
+
return () => returnFocusRef.current?.focus();
|
|
4836
|
+
}, [open]);
|
|
4837
|
+
React81.useEffect(() => {
|
|
4838
|
+
if (open && mounted) dialogRef.current?.focus();
|
|
4839
|
+
}, [open, mounted, stepIndex]);
|
|
4840
|
+
const trapTab = (e) => {
|
|
4841
|
+
if (e.key !== "Tab") return;
|
|
4842
|
+
const dialog = e.currentTarget;
|
|
4843
|
+
const focusable = Array.from(dialog.querySelectorAll("button:not([disabled]), [href], input:not([disabled]), [tabindex]:not([tabindex='-1'])"));
|
|
4844
|
+
if (focusable.length === 0) {
|
|
4845
|
+
e.preventDefault();
|
|
4846
|
+
return;
|
|
4847
|
+
}
|
|
4848
|
+
const first = focusable[0];
|
|
4849
|
+
const last = focusable[focusable.length - 1];
|
|
4850
|
+
const active = document.activeElement;
|
|
4851
|
+
if (e.shiftKey && (active === first || active === dialog)) {
|
|
4852
|
+
e.preventDefault();
|
|
4853
|
+
last.focus();
|
|
4854
|
+
} else if (!e.shiftKey && active === last) {
|
|
4855
|
+
e.preventDefault();
|
|
4856
|
+
first.focus();
|
|
4857
|
+
}
|
|
4858
|
+
};
|
|
4781
4859
|
React81.useEffect(() => {
|
|
4782
4860
|
if (!open || !step) return;
|
|
4783
4861
|
const update = () => {
|
|
@@ -4832,12 +4910,16 @@ var Tour = ({
|
|
|
4832
4910
|
/* @__PURE__ */ jsxs52(
|
|
4833
4911
|
"div",
|
|
4834
4912
|
{
|
|
4913
|
+
ref: dialogRef,
|
|
4835
4914
|
role: "dialog",
|
|
4836
4915
|
"aria-modal": "true",
|
|
4837
|
-
|
|
4916
|
+
"aria-labelledby": titleId,
|
|
4917
|
+
tabIndex: -1,
|
|
4918
|
+
onKeyDown: trapTab,
|
|
4919
|
+
className: "fixed z-overlay w-80 rounded-lg border border-border bg-popover p-4 font-sans text-popover-foreground shadow-lg outline-none transition-all duration-fast",
|
|
4838
4920
|
style: cardStyle,
|
|
4839
4921
|
children: [
|
|
4840
|
-
/* @__PURE__ */ jsx87("p", { className: "text-title-sm font-medium text-foreground", children: step.title }),
|
|
4922
|
+
/* @__PURE__ */ jsx87("p", { id: titleId, className: "text-title-sm font-medium text-foreground", children: step.title }),
|
|
4841
4923
|
/* @__PURE__ */ jsx87("div", { className: "mt-1.5 text-body-sm text-muted-foreground", children: step.content }),
|
|
4842
4924
|
/* @__PURE__ */ jsxs52("div", { className: "mt-4 flex items-center justify-between", children: [
|
|
4843
4925
|
/* @__PURE__ */ jsxs52("span", { className: "text-label-sm text-muted-foreground", children: [
|
|
@@ -5032,10 +5114,21 @@ function DataGrid({
|
|
|
5032
5114
|
window.addEventListener("pointermove", handleMove);
|
|
5033
5115
|
window.addEventListener("pointerup", handleUp);
|
|
5034
5116
|
}
|
|
5035
|
-
|
|
5036
|
-
|
|
5117
|
+
const restoreFocusRef = React83.useRef(null);
|
|
5118
|
+
React83.useEffect(() => {
|
|
5119
|
+
const target = restoreFocusRef.current;
|
|
5120
|
+
if (editing || !target) return;
|
|
5121
|
+
restoreFocusRef.current = null;
|
|
5122
|
+
containerRef.current?.querySelector(`[data-cell="${target.rowIndex}:${target.columnId}"]`)?.focus();
|
|
5123
|
+
}, [editing]);
|
|
5124
|
+
function endEdit(restoreFocus) {
|
|
5125
|
+
if (restoreFocus && editing) restoreFocusRef.current = { rowIndex: editing.rowIndex, columnId: editing.columnId };
|
|
5037
5126
|
setEditing(null);
|
|
5038
5127
|
}
|
|
5128
|
+
function commitEdit(column, row, rowIndex, value, restoreFocus = false) {
|
|
5129
|
+
column.onCellEdit?.(row, rowIndex, value);
|
|
5130
|
+
endEdit(restoreFocus);
|
|
5131
|
+
}
|
|
5039
5132
|
return /* @__PURE__ */ jsx89(
|
|
5040
5133
|
"div",
|
|
5041
5134
|
{
|
|
@@ -5074,9 +5167,16 @@ function DataGrid({
|
|
|
5074
5167
|
});
|
|
5075
5168
|
},
|
|
5076
5169
|
onClick: () => column.sortable && toggleSort(column.id),
|
|
5170
|
+
tabIndex: column.sortable ? 0 : void 0,
|
|
5171
|
+
onKeyDown: (e) => {
|
|
5172
|
+
if (column.sortable && e.target === e.currentTarget && (e.key === "Enter" || e.key === " ")) {
|
|
5173
|
+
e.preventDefault();
|
|
5174
|
+
toggleSort(column.id);
|
|
5175
|
+
}
|
|
5176
|
+
},
|
|
5077
5177
|
className: cn(
|
|
5078
5178
|
"relative flex h-10 shrink-0 select-none items-center gap-1 px-2 font-medium text-muted-foreground",
|
|
5079
|
-
column.sortable && "cursor-pointer hover:text-foreground",
|
|
5179
|
+
column.sortable && "cursor-pointer outline-none hover:text-foreground focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ring",
|
|
5080
5180
|
column.pinned && "bg-background"
|
|
5081
5181
|
),
|
|
5082
5182
|
style: { width, ...pinnedStyle },
|
|
@@ -5114,8 +5214,20 @@ function DataGrid({
|
|
|
5114
5214
|
"div",
|
|
5115
5215
|
{
|
|
5116
5216
|
role: "gridcell",
|
|
5217
|
+
"data-cell": `${rowIndex}:${column.id}`,
|
|
5117
5218
|
onDoubleClick: () => column.editable && setEditing({ rowIndex, columnId: column.id }),
|
|
5118
|
-
|
|
5219
|
+
tabIndex: column.editable ? 0 : void 0,
|
|
5220
|
+
onKeyDown: (e) => {
|
|
5221
|
+
if (column.editable && e.target === e.currentTarget && (e.key === "Enter" || e.key === "F2")) {
|
|
5222
|
+
e.preventDefault();
|
|
5223
|
+
setEditing({ rowIndex, columnId: column.id });
|
|
5224
|
+
}
|
|
5225
|
+
},
|
|
5226
|
+
className: cn(
|
|
5227
|
+
"flex shrink-0 items-center px-2",
|
|
5228
|
+
column.editable && "outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ring",
|
|
5229
|
+
column.pinned && "bg-background"
|
|
5230
|
+
),
|
|
5119
5231
|
style: { width, ...pinnedStyle },
|
|
5120
5232
|
children: isEditing ? /* @__PURE__ */ jsx89(
|
|
5121
5233
|
"input",
|
|
@@ -5124,8 +5236,8 @@ function DataGrid({
|
|
|
5124
5236
|
defaultValue: column.value ? String(column.value(row)) : "",
|
|
5125
5237
|
className: "h-7 w-full rounded-sm border-0 bg-transparent px-1 text-sm outline-none ring-1 ring-inset ring-ring",
|
|
5126
5238
|
onKeyDown: (e) => {
|
|
5127
|
-
if (e.key === "Enter") commitEdit(column, row, rowIndex, e.currentTarget.value);
|
|
5128
|
-
else if (e.key === "Escape")
|
|
5239
|
+
if (e.key === "Enter") commitEdit(column, row, rowIndex, e.currentTarget.value, true);
|
|
5240
|
+
else if (e.key === "Escape") endEdit(true);
|
|
5129
5241
|
},
|
|
5130
5242
|
onBlur: (e) => commitEdit(column, row, rowIndex, e.currentTarget.value)
|
|
5131
5243
|
}
|
|
@@ -5540,7 +5652,6 @@ var ColorPicker = React86.forwardRef(
|
|
|
5540
5652
|
max: 360,
|
|
5541
5653
|
step: 1,
|
|
5542
5654
|
onValueChange: ([nh]) => commit(nh ?? h, s, v, a),
|
|
5543
|
-
"aria-label": "Hue",
|
|
5544
5655
|
className: "relative flex h-4 w-full touch-none select-none items-center",
|
|
5545
5656
|
children: [
|
|
5546
5657
|
/* @__PURE__ */ jsx92(
|
|
@@ -5552,7 +5663,7 @@ var ColorPicker = React86.forwardRef(
|
|
|
5552
5663
|
}
|
|
5553
5664
|
}
|
|
5554
5665
|
),
|
|
5555
|
-
/* @__PURE__ */ jsx92(SliderPrimitive3.Thumb, { className: "block size-4 rounded-full border-2 border-white bg-background shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring" })
|
|
5666
|
+
/* @__PURE__ */ jsx92(SliderPrimitive3.Thumb, { "aria-label": "Hue", className: "block size-4 rounded-full border-2 border-white bg-background shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring" })
|
|
5556
5667
|
]
|
|
5557
5668
|
}
|
|
5558
5669
|
),
|
|
@@ -5564,7 +5675,6 @@ var ColorPicker = React86.forwardRef(
|
|
|
5564
5675
|
max: 100,
|
|
5565
5676
|
step: 1,
|
|
5566
5677
|
onValueChange: ([na]) => commit(h, s, v, (na ?? a * 100) / 100),
|
|
5567
|
-
"aria-label": "Alpha",
|
|
5568
5678
|
className: "relative flex h-4 w-full touch-none select-none items-center",
|
|
5569
5679
|
children: [
|
|
5570
5680
|
/* @__PURE__ */ jsx92(
|
|
@@ -5584,7 +5694,7 @@ var ColorPicker = React86.forwardRef(
|
|
|
5584
5694
|
)
|
|
5585
5695
|
}
|
|
5586
5696
|
),
|
|
5587
|
-
/* @__PURE__ */ jsx92(SliderPrimitive3.Thumb, { className: "block size-4 rounded-full border-2 border-white bg-background shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring" })
|
|
5697
|
+
/* @__PURE__ */ jsx92(SliderPrimitive3.Thumb, { "aria-label": "Alpha", className: "block size-4 rounded-full border-2 border-white bg-background shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring" })
|
|
5588
5698
|
]
|
|
5589
5699
|
}
|
|
5590
5700
|
),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kinetixui/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
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": [
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"@dnd-kit/sortable": "^10.0.0",
|
|
45
45
|
"@dnd-kit/utilities": "^3.2.2",
|
|
46
46
|
"@hookform/resolvers": "^5.9.1",
|
|
47
|
-
"@kinetixui/tokens": "0.
|
|
47
|
+
"@kinetixui/tokens": "0.17.0",
|
|
48
48
|
"@radix-ui/react-accordion": "^1.2.20",
|
|
49
49
|
"@radix-ui/react-alert-dialog": "^1.1.23",
|
|
50
50
|
"@radix-ui/react-aspect-ratio": "^1.1.15",
|
|
@@ -101,6 +101,7 @@
|
|
|
101
101
|
"@types/react": "^19.3.0",
|
|
102
102
|
"@types/react-dom": "^19.3.0",
|
|
103
103
|
"@vitejs/plugin-react": "^6.1.1",
|
|
104
|
+
"axe-core": "4.13.0",
|
|
104
105
|
"jsdom": "^30.0.1",
|
|
105
106
|
"react": "^19.3.0",
|
|
106
107
|
"react-dom": "^19.3.0",
|
package/src/components/badge.tsx
CHANGED
|
@@ -15,7 +15,7 @@ import { cn } from "../lib/utils";
|
|
|
15
15
|
* container. Hence the swapped bg/text on that one variant.
|
|
16
16
|
*/
|
|
17
17
|
const badgeVariants = cva(
|
|
18
|
-
"inline-flex items-center rounded-full px-2.5 py-1 text-
|
|
18
|
+
"inline-flex items-center rounded-full px-2.5 py-1 text-label-md font-sans transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
|
19
19
|
{
|
|
20
20
|
variants: {
|
|
21
21
|
variant: {
|
|
@@ -20,7 +20,7 @@ const bannerVariants = cva(
|
|
|
20
20
|
{
|
|
21
21
|
variants: {
|
|
22
22
|
variant: {
|
|
23
|
-
information: "border-info/20 bg-info/10 text-info",
|
|
23
|
+
information: "border-info/20 bg-info/10 text-info-on-container",
|
|
24
24
|
warning: "border-warning/25 bg-warning/15 text-warning",
|
|
25
25
|
success: "border-success/25 bg-success/15 text-success",
|
|
26
26
|
error: "border-destructive/20 bg-destructive/10 text-destructive",
|
|
@@ -36,14 +36,18 @@ const buttonVariants = cva(
|
|
|
36
36
|
variant: {
|
|
37
37
|
// bg primary / text on-primary; hover+active step down the blue ramp; disabled -> border grey
|
|
38
38
|
// rest = --primary; hover/active dim it via opacity so it works in BOTH themes.
|
|
39
|
+
// Alphas are bounded by contrast, not taste: text on the dimmed fill must stay >= 4.5:1
|
|
40
|
+
// (light: 90% = 5.05, 85% = 4.54, 80% = 4.11 fails) — enforced by check:contrast.
|
|
39
41
|
// (the old hover:bg-[--color-blue-600] was a hard-coded dark navy — on dark mode,
|
|
40
42
|
// where --primary is light, it inverted the button into the near-black background.)
|
|
41
43
|
Primary:
|
|
42
|
-
"bg-primary text-primary-foreground hover:bg-primary/90 active:bg-primary/
|
|
44
|
+
"bg-primary text-primary-foreground hover:bg-primary/90 active:bg-primary/85 disabled:bg-border disabled:text-muted-foreground",
|
|
43
45
|
// rest = --secondary; hover/active promote to the --secondary-foreground fill with
|
|
44
|
-
// inverted text — theme-aware, unlike the previous hard-coded green primitives
|
|
46
|
+
// inverted text — theme-aware, unlike the previous hard-coded green primitives.
|
|
47
|
+
// Active is solid like hover: dimming it (90% was 3.97:1) drops the text under AA,
|
|
48
|
+
// and anything above ~97% is imperceptible. Enforced by check:contrast.
|
|
45
49
|
Secondary:
|
|
46
|
-
"bg-secondary text-secondary-foreground hover:bg-secondary-foreground hover:text-secondary active:bg-secondary-foreground
|
|
50
|
+
"bg-secondary text-secondary-foreground hover:bg-secondary-foreground hover:text-secondary active:bg-secondary-foreground active:text-secondary focus-visible:bg-secondary disabled:bg-border disabled:text-muted-foreground",
|
|
47
51
|
// 1px outline; hover fills with accent + strengthens the edge to --ring so the
|
|
48
52
|
// state stays visible on the darkest surfaces (WCAG 2.2 SC 1.4.11); active pins primary
|
|
49
53
|
Outline:
|
|
@@ -58,11 +62,11 @@ const buttonVariants = cva(
|
|
|
58
62
|
},
|
|
59
63
|
size: {
|
|
60
64
|
// padding = Figma spacing/3 + spacing/2 ; type = Label Small (11/16, +0.5 tracking)
|
|
61
|
-
sm: "px-3 py-2 text-
|
|
65
|
+
sm: "px-3 py-2 text-label-sm",
|
|
62
66
|
// spacing/4 + spacing/3 ; Label Medium (12/16, +0.5)
|
|
63
|
-
md: "px-4 py-3 text-
|
|
67
|
+
md: "px-4 py-3 text-label-md",
|
|
64
68
|
// spacing/6 + spacing/3 ; Label Large (14/20, +0.1)
|
|
65
|
-
lg: "px-6 py-3 text-
|
|
69
|
+
lg: "px-6 py-3 text-label-lg",
|
|
66
70
|
// spacing/3 all round, square
|
|
67
71
|
icon: "p-3 [&>*:not(svg)]:sr-only",
|
|
68
72
|
},
|
|
@@ -84,9 +88,9 @@ const buttonVariants = cva(
|
|
|
84
88
|
compoundVariants: [
|
|
85
89
|
{ variant: "Destructive", class: "focus-visible:shadow-focus-destructive" },
|
|
86
90
|
{ variant: "Primary", state: "Hover", class: "bg-primary/90" },
|
|
87
|
-
{ variant: "Primary", state: "Active", class: "bg-primary/
|
|
91
|
+
{ variant: "Primary", state: "Active", class: "bg-primary/85" },
|
|
88
92
|
{ variant: "Secondary", state: "Hover", class: "bg-secondary-foreground text-secondary" },
|
|
89
|
-
{ variant: "Secondary", state: "Active", class: "bg-secondary-foreground
|
|
93
|
+
{ variant: "Secondary", state: "Active", class: "bg-secondary-foreground text-secondary" },
|
|
90
94
|
{ variant: "Outline", state: "Hover", class: "border-ring bg-accent" },
|
|
91
95
|
{ variant: "Outline", state: "Active", class: "border-primary bg-accent text-primary" },
|
|
92
96
|
{ variant: "Ghost", state: "Hover", class: "bg-accent ring-1 ring-inset ring-ring" },
|
|
@@ -220,7 +220,6 @@ const ColorPicker = React.forwardRef<HTMLDivElement, ColorPickerProps>(
|
|
|
220
220
|
max={360}
|
|
221
221
|
step={1}
|
|
222
222
|
onValueChange={([nh]) => commit(nh ?? h, s, v, a)}
|
|
223
|
-
aria-label="Hue"
|
|
224
223
|
className="relative flex h-4 w-full touch-none select-none items-center"
|
|
225
224
|
>
|
|
226
225
|
<SliderPrimitive.Track
|
|
@@ -230,7 +229,7 @@ const ColorPicker = React.forwardRef<HTMLDivElement, ColorPickerProps>(
|
|
|
230
229
|
"linear-gradient(to right, hsl(0 100% 50%), hsl(60 100% 50%), hsl(120 100% 50%), hsl(180 100% 50%), hsl(240 100% 50%), hsl(300 100% 50%), hsl(360 100% 50%))",
|
|
231
230
|
}}
|
|
232
231
|
/>
|
|
233
|
-
<SliderPrimitive.Thumb className="block size-4 rounded-full border-2 border-white bg-background shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring" />
|
|
232
|
+
<SliderPrimitive.Thumb aria-label="Hue" className="block size-4 rounded-full border-2 border-white bg-background shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring" />
|
|
234
233
|
</SliderPrimitive.Root>
|
|
235
234
|
|
|
236
235
|
{alpha && (
|
|
@@ -240,7 +239,6 @@ const ColorPicker = React.forwardRef<HTMLDivElement, ColorPickerProps>(
|
|
|
240
239
|
max={100}
|
|
241
240
|
step={1}
|
|
242
241
|
onValueChange={([na]) => commit(h, s, v, (na ?? a * 100) / 100)}
|
|
243
|
-
aria-label="Alpha"
|
|
244
242
|
className="relative flex h-4 w-full touch-none select-none items-center"
|
|
245
243
|
>
|
|
246
244
|
<SliderPrimitive.Track
|
|
@@ -256,7 +254,7 @@ const ColorPicker = React.forwardRef<HTMLDivElement, ColorPickerProps>(
|
|
|
256
254
|
style={{ backgroundImage: `linear-gradient(to right, transparent, hsl(${h} ${s}% ${v}%))` }}
|
|
257
255
|
/>
|
|
258
256
|
</SliderPrimitive.Track>
|
|
259
|
-
<SliderPrimitive.Thumb className="block size-4 rounded-full border-2 border-white bg-background shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring" />
|
|
257
|
+
<SliderPrimitive.Thumb aria-label="Alpha" className="block size-4 rounded-full border-2 border-white bg-background shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring" />
|
|
260
258
|
</SliderPrimitive.Root>
|
|
261
259
|
)}
|
|
262
260
|
|