@nadicodeai/ui 1.1.0 → 1.1.1
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/components/chart-time-series.d.ts.map +1 -1
- package/dist/components/chart-time-series.js +8 -7
- package/dist/components/combobox.js +1 -1
- package/dist/components/input-group.js +1 -1
- package/dist/components/input-otp.js +2 -2
- package/dist/components/slider.js +1 -1
- package/dist/eslint/rules/no-repainted-focus.js +10 -15
- package/dist/styles/globals.css +16 -0
- package/docs/contract.md +6 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chart-time-series.d.ts","sourceRoot":"","sources":["../../src/components/chart-time-series.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAY/B,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,kCAAkC,CAAC;
|
|
1
|
+
{"version":3,"file":"chart-time-series.d.ts","sourceRoot":"","sources":["../../src/components/chart-time-series.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAY/B,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,kCAAkC,CAAC;AAiH1C,KAAK,gBAAgB,GAAG;IACtB,2EAA2E;IAC3E,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,KAAK,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;AAEvD,KAAK,eAAe,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,UAAU,CAAC,GAAG;IACxE,IAAI,EAAE,SAAS,eAAe,EAAE,CAAC;IACjC,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACpC,2DAA2D;IAC3D,cAAc,EAAE,cAAc,CAAC;IAC/B,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAC;IACd,oEAAoE;IACpE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAC3C;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAsJF,iBAAS,cAAc,CAAC,EACtB,IAAI,EACJ,IAAI,EACJ,MAAM,EACN,cAAc,EACd,KAAK,EACL,MAAiB,EACjB,cAA8B,EAC9B,UAAU,EACV,UAAoB,EACpB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,eAAe,qBAyDjB;AAED,iBAAS,cAAc,CAAC,EACtB,IAAI,EACJ,IAAI,EACJ,MAAM,EACN,cAAc,EACd,KAAK,EACL,MAAiB,EACjB,cAA8B,EAC9B,UAAU,EACV,UAAoB,EACpB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,eAAe,qBA6EjB;AAED,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,CAAC;AAC1C,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,gBAAgB,EAAE,CAAC"}
|
|
@@ -34,16 +34,17 @@ import { CHART_ENTITY_OTHER_COLOR, entityColor, } from "./chart-entity-colors.js
|
|
|
34
34
|
* never shows, which would leave the `accessibilityLayer` keyboard path with
|
|
35
35
|
* no visible focus — and that utility also beats the global base-layer focus
|
|
36
36
|
* outline. chart.tsx is untouched, so we restore the canonical treatment
|
|
37
|
-
* through the `className` we pass to our own `ChartContainer` instances:
|
|
38
|
-
* `:focus-visible`
|
|
37
|
+
* through the `className` we pass to our own `ChartContainer` instances: this
|
|
38
|
+
* `:focus-visible` override sits on a distinct arbitrary-variant selector (a
|
|
39
39
|
* different tailwind-merge group AND modifier, so `cn()` keeps both) with
|
|
40
40
|
* higher CSS specificity, re-enabling the offset ink outline only while the
|
|
41
|
-
* svg is keyboard-focused.
|
|
41
|
+
* svg is keyboard-focused. `nc-focus-outline` writes `outline-style: solid`
|
|
42
|
+
* outright, which is what gets past `outline-hidden` — that utility parks
|
|
43
|
+
* `none` in the `--tw-outline-style` variable every Tailwind outline utility
|
|
44
|
+
* reads, so a plain `focus-visible:outline` restates a suppression.
|
|
42
45
|
*/
|
|
43
|
-
const CHART_SURFACE_FOCUS_CLASS = "[&_.recharts-surface:focus-visible]:outline " +
|
|
44
|
-
"[&_.recharts-surface:focus-visible]:outline-
|
|
45
|
-
"[&_.recharts-surface:focus-visible]:outline-ring " +
|
|
46
|
-
"[&_.recharts-surface:focus-visible]:outline-offset-[var(--outline-offset-focus)]";
|
|
46
|
+
const CHART_SURFACE_FOCUS_CLASS = "[&_.recharts-surface:focus-visible]:nc-focus-outline " +
|
|
47
|
+
"[&_.recharts-surface:focus-visible]:outline-ring";
|
|
47
48
|
/**
|
|
48
49
|
* The x ticks land at ONE constant stride across the range, and the stride is
|
|
49
50
|
* the smallest one whose labels still clear each other at the width the chart
|
|
@@ -44,7 +44,7 @@ function ComboboxSeparator({ className, ...props }) {
|
|
|
44
44
|
return (_jsx(ComboboxPrimitive.Separator, { "data-slot": "combobox-separator", className: cn("-mx-1 my-1 h-px bg-border", className), ...props }));
|
|
45
45
|
}
|
|
46
46
|
function ComboboxChips({ className, ...props }) {
|
|
47
|
-
return (_jsx(ComboboxPrimitive.Chips, { "data-slot": "combobox-chips", className: cn("flex min-h-touch-target flex-wrap items-center gap-1 rounded-md border border-input bg-background bg-clip-padding px-control-padding-inline py-1 text-sm transition-colors focus-within:
|
|
47
|
+
return (_jsx(ComboboxPrimitive.Chips, { "data-slot": "combobox-chips", className: cn("flex min-h-touch-target flex-wrap items-center gap-1 rounded-md border border-input bg-background bg-clip-padding px-control-padding-inline py-1 text-sm transition-colors focus-within:nc-focus-outline focus-within:outline-ring has-aria-invalid:border-destructive has-aria-invalid:nc-focus-outline has-aria-invalid:outline-destructive has-data-[slot=combobox-chip]:px-1 [@media(hover:hover)_and_(pointer:fine)]:min-h-control-height", className), ...props }));
|
|
48
48
|
}
|
|
49
49
|
function ComboboxChip({ className, children, showRemove = true, ...props }) {
|
|
50
50
|
return (_jsxs(ComboboxPrimitive.Chip, { "data-slot": "combobox-chip", className: cn("flex h-[calc(--spacing(5.25))] w-fit items-center justify-center gap-1 rounded-md bg-muted px-1.5 text-xs font-medium whitespace-nowrap text-foreground has-disabled:pointer-events-none has-disabled:cursor-not-allowed has-disabled:opacity-50 has-data-[slot=combobox-chip-remove]:pr-0", className), ...props, children: [children, showRemove && (_jsx(ComboboxPrimitive.ChipRemove, { render: _jsx(Button, { variant: "ghost", size: "icon-xs" }), className: "-ml-1 opacity-50 hover:opacity-100", "data-slot": "combobox-chip-remove", children: _jsx(XIcon, { className: "pointer-events-none" }) }))] }));
|
|
@@ -6,7 +6,7 @@ import { Button } from "./button.js";
|
|
|
6
6
|
import { Input } from "./input.js";
|
|
7
7
|
import { Textarea } from "./textarea.js";
|
|
8
8
|
function InputGroup({ className, ...props }) {
|
|
9
|
-
return (_jsx("div", { "data-slot": "input-group", role: "group", className: cn("group/input-group relative flex h-control-height min-h-touch-target w-full min-w-0 items-center rounded-md border border-input bg-background transition-colors outline-none in-data-[slot=combobox-content]:focus-within:border-inherit has-disabled:bg-input/50 has-disabled:opacity-50 not-in-data-[slot=combobox-content]:has-[[data-slot=input-group-control]:focus-visible]:
|
|
9
|
+
return (_jsx("div", { "data-slot": "input-group", role: "group", className: cn("group/input-group relative flex h-control-height min-h-touch-target w-full min-w-0 items-center rounded-md border border-input bg-background transition-colors outline-none in-data-[slot=combobox-content]:focus-within:border-inherit has-disabled:bg-input/50 has-disabled:opacity-50 not-in-data-[slot=combobox-content]:has-[[data-slot=input-group-control]:focus-visible]:nc-focus-outline has-[[data-slot=input-group-control]:focus-visible]:outline-ring has-[[data-slot][aria-invalid=true]]:border-destructive has-[[data-slot][aria-invalid=true]]:nc-focus-outline has-[[data-slot][aria-invalid=true]]:outline-destructive has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=inline-start]]:[&>input]:pl-1.5 [@media(hover:hover)_and_(pointer:fine)]:min-h-0", className), ...props }));
|
|
10
10
|
}
|
|
11
11
|
const inputGroupAddonVariants = cva("flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium text-muted-foreground select-none group-data-[disabled=true]/input-group:opacity-50 [&>kbd]:rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-4", {
|
|
12
12
|
variants: {
|
|
@@ -16,12 +16,12 @@ function InputOTP({ className, containerClassName, ...props }) {
|
|
|
16
16
|
suppressHydrationWarning: true }));
|
|
17
17
|
}
|
|
18
18
|
function InputOTPGroup({ className, ...props }) {
|
|
19
|
-
return (_jsx("div", { "data-slot": "input-otp-group", className: cn("flex items-center rounded-md has-aria-invalid:border-destructive has-aria-invalid:
|
|
19
|
+
return (_jsx("div", { "data-slot": "input-otp-group", className: cn("flex items-center rounded-md has-aria-invalid:border-destructive has-aria-invalid:nc-focus-outline has-aria-invalid:outline-destructive", className), ...props }));
|
|
20
20
|
}
|
|
21
21
|
function InputOTPSlot({ index, className, ...props }) {
|
|
22
22
|
const inputOTPContext = React.useContext(OTPInputContext);
|
|
23
23
|
const { char, hasFakeCaret, isActive } = inputOTPContext?.slots[index] ?? {};
|
|
24
|
-
return (_jsxs("div", { "data-slot": "input-otp-slot", "data-active": isActive, className: cn("relative flex size-control-height min-h-touch-target min-w-touch-target items-center justify-center border-y border-r border-input bg-background text-sm transition-[border-color,color,opacity] duration-[var(--nc-duration-confirm)] ease-[var(--nc-ease-out-strong)] motion-reduce:transition-none outline-none first:rounded-l-md first:border-l last:rounded-r-md aria-invalid:border-destructive data-[active=true]:z-10 data-[active=true]:
|
|
24
|
+
return (_jsxs("div", { "data-slot": "input-otp-slot", "data-active": isActive, className: cn("relative flex size-control-height min-h-touch-target min-w-touch-target items-center justify-center border-y border-r border-input bg-background text-sm transition-[border-color,color,opacity] duration-[var(--nc-duration-confirm)] ease-[var(--nc-ease-out-strong)] motion-reduce:transition-none outline-none first:rounded-l-md first:border-l last:rounded-r-md aria-invalid:border-destructive data-[active=true]:z-10 data-[active=true]:nc-focus-outline data-[active=true]:outline-ring data-[active=true]:aria-invalid:border-destructive data-[active=true]:aria-invalid:outline-destructive [@media(hover:hover)_and_(pointer:fine)]:min-h-0 [@media(hover:hover)_and_(pointer:fine)]:min-w-0", className), ...props, children: [char, hasFakeCaret && (_jsx("div", { className: "pointer-events-none absolute inset-0 flex items-center justify-center", children: _jsx("div", { className: "h-4 w-px bg-foreground motion-safe:animate-caret-blink motion-safe:duration-[var(--nc-duration-typing)]" }) }))] }));
|
|
25
25
|
}
|
|
26
26
|
function InputOTPSeparator({ ...props }) {
|
|
27
27
|
return (_jsx("div", { "data-slot": "input-otp-separator", className: "flex items-center [&_svg:not([class*='size-'])]:size-4", role: "separator", ...props, children: _jsx(MinusIcon, {}) }));
|
|
@@ -7,6 +7,6 @@ function Slider({ className, defaultValue, value, min = 0, max = 100, ...props }
|
|
|
7
7
|
: Array.isArray(defaultValue)
|
|
8
8
|
? defaultValue
|
|
9
9
|
: [min, max];
|
|
10
|
-
return (_jsx(SliderPrimitive.Root, { className: cn("data-horizontal:w-full data-vertical:h-full", className), "data-slot": "slider", defaultValue: defaultValue, value: value, min: min, max: max, thumbAlignment: "edge", ...props, children: _jsxs(SliderPrimitive.Control, { className: "relative flex w-full touch-none items-center select-none data-disabled:opacity-50 data-vertical:h-full data-vertical:min-h-40 data-vertical:w-auto data-vertical:flex-col", children: [_jsx(SliderPrimitive.Track, { "data-slot": "slider-track", className: "relative grow overflow-hidden rounded-full bg-muted select-none data-horizontal:h-1 data-horizontal:w-full data-vertical:h-full data-vertical:w-1", children: _jsx(SliderPrimitive.Indicator, { "data-slot": "slider-range", className: "bg-primary select-none data-horizontal:h-full data-vertical:w-full" }) }), Array.from({ length: _values.length }, (_, index) => (_jsx(SliderPrimitive.Thumb, { "data-slot": "slider-thumb", className: "relative block size-3 shrink-0 rounded-full border border-input bg-background transition-colors select-none after:absolute after:top-1/2 after:left-1/2 after:size-touch-target after:-translate-x-1/2 after:-translate-y-1/2 hover:
|
|
10
|
+
return (_jsx(SliderPrimitive.Root, { className: cn("data-horizontal:w-full data-vertical:h-full", className), "data-slot": "slider", defaultValue: defaultValue, value: value, min: min, max: max, thumbAlignment: "edge", ...props, children: _jsxs(SliderPrimitive.Control, { className: "relative flex w-full touch-none items-center select-none data-disabled:opacity-50 data-vertical:h-full data-vertical:min-h-40 data-vertical:w-auto data-vertical:flex-col", children: [_jsx(SliderPrimitive.Track, { "data-slot": "slider-track", className: "relative grow overflow-hidden rounded-full bg-muted select-none data-horizontal:h-1 data-horizontal:w-full data-vertical:h-full data-vertical:w-1", children: _jsx(SliderPrimitive.Indicator, { "data-slot": "slider-range", className: "bg-primary select-none data-horizontal:h-full data-vertical:w-full" }) }), Array.from({ length: _values.length }, (_, index) => (_jsx(SliderPrimitive.Thumb, { "data-slot": "slider-thumb", className: "relative block size-3 shrink-0 rounded-full border border-input bg-background transition-colors select-none after:absolute after:top-1/2 after:left-1/2 after:size-touch-target after:-translate-x-1/2 after:-translate-y-1/2 hover:nc-focus-outline active:nc-focus-outline disabled:pointer-events-none disabled:opacity-50 [@media(hover:hover)_and_(pointer:fine)]:after:hidden" }, index)))] }) }));
|
|
11
11
|
}
|
|
12
12
|
export { Slider };
|
|
@@ -10,15 +10,16 @@ import { classUtilities } from "./jsx-class-utility.js";
|
|
|
10
10
|
* the canonical outline".
|
|
11
11
|
*
|
|
12
12
|
* A focus-variant utility may only suppress the outline (outline-none,
|
|
13
|
-
* outline-hidden), restate the canonical treatment on a proxy target
|
|
14
|
-
* outline
|
|
15
|
-
* does when the group carries the indicator for its inner
|
|
16
|
-
* non-paint properties (z-index, colors of the element
|
|
13
|
+
* outline-hidden), restate the canonical treatment on a proxy target through
|
|
14
|
+
* the one shared `nc-focus-outline` utility plus a ring-role color (as
|
|
15
|
+
* input-group does when the group carries the indicator for its inner
|
|
16
|
+
* control), or move non-paint properties (z-index, colors of the element
|
|
17
|
+
* itself, …). Restating the style, width, and offset as separate utilities is
|
|
18
|
+
* a fork of that owner, so the raw outline width belongs to nobody.
|
|
17
19
|
*/
|
|
18
20
|
const RING_BASE = /^-?ring(?:-|$)/;
|
|
19
|
-
const ALLOWED_OUTLINE_BASE = new Set(["outline", "outline-none", "outline-hidden"]);
|
|
21
|
+
const ALLOWED_OUTLINE_BASE = new Set(["nc-focus-outline", "outline-none", "outline-hidden"]);
|
|
20
22
|
const ALLOWED_OUTLINE_COLOR = /^outline-(?:ring|sidebar-ring)$/;
|
|
21
|
-
const OUTLINE_TOKEN_LENGTH = /^outline-\[length:var\(--outline-width-focus\)\]$/;
|
|
22
23
|
const OUTLINE_OFFSET = /^-?outline-offset-/;
|
|
23
24
|
|
|
24
25
|
function baseOf(utility) {
|
|
@@ -39,15 +40,9 @@ function repaintedFocus(utility) {
|
|
|
39
40
|
if (base === utility) return undefined;
|
|
40
41
|
if (RING_BASE.test(base)) return "boxShadowFocus";
|
|
41
42
|
if (base.startsWith("[outline")) return "customFocusOutline";
|
|
43
|
+
if (ALLOWED_OUTLINE_BASE.has(base)) return undefined;
|
|
42
44
|
if (!base.startsWith("outline")) return undefined;
|
|
43
|
-
if (
|
|
44
|
-
ALLOWED_OUTLINE_BASE.has(base) ||
|
|
45
|
-
ALLOWED_OUTLINE_COLOR.test(base) ||
|
|
46
|
-
OUTLINE_TOKEN_LENGTH.test(base) ||
|
|
47
|
-
OUTLINE_OFFSET.test(base)
|
|
48
|
-
) {
|
|
49
|
-
return undefined;
|
|
50
|
-
}
|
|
45
|
+
if (ALLOWED_OUTLINE_COLOR.test(base) || OUTLINE_OFFSET.test(base)) return undefined;
|
|
51
46
|
return "customFocusOutline";
|
|
52
47
|
}
|
|
53
48
|
|
|
@@ -63,7 +58,7 @@ export const noRepaintedFocus = {
|
|
|
63
58
|
boxShadowFocus:
|
|
64
59
|
"Focus never paints a box-shadow: the base layer draws the offset outline. Drop {{utility}}.",
|
|
65
60
|
customFocusOutline:
|
|
66
|
-
"{{utility}} repaints focus with its own width or hue. The base layer owns the focus outline; a proxy target
|
|
61
|
+
"{{utility}} repaints focus with its own width or hue. The base layer owns the focus outline; a proxy target takes nc-focus-outline plus a ring-role color.",
|
|
67
62
|
},
|
|
68
63
|
},
|
|
69
64
|
create(context) {
|
package/dist/styles/globals.css
CHANGED
|
@@ -92,6 +92,22 @@
|
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
/*
|
|
96
|
+
* The proxy focus outline: the one restatement of the canonical treatment
|
|
97
|
+
* above, for an element that carries the indicator on another element's
|
|
98
|
+
* behalf (the input group around its control, the OTP slot for the hidden
|
|
99
|
+
* input, the chart wrapper for its svg, the slider thumb). The call site adds
|
|
100
|
+
* the hue with outline-ring or outline-destructive and nothing else.
|
|
101
|
+
* Declared as a utility so variant prefixes can gate it, and so the style is
|
|
102
|
+
* literally `solid` rather than Tailwind's `--tw-outline-style`, which any
|
|
103
|
+
* outline-none or outline-hidden on the same element sets to `none` for good.
|
|
104
|
+
*/
|
|
105
|
+
@utility nc-focus-outline {
|
|
106
|
+
outline-style: solid;
|
|
107
|
+
outline-width: var(--outline-width-focus);
|
|
108
|
+
outline-offset: var(--outline-offset-focus);
|
|
109
|
+
}
|
|
110
|
+
|
|
95
111
|
@layer components {
|
|
96
112
|
.nc-toast {
|
|
97
113
|
border: 0 !important;
|
package/docs/contract.md
CHANGED
|
@@ -39,7 +39,12 @@ with a box-shadow ring, a raw width, or its own hue
|
|
|
39
39
|
(`nadicodeai/no-repainted-focus` enforces this). A component may only
|
|
40
40
|
suppress the outline where a proxy target carries it (`InputGroup` shows the
|
|
41
41
|
indicator for its inner control; the OTP active slot for its hidden input) or
|
|
42
|
-
where a highlight ground replaces it (menu and command items).
|
|
42
|
+
where a highlight ground replaces it (menu and command items). The proxy
|
|
43
|
+
target draws it with `nc-focus-outline`, the one utility that carries the
|
|
44
|
+
outline's style, width, and offset, plus a ring-role color utility. Nothing
|
|
45
|
+
restates those three as separate utilities: an `outline-none` or
|
|
46
|
+
`outline-hidden` on the same element leaves Tailwind's own outline utilities
|
|
47
|
+
painting nothing.
|
|
43
48
|
|
|
44
49
|
`nc-glass` is the one glass material, and glass is floating chrome: menu,
|
|
45
50
|
popover, select, combobox, and navigation-menu popups, the command palette,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nadicodeai/ui",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"@assistant-ui/react": "0.15.1",
|
|
64
64
|
"@assistant-ui/react-markdown": "0.14.8",
|
|
65
65
|
"@base-ui/react": "^1.6.0",
|
|
66
|
-
"@nadicodeai/design-system": "1.1.
|
|
66
|
+
"@nadicodeai/design-system": "1.1.1",
|
|
67
67
|
"@paper-design/shaders": "0.0.78",
|
|
68
68
|
"class-variance-authority": "^0.7.1",
|
|
69
69
|
"clsx": "^2.1.1",
|