@godxjp/ui 18.15.3 → 19.0.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.
Files changed (145) hide show
  1. package/dist/app/date-format-labels.d.ts +1 -1
  2. package/dist/app/time-format-labels.d.ts +1 -1
  3. package/dist/components/data-display/badge.d.ts +1 -1
  4. package/dist/components/data-display/card.d.ts +4 -13
  5. package/dist/components/data-display/card.js +2 -15
  6. package/dist/components/data-display/data-table.d.ts +3 -8
  7. package/dist/components/data-display/data-table.js +106 -68
  8. package/dist/components/data-display/descriptions.js +34 -5
  9. package/dist/components/data-display/empty-state.js +1 -1
  10. package/dist/components/data-display/permission-matrix.js +7 -7
  11. package/dist/components/data-display/popover.js +2 -9
  12. package/dist/components/data-display/qr-code.d.ts +1 -1
  13. package/dist/components/data-display/scroll-area.d.ts +3 -1
  14. package/dist/components/data-display/scroll-area.js +150 -16
  15. package/dist/components/data-display/table.js +12 -3
  16. package/dist/components/data-entry/branch-scope-picker.js +4 -4
  17. package/dist/components/data-entry/calendar.js +15 -24
  18. package/dist/components/data-entry/cascader.js +67 -71
  19. package/dist/components/data-entry/checkbox.js +9 -1
  20. package/dist/components/data-entry/command.d.ts +6 -6
  21. package/dist/components/data-entry/date-picker.js +28 -15
  22. package/dist/components/data-entry/date-range-picker.js +10 -10
  23. package/dist/components/data-entry/form-field.js +7 -3
  24. package/dist/components/data-entry/index.d.ts +2 -1
  25. package/dist/components/data-entry/input.js +8 -19
  26. package/dist/components/data-entry/label.js +4 -2
  27. package/dist/components/data-entry/month-picker.js +16 -15
  28. package/dist/components/data-entry/month-range-picker.js +17 -16
  29. package/dist/components/data-entry/radio.js +6 -1
  30. package/dist/components/data-entry/search-select.js +29 -29
  31. package/dist/components/data-entry/select.js +15 -18
  32. package/dist/components/data-entry/switch.js +7 -1
  33. package/dist/components/data-entry/textarea.d.ts +56 -0
  34. package/dist/components/data-entry/textarea.js +103 -40
  35. package/dist/components/data-entry/time-picker.js +52 -52
  36. package/dist/components/data-entry/toggle-group.d.ts +1 -0
  37. package/dist/components/data-entry/toggle.d.ts +1 -1
  38. package/dist/components/data-entry/transfer.js +14 -15
  39. package/dist/components/data-entry/tree-select.js +16 -22
  40. package/dist/components/data-entry/upload-crop-dialog.js +4 -4
  41. package/dist/components/data-entry/upload.d.ts +1 -1
  42. package/dist/components/data-entry/upload.js +46 -46
  43. package/dist/components/feedback/alert.js +7 -14
  44. package/dist/components/feedback/dialog.d.ts +23 -32
  45. package/dist/components/feedback/dialog.js +16 -13
  46. package/dist/components/feedback/sheet.d.ts +1 -1
  47. package/dist/components/feedback/sheet.js +14 -14
  48. package/dist/components/feedback/skeleton.js +13 -10
  49. package/dist/components/feedback/sonner.d.ts +1 -1
  50. package/dist/components/feedback/sonner.js +17 -8
  51. package/dist/components/feedback/tooltip.js +1 -1
  52. package/dist/components/general/activity.d.ts +44 -0
  53. package/dist/components/general/activity.js +65 -0
  54. package/dist/components/general/button.d.ts +2 -2
  55. package/dist/components/general/button.js +11 -24
  56. package/dist/components/general/index.d.ts +2 -0
  57. package/dist/components/general/index.js +2 -0
  58. package/dist/components/layout/app-shell.js +5 -3
  59. package/dist/components/layout/auth-divider.d.ts +9 -2
  60. package/dist/components/layout/auth-divider.js +7 -11
  61. package/dist/components/layout/index.d.ts +1 -0
  62. package/dist/components/layout/page-container.d.ts +1 -1
  63. package/dist/components/layout/page-container.js +32 -20
  64. package/dist/components/layout/resizable.d.ts +12 -12
  65. package/dist/components/layout/separator.d.ts +1 -0
  66. package/dist/components/layout/service-role-panel.js +47 -42
  67. package/dist/components/layout/sidebar.js +13 -1
  68. package/dist/components/layout/split-pane.d.ts +12 -1
  69. package/dist/components/layout/split-pane.js +13 -4
  70. package/dist/components/navigation/app-setting-picker.js +5 -14
  71. package/dist/components/navigation/dropdown-menu.js +10 -16
  72. package/dist/components/navigation/menubar.d.ts +3 -6
  73. package/dist/components/navigation/pagination-utils.d.ts +15 -2
  74. package/dist/components/navigation/pagination-utils.js +27 -21
  75. package/dist/components/navigation/pagination.d.ts +4 -4
  76. package/dist/components/navigation/pagination.js +2 -2
  77. package/dist/components/navigation/steps.js +31 -67
  78. package/dist/components/navigation/tabs.js +17 -5
  79. package/dist/components/query/infinite-query-state.js +2 -2
  80. package/dist/components/ui/accordion.d.ts +1 -1
  81. package/dist/components/ui/avatar.d.ts +1 -1
  82. package/dist/components/ui/avatar.js +29 -10
  83. package/dist/components/ui/input-otp.d.ts +8 -6
  84. package/dist/components/ui/rating.js +1 -1
  85. package/dist/components/ui/separator.d.ts +27 -2
  86. package/dist/components/ui/separator.js +42 -11
  87. package/dist/components/ui/toggle-group.d.ts +15 -5
  88. package/dist/components/ui/toggle-group.js +43 -18
  89. package/dist/components/ui/toggle.d.ts +66 -5
  90. package/dist/components/ui/toggle.js +64 -9
  91. package/dist/i18n/messages/en.json +8 -0
  92. package/dist/i18n/messages/ja.json +8 -0
  93. package/dist/i18n/messages/vi.json +8 -0
  94. package/dist/lib/control-styles.d.ts +14 -0
  95. package/dist/lib/control-styles.js +2 -0
  96. package/dist/props/components/data-display.prop.d.ts +85 -3
  97. package/dist/props/components/data-entry.prop.d.ts +17 -0
  98. package/dist/props/components/general.prop.d.ts +52 -1
  99. package/dist/props/components/layout.prop.d.ts +137 -2
  100. package/dist/props/registry.d.ts +116 -5
  101. package/dist/props/registry.js +165 -4
  102. package/dist/props/vocabulary/index.d.ts +2 -2
  103. package/dist/props/vocabulary/interaction.prop.d.ts +15 -0
  104. package/dist/props/vocabulary/layout.prop.d.ts +8 -0
  105. package/dist/styles/alert-layout.css +69 -3
  106. package/dist/styles/badge-layout.css +2 -2
  107. package/dist/styles/base.css +6 -0
  108. package/dist/styles/card-layout.css +27 -7
  109. package/dist/styles/control.css +1150 -29
  110. package/dist/styles/data-display-layout.css +185 -11
  111. package/dist/styles/data-entry-layout.css +352 -1
  112. package/dist/styles/density.css +2 -2
  113. package/dist/styles/dialog-layout.css +203 -4
  114. package/dist/styles/focus-ring.css +155 -0
  115. package/dist/styles/form-layout.css +50 -5
  116. package/dist/styles/index.css +1 -0
  117. package/dist/styles/layout.css +412 -23
  118. package/dist/styles/motion.css +178 -0
  119. package/dist/styles/navigation-layout.css +398 -22
  120. package/dist/styles/shell-layout.css +137 -39
  121. package/dist/styles/table-layout.css +153 -22
  122. package/dist/styles/toggle.css +59 -0
  123. package/dist/tokens/base.css +5 -0
  124. package/dist/tokens/components/activity.css +64 -0
  125. package/dist/tokens/components/badge.css +7 -0
  126. package/dist/tokens/components/card.css +14 -10
  127. package/dist/tokens/components/chart.css +1 -1
  128. package/dist/tokens/components/control.css +358 -8
  129. package/dist/tokens/components/data-display.css +75 -0
  130. package/dist/tokens/components/data-entry.css +25 -0
  131. package/dist/tokens/components/descriptions.css +22 -0
  132. package/dist/tokens/components/feedback.css +143 -4
  133. package/dist/tokens/components/form.css +16 -0
  134. package/dist/tokens/components/legal-document.css +5 -1
  135. package/dist/tokens/components/navigation.css +109 -0
  136. package/dist/tokens/components/scroll-area.css +21 -0
  137. package/dist/tokens/components/separator.css +52 -0
  138. package/dist/tokens/components/shell.css +132 -20
  139. package/dist/tokens/components/table.css +52 -3
  140. package/dist/tokens/components/toggle.css +82 -0
  141. package/dist/tokens/components/upload.css +107 -0
  142. package/dist/tokens/foundation.css +171 -10
  143. package/dist/tokens/semantic/layout.css +120 -1
  144. package/package.json +84 -68
  145. package/scripts/ui-audit.mjs +51 -2
@@ -3,19 +3,153 @@ import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
5
5
  import { cn } from "../../lib/utils.js";
6
- const ScrollArea = React.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
7
- ScrollAreaPrimitive.Root,
8
- {
9
- ref,
10
- className: cn("relative overflow-hidden", className),
11
- ...props,
12
- children: [
13
- /* @__PURE__ */ jsx(ScrollAreaPrimitive.Viewport, { tabIndex: 0, className: "size-full rounded-[inherit]", children }),
14
- /* @__PURE__ */ jsx(ScrollBar, {}),
15
- /* @__PURE__ */ jsx(ScrollAreaPrimitive.Corner, {})
16
- ]
6
+ const ANCHOR_OFFSET_TOKEN = "--scroll-area-anchor-offset";
7
+ const ANCHOR_OFFSET_FALLBACK_PX = 48;
8
+ const ROOT_FONT_SIZE_FALLBACK_PX = 16;
9
+ function cssLengthToPx(value, rootFontSize) {
10
+ const match = /^(-?\d*\.?\d+)(px|rem|em)?$/.exec(value.trim());
11
+ if (match == null) return void 0;
12
+ const amount = Number(match[1]);
13
+ if (!Number.isFinite(amount)) return void 0;
14
+ return match[2] === "rem" || match[2] === "em" ? amount * rootFontSize : amount;
15
+ }
16
+ function readAnchorOffset(element) {
17
+ if (typeof window === "undefined" || typeof window.getComputedStyle !== "function") {
18
+ return ANCHOR_OFFSET_FALLBACK_PX;
17
19
  }
18
- ));
20
+ const rootFontSize = cssLengthToPx(
21
+ window.getComputedStyle(element.ownerDocument.documentElement).fontSize || "",
22
+ ROOT_FONT_SIZE_FALLBACK_PX
23
+ ) ?? ROOT_FONT_SIZE_FALLBACK_PX;
24
+ const declared = window.getComputedStyle(element).getPropertyValue(ANCHOR_OFFSET_TOKEN);
25
+ return cssLengthToPx(declared, rootFontSize) ?? ANCHOR_OFFSET_FALLBACK_PX;
26
+ }
27
+ function resolveRowContainer(root) {
28
+ let container = root;
29
+ while (container.children.length === 1 && container.firstElementChild) {
30
+ container = container.firstElementChild;
31
+ }
32
+ return container;
33
+ }
34
+ function findAnchorChild(root, scrollTop) {
35
+ const children = resolveRowContainer(root).children;
36
+ let low = 0;
37
+ let high = children.length - 1;
38
+ let found = null;
39
+ while (low <= high) {
40
+ const mid = Math.floor((low + high) / 2);
41
+ const child = children[mid];
42
+ if (!(child instanceof HTMLElement)) break;
43
+ if (child.offsetTop + child.offsetHeight > scrollTop) {
44
+ found = child;
45
+ high = mid - 1;
46
+ } else {
47
+ low = mid + 1;
48
+ }
49
+ }
50
+ return found;
51
+ }
52
+ function setScrollOffset(element, top) {
53
+ if (typeof element.scrollTo === "function") element.scrollTo({ top, behavior: "instant" });
54
+ else element.scrollTop = top;
55
+ }
56
+ function useBottomAnchor(viewport, enabled, offset, onAnchoredChange) {
57
+ const onAnchoredChangeRef = React.useRef(onAnchoredChange);
58
+ React.useEffect(() => {
59
+ onAnchoredChangeRef.current = onAnchoredChange;
60
+ }, [onAnchoredChange]);
61
+ React.useEffect(() => {
62
+ if (!viewport || !enabled) return;
63
+ const content = viewport.firstElementChild ?? viewport;
64
+ const band = offset ?? readAnchorOffset(viewport);
65
+ let anchored = true;
66
+ let record = null;
67
+ const distanceFromBottom = () => viewport.scrollHeight - viewport.clientHeight - viewport.scrollTop;
68
+ const setAnchored = (next) => {
69
+ if (anchored === next) return;
70
+ anchored = next;
71
+ onAnchoredChangeRef.current?.(next);
72
+ };
73
+ const remember = () => {
74
+ const node = findAnchorChild(content, viewport.scrollTop);
75
+ record = node ? { node, top: node.offsetTop - viewport.scrollTop } : null;
76
+ };
77
+ const pinToBottom = () => {
78
+ setScrollOffset(viewport, Math.max(0, viewport.scrollHeight - viewport.clientHeight));
79
+ };
80
+ const handleScroll = () => {
81
+ setAnchored(distanceFromBottom() <= band);
82
+ remember();
83
+ };
84
+ const handleContentChange = () => {
85
+ if (anchored) {
86
+ pinToBottom();
87
+ } else if (record && viewport.contains(record.node)) {
88
+ const target = Math.max(0, record.node.offsetTop - record.top);
89
+ if (target !== viewport.scrollTop) setScrollOffset(viewport, target);
90
+ setAnchored(distanceFromBottom() <= band);
91
+ }
92
+ remember();
93
+ };
94
+ pinToBottom();
95
+ remember();
96
+ viewport.addEventListener("scroll", handleScroll, { passive: true });
97
+ const observers = [];
98
+ if (typeof MutationObserver !== "undefined") {
99
+ const mutationObserver = new MutationObserver(handleContentChange);
100
+ mutationObserver.observe(content, { childList: true, subtree: true, characterData: true });
101
+ observers.push(mutationObserver);
102
+ }
103
+ if (typeof ResizeObserver !== "undefined") {
104
+ const resizeObserver = new ResizeObserver(handleContentChange);
105
+ resizeObserver.observe(viewport);
106
+ resizeObserver.observe(content);
107
+ observers.push(resizeObserver);
108
+ }
109
+ return () => {
110
+ viewport.removeEventListener("scroll", handleScroll);
111
+ for (const observer of observers) observer.disconnect();
112
+ };
113
+ }, [viewport, enabled, offset]);
114
+ }
115
+ const ScrollArea = React.forwardRef(
116
+ ({ className, children, viewportRef, anchor = "none", anchorOffset, onAnchoredChange, ...props }, ref) => {
117
+ const [viewport, setViewport] = React.useState(null);
118
+ const attachViewport = React.useCallback(
119
+ (node) => {
120
+ setViewport(node);
121
+ if (typeof viewportRef === "function") viewportRef(node);
122
+ else if (viewportRef)
123
+ viewportRef.current = node;
124
+ },
125
+ [viewportRef]
126
+ );
127
+ useBottomAnchor(viewport, anchor === "bottom", anchorOffset, onAnchoredChange);
128
+ return /* @__PURE__ */ jsxs(
129
+ ScrollAreaPrimitive.Root,
130
+ {
131
+ ref,
132
+ className: cn("relative overflow-hidden", className),
133
+ ...props,
134
+ children: [
135
+ /* @__PURE__ */ jsx(
136
+ ScrollAreaPrimitive.Viewport,
137
+ {
138
+ ref: attachViewport,
139
+ tabIndex: 0,
140
+ "data-slot": "scroll-area-viewport",
141
+ "data-anchor": anchor,
142
+ className: "size-full rounded-[inherit]",
143
+ children
144
+ }
145
+ ),
146
+ /* @__PURE__ */ jsx(ScrollBar, {}),
147
+ /* @__PURE__ */ jsx(ScrollAreaPrimitive.Corner, {})
148
+ ]
149
+ }
150
+ );
151
+ }
152
+ );
19
153
  ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
20
154
  const ScrollBar = React.forwardRef(({ className, orientation = "vertical", ...props }, ref) => /* @__PURE__ */ jsx(
21
155
  ScrollAreaPrimitive.ScrollAreaScrollbar,
@@ -23,13 +157,13 @@ const ScrollBar = React.forwardRef(({ className, orientation = "vertical", ...pr
23
157
  ref,
24
158
  orientation,
25
159
  className: cn(
26
- "flex touch-none transition-colors select-none",
27
- orientation === "vertical" && "h-full w-2.5 border-s border-s-transparent p-px",
28
- orientation === "horizontal" && "h-2.5 flex-col border-t border-t-transparent p-px",
160
+ "ui-scroll-area-bar flex touch-none transition-colors select-none",
161
+ orientation === "vertical" && "ui-scroll-area-bar--vertical h-full border-s border-s-transparent",
162
+ orientation === "horizontal" && "ui-scroll-area-bar--horizontal flex-col border-t border-t-transparent",
29
163
  className
30
164
  ),
31
165
  ...props,
32
- children: /* @__PURE__ */ jsx(ScrollAreaPrimitive.ScrollAreaThumb, { className: "bg-border relative flex-1 rounded-full" })
166
+ children: /* @__PURE__ */ jsx(ScrollAreaPrimitive.ScrollAreaThumb, { className: "ui-scroll-area-thumb bg-border relative flex-1" })
33
167
  }
34
168
  ));
35
169
  ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
@@ -34,7 +34,7 @@ const Table = React.forwardRef(
34
34
  {
35
35
  ref,
36
36
  "data-slot": "table",
37
- className: cn("w-full caption-bottom text-sm", bordered && "ui-table-bordered", className),
37
+ className: cn("w-full caption-bottom", bordered && "ui-table-bordered", className),
38
38
  ...props
39
39
  }
40
40
  )
@@ -45,14 +45,23 @@ const Table = React.forwardRef(
45
45
  Table.displayName = "Table";
46
46
  const TableHeader = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("thead", { ref, className: cn("[&_tr]:border-b", className), ...props }));
47
47
  TableHeader.displayName = "TableHeader";
48
- const TableBody = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("tbody", { ref, className: cn("[&_tr:last-child]:border-0", className), ...props }));
48
+ const TableBody = React.forwardRef(({ className, ...props }, ref) => (
49
+ // "The last row draws no rule" is an idiom, not a service-tunable constant, so it is a CSS
50
+ // rule (`[data-slot="table-body"] .ui-table-row:last-child`) rather than a utility. That only
51
+ // works because TableRow's own bottom rule moved to `.ui-table-row` in the same layer: while
52
+ // the row carried a `border-b` UTILITY, no `components` rule could ever zero it (gh#412).
53
+ /* @__PURE__ */ jsx("tbody", { ref, "data-slot": "table-body", className: cn(className), ...props })
54
+ ));
49
55
  TableBody.displayName = "TableBody";
50
56
  const TableRow = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
51
57
  "tr",
52
58
  {
53
59
  ref,
54
60
  className: cn(
55
- "ui-table-row hover:bg-accent/70 data-[state=selected]:bg-primary/[0.06] border-b transition-colors",
61
+ // The row rule itself is `.ui-table-row` in table-layout.css (--table-row-border-width),
62
+ // NOT a `border-b` utility — a utility sits in `@layer utilities` and would outrank the
63
+ // `components` rule that has to zero it on the last row (gh#319).
64
+ "ui-table-row hover:bg-accent/70 data-[state=selected]:bg-primary/[0.06] transition-colors",
56
65
  className
57
66
  ),
58
67
  ...props
@@ -66,7 +66,7 @@ const BranchScopePicker = React.forwardRef(
66
66
  id: rootId,
67
67
  "data-slot": "branch-scope-picker",
68
68
  "data-readonly": readOnly || void 0,
69
- className: cn("ui-branch-scope-picker flex flex-col gap-3", className),
69
+ className: cn("ui-branch-scope-picker", className),
70
70
  children
71
71
  }
72
72
  );
@@ -113,7 +113,7 @@ const BranchScopePicker = React.forwardRef(
113
113
  return surface(
114
114
  /* @__PURE__ */ jsxs(Fragment, { children: [
115
115
  /* @__PURE__ */ jsx(Text, { size: "sm", weight: "medium", children: value.mode === "all" ? resolvedAllLabel : resolvedSelectedLabel }),
116
- value.mode === "selected" && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: selectedBranches.map((branch) => /* @__PURE__ */ jsx(Badge, { variant: "outline", children: branch.name }, branch.id)) })
116
+ value.mode === "selected" && /* @__PURE__ */ jsx("div", { className: "ui-branch-scope-picker-badges", children: selectedBranches.map((branch) => /* @__PURE__ */ jsx(Badge, { variant: "outline", children: branch.name }, branch.id)) })
117
117
  ] })
118
118
  );
119
119
  }
@@ -145,7 +145,7 @@ const BranchScopePicker = React.forwardRef(
145
145
  "aria-errormessage": invalid ? mergeAriaIds(errorId, fieldA11y["aria-errormessage"]) : void 0
146
146
  }
147
147
  ),
148
- value.mode === "selected" && /* @__PURE__ */ jsxs("div", { className: "border-border flex flex-col gap-2 border-s-2 ps-4", children: [
148
+ value.mode === "selected" && /* @__PURE__ */ jsxs("div", { className: "ui-branch-scope-picker-subset", children: [
149
149
  searchable && branches.length > 0 && /* @__PURE__ */ jsx(
150
150
  SearchInput,
151
151
  {
@@ -169,7 +169,7 @@ const BranchScopePicker = React.forwardRef(
169
169
  role: "group",
170
170
  "aria-label": t("dataEntry.branchScope.listLabel"),
171
171
  tabIndex: 0,
172
- className: "max-h-64 overflow-y-auto",
172
+ className: "ui-branch-scope-picker-list",
173
173
  children: /* @__PURE__ */ jsx(
174
174
  CheckboxGroup,
175
175
  {
@@ -23,50 +23,41 @@ function Calendar({
23
23
  ...labels,
24
24
  labelNav: labels?.labelNav ?? (() => `${ariaLabel ?? "Calendar"} navigation`)
25
25
  },
26
- className: cn("relative w-fit p-3", className),
26
+ className: cn("ui-calendar", className),
27
27
  classNames: {
28
- months: cn("flex flex-col gap-4 sm:flex-row", classNames?.months),
29
- month: cn("relative flex flex-col gap-4", classNames?.month),
30
- month_caption: cn(
31
- "flex h-[length:var(--control-height)] w-full items-center justify-center px-8",
32
- classNames?.month_caption
33
- ),
34
- caption_label: cn("text-sm font-medium", classNames?.caption_label),
28
+ months: cn("ui-calendar-months", classNames?.months),
29
+ month: cn("ui-calendar-month", classNames?.month),
30
+ month_caption: cn("ui-calendar-caption", classNames?.month_caption),
31
+ caption_label: cn("ui-calendar-caption-label", classNames?.caption_label),
35
32
  // z-10 is load-bearing: `month` below is position:relative and later in the DOM, so
36
33
  // without a z-index it paints OVER these buttons — they render but never receive the
37
34
  // click (the month caption swallows it) and the calendar looks frozen.
38
- nav: cn(
39
- "absolute inset-x-0 top-3 z-10 flex items-center justify-between px-1",
40
- classNames?.nav
41
- ),
35
+ nav: cn("ui-calendar-nav", classNames?.nav),
42
36
  button_previous: cn(
43
37
  buttonVariants({ variant: "outline" }),
44
38
  controlIconSmClass,
45
- "bg-transparent p-0 opacity-70 hover:opacity-100",
39
+ "ui-calendar-nav-button",
46
40
  classNames?.button_previous
47
41
  ),
48
42
  button_next: cn(
49
43
  buttonVariants({ variant: "outline" }),
50
44
  controlIconSmClass,
51
- "bg-transparent p-0 opacity-70 hover:opacity-100",
45
+ "ui-calendar-nav-button",
52
46
  classNames?.button_next
53
47
  ),
54
- month_grid: cn("mt-4 w-full border-collapse", classNames?.month_grid),
48
+ month_grid: cn("ui-calendar-grid", classNames?.month_grid),
55
49
  weekdays: cn("flex", classNames?.weekdays),
56
- weekday: cn(
57
- "w-[length:var(--control-height)] rounded-md text-xs font-normal text-muted-foreground",
58
- classNames?.weekday
59
- ),
60
- week: cn("mt-2 flex w-full", classNames?.week),
50
+ weekday: cn("ui-calendar-weekday", classNames?.weekday),
51
+ week: cn("ui-calendar-week", classNames?.week),
61
52
  day: cn(
62
- "relative p-0 text-center text-sm focus-within:relative focus-within:z-20",
53
+ "ui-calendar-day",
63
54
  "[&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-outside)]:bg-accent/50",
64
55
  "[&:has([aria-selected].day-range-end)]:rounded-e-md",
65
56
  classNames?.day
66
57
  ),
67
58
  day_button: cn(
68
59
  buttonVariants({ variant: "ghost" }),
69
- "size-[length:var(--control-height)] p-0 font-normal aria-selected:opacity-100",
60
+ "ui-calendar-day-button",
70
61
  classNames?.day_button
71
62
  ),
72
63
  range_start: cn("day-range-start rounded-s-md", classNames?.range_start),
@@ -88,14 +79,14 @@ function Calendar({
88
79
  "day-outside text-muted-foreground aria-selected:text-muted-foreground",
89
80
  classNames?.outside
90
81
  ),
91
- disabled: cn("text-muted-foreground opacity-50", classNames?.disabled),
82
+ disabled: cn("ui-calendar-day-disabled", classNames?.disabled),
92
83
  hidden: cn("invisible", classNames?.hidden),
93
84
  ...classNames
94
85
  },
95
86
  components: {
96
87
  Chevron: ({ orientation, className: chevronClassName }) => {
97
88
  const Icon = orientation === "left" ? ChevronLeft : ChevronRight;
98
- return /* @__PURE__ */ jsx(Icon, { className: cn("size-4", chevronClassName), "aria-hidden": "true" });
89
+ return /* @__PURE__ */ jsx(Icon, { className: cn("ui-calendar-chevron", chevronClassName), "aria-hidden": "true" });
99
90
  }
100
91
  },
101
92
  ...props,
@@ -38,12 +38,12 @@ function CheckboxVisual({
38
38
  "aria-hidden": "true",
39
39
  "data-slot": "checkbox",
40
40
  "data-state": state,
41
+ "data-disabled": disabled ? "" : void 0,
41
42
  className: cn(
42
43
  "ui-checkbox inline-flex items-center justify-center",
43
44
  // The shared CSS fills the box for [data-state=checked]; mirror that fill for the
44
45
  // indeterminate ("some children selected") state so a partial parent reads as partial.
45
46
  "data-[state=indeterminate]:border-primary data-[state=indeterminate]:bg-primary data-[state=indeterminate]:text-primary-foreground",
46
- disabled && "cursor-not-allowed opacity-50",
47
47
  className
48
48
  ),
49
49
  children: indeterminate ? /* @__PURE__ */ jsx(Minus, { className: "ui-checkbox-icon", "aria-hidden": "true" }) : checked ? /* @__PURE__ */ jsx(Check, { className: "ui-checkbox-icon", "aria-hidden": "true" }) : null
@@ -175,13 +175,13 @@ function Cascader({
175
175
  else commitSingle([]);
176
176
  };
177
177
  const renderCascadeColumns = () => /* @__PURE__ */ jsxs(ScrollArea, { className: "w-full", children: [
178
- /* @__PURE__ */ jsx("div", { className: "flex max-h-[min(280px,50vh)]", children: columns.map((col, colIndex) => /* @__PURE__ */ jsx(
178
+ /* @__PURE__ */ jsx("div", { className: "ui-cascader-columns", children: columns.map((col, colIndex) => /* @__PURE__ */ jsx(
179
179
  "ul",
180
180
  {
181
181
  role: "listbox",
182
182
  "aria-orientation": "vertical",
183
183
  "aria-multiselectable": multiple ? true : void 0,
184
- className: "min-w-[9rem] border-e last:border-e-0",
184
+ className: "ui-cascader-column",
185
185
  children: col.map((node) => {
186
186
  const path = [...activePath.slice(0, colIndex), node.value];
187
187
  const hasChildren = (node.children?.length ?? 0) > 0 && node.isLeaf !== true;
@@ -197,11 +197,11 @@ function Cascader({
197
197
  "aria-haspopup": hasChildren ? "menu" : void 0,
198
198
  "aria-expanded": hasChildren ? active : void 0,
199
199
  disabled: node.disabled,
200
+ "data-disabled": node.disabled ? "" : void 0,
200
201
  className: cn(
201
- "flex w-full items-center gap-1 px-3 py-2 text-sm outline-none",
202
+ "ui-cascader-option",
202
203
  "hover:bg-accent hover:text-accent-foreground",
203
- active && "bg-accent/70 font-medium",
204
- node.disabled && "pointer-events-none opacity-50"
204
+ active && "bg-accent/70 font-medium"
205
205
  ),
206
206
  onMouseEnter: (
207
207
  // Hover-expand: a parent opens its children column; a leaf collapses any
@@ -221,9 +221,9 @@ function Cascader({
221
221
  className: "me-1"
222
222
  }
223
223
  ),
224
- !multiple && selected && /* @__PURE__ */ jsx(Check, { className: "me-1 size-4 shrink-0", "aria-hidden": "true" }),
224
+ !multiple && selected && /* @__PURE__ */ jsx(Check, { className: "ui-cascader-option-icon", "aria-hidden": "true" }),
225
225
  /* @__PURE__ */ jsx("span", { className: "flex-1 truncate text-start", children: node.label }),
226
- hasChildren && /* @__PURE__ */ jsx(ChevronRight, { className: "size-4 shrink-0 opacity-50", "aria-hidden": "true" })
226
+ hasChildren && /* @__PURE__ */ jsx(ChevronRight, { className: "ui-cascader-option-chevron", "aria-hidden": "true" })
227
227
  ]
228
228
  }
229
229
  ) }, node.value);
@@ -252,81 +252,77 @@ function Cascader({
252
252
  "w-full justify-start font-normal",
253
253
  controlOpenRingClass,
254
254
  // Reserve trailing room for the single clear-or-chevron overlay rendered below.
255
- "pe-9",
255
+ "ui-control-trigger-affixed",
256
256
  !displayLabel && "text-muted-foreground"
257
257
  ),
258
258
  children: /* @__PURE__ */ jsx("span", { className: "truncate", children: displayLabel ?? resolvedPlaceholder })
259
259
  }
260
260
  ) }),
261
- /* @__PURE__ */ jsxs(
262
- PopoverContent,
263
- {
264
- id: panelId,
265
- className: "w-auto min-w-[var(--radix-popover-trigger-width)] p-0",
266
- align: "start",
267
- children: [
268
- showSearch && /* @__PURE__ */ jsx(Command, { shouldFilter: false, children: /* @__PURE__ */ jsx(
269
- CommandInput,
270
- {
271
- placeholder: t("dataEntry.cascader.searchPlaceholder"),
272
- value: search,
273
- onValueChange: setSearch
274
- }
275
- ) }),
276
- isSearching ? /* @__PURE__ */ jsx(ScrollArea, { className: "max-h-[min(300px,50vh)]", children: /* @__PURE__ */ jsx(
277
- "div",
278
- {
279
- className: "p-1",
280
- role: "listbox",
281
- "aria-multiselectable": multiple ? true : void 0,
282
- children: searchResults.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-muted-foreground py-6 text-center text-sm", children: t("dataEntry.cascader.empty") }) : searchResults.map(({ path, labels }) => {
283
- const label = labels.join(" / ");
284
- const selected = multiple ? pathInValues(path, multiValue) : pathsEqual(path, singleValue);
285
- return /* @__PURE__ */ jsxs(
286
- "button",
287
- {
288
- type: "button",
289
- role: "option",
290
- "aria-selected": selected,
291
- className: cn(
292
- "flex w-full items-center rounded-sm px-2 py-1.5 text-sm outline-none",
293
- "hover:bg-accent hover:text-accent-foreground",
294
- selected && "bg-accent/60"
295
- ),
296
- onClick: () => handleSelectNode({ value: path.at(-1), label }, path),
297
- children: [
298
- multiple ? /* @__PURE__ */ jsx(CheckboxVisual, { checked: selected, className: "me-2" }) : /* @__PURE__ */ jsx(
299
- Check,
300
- {
301
- className: cn(
302
- "me-2 size-4 shrink-0",
303
- selected ? "opacity-100" : "opacity-0"
304
- ),
305
- "aria-hidden": "true"
306
- }
307
- ),
308
- /* @__PURE__ */ jsx("span", { className: "truncate text-start", children: label })
309
- ]
310
- },
311
- pathKey(path)
312
- );
313
- })
314
- }
315
- ) }) : renderCascadeColumns()
316
- ]
317
- }
318
- )
261
+ /* @__PURE__ */ jsxs(PopoverContent, { id: panelId, className: "ui-cascader-popover", align: "start", children: [
262
+ showSearch && /* @__PURE__ */ jsx(Command, { shouldFilter: false, children: /* @__PURE__ */ jsx(
263
+ CommandInput,
264
+ {
265
+ placeholder: t("dataEntry.cascader.searchPlaceholder"),
266
+ value: search,
267
+ onValueChange: setSearch
268
+ }
269
+ ) }),
270
+ isSearching ? /* @__PURE__ */ jsx(ScrollArea, { className: "ui-cascader-list", children: /* @__PURE__ */ jsx(
271
+ "div",
272
+ {
273
+ className: "ui-cascader-panel",
274
+ role: "listbox",
275
+ "aria-multiselectable": multiple ? true : void 0,
276
+ children: searchResults.length === 0 ? /* @__PURE__ */ jsx("p", { className: "ui-cascader-empty", children: t("dataEntry.cascader.empty") }) : searchResults.map(({ path, labels }) => {
277
+ const label = labels.join(" / ");
278
+ const selected = multiple ? pathInValues(path, multiValue) : pathsEqual(path, singleValue);
279
+ return /* @__PURE__ */ jsxs(
280
+ "button",
281
+ {
282
+ type: "button",
283
+ role: "option",
284
+ "aria-selected": selected,
285
+ className: cn(
286
+ "ui-cascader-result",
287
+ "hover:bg-accent hover:text-accent-foreground",
288
+ selected && "bg-accent/60"
289
+ ),
290
+ onClick: () => handleSelectNode({ value: path.at(-1), label }, path),
291
+ children: [
292
+ multiple ? /* @__PURE__ */ jsx(CheckboxVisual, { checked: selected, className: "me-2" }) : /* @__PURE__ */ jsx(
293
+ Check,
294
+ {
295
+ className: "ui-cascader-result-icon",
296
+ "data-selected": selected ? "true" : "false",
297
+ "aria-hidden": "true"
298
+ }
299
+ ),
300
+ /* @__PURE__ */ jsx("span", { className: "truncate text-start", children: label })
301
+ ]
302
+ },
303
+ pathKey(path)
304
+ );
305
+ })
306
+ }
307
+ ) }) : renderCascadeColumns()
308
+ ] })
319
309
  ] }),
320
- /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-y-0 end-3 flex items-center", children: showClear ? /* @__PURE__ */ jsx(
310
+ /* @__PURE__ */ jsx("div", { className: "ui-control-affix ui-cascader-affix", children: showClear ? /* @__PURE__ */ jsx(
321
311
  "button",
322
312
  {
323
313
  type: "button",
324
314
  "aria-label": t("dataEntry.cascader.clear"),
325
- className: "pointer-events-auto flex size-4 items-center justify-center rounded-sm opacity-50 hover:opacity-100 focus-visible:opacity-100",
315
+ className: "ui-control-affix-action",
326
316
  onClick: clearValue,
327
- children: /* @__PURE__ */ jsx(X, { className: "size-4", "aria-hidden": "true" })
317
+ children: /* @__PURE__ */ jsx(X, { className: "ui-control-affix-icon", "aria-hidden": "true" })
318
+ }
319
+ ) : /* @__PURE__ */ jsx(
320
+ ChevronsUpDown,
321
+ {
322
+ className: "ui-control-affix-icon ui-control-affix-indicator",
323
+ "aria-hidden": "true"
328
324
  }
329
- ) : /* @__PURE__ */ jsx(ChevronsUpDown, { className: "size-4 shrink-0 opacity-50", "aria-hidden": "true" }) })
325
+ ) })
330
326
  ] });
331
327
  }
332
328
  export {
@@ -11,7 +11,15 @@ const CheckboxRoot = React.forwardRef(({ className, ...props }, ref) => /* @__PU
11
11
  ref,
12
12
  "data-slot": "checkbox",
13
13
  className: cn(
14
- "peer ui-checkbox focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground shrink-0 shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
14
+ // `disabled:cursor-not-allowed disabled:opacity-50` and the checked fill are DELETED, not
15
+ // moved (#319): `.ui-checkbox:disabled, .ui-checkbox[data-disabled]` and
16
+ // `.ui-checkbox[data-state="checked"]` in styles/control.css already declare both, reading
17
+ // --disabled-opacity and --checkbox-checked-background. Utilities are layered AFTER
18
+ // components in Tailwind v4, so these literals were silently OUTRANKING those knobs — a
19
+ // service overriding --checkbox-checked-background got no fill change at all. Radix sets
20
+ // `data-state`/`data-disabled` on the root, so the CSS rules match. Rendering is unchanged:
21
+ // both knobs default to exactly the values these utilities hard-coded.
22
+ "peer ui-checkbox aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[state=checked]:border-primary data-[state=checked]:text-primary-foreground shrink-0 shadow-xs transition-shadow outline-none",
15
23
  className
16
24
  ),
17
25
  ...props,
@@ -5,7 +5,7 @@ export declare const Command: React.ForwardRefExoticComponent<Omit<{
5
5
  ref?: React.Ref<HTMLDivElement>;
6
6
  } & {
7
7
  asChild?: boolean;
8
- }, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild"> & {
8
+ }, "asChild" | "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
9
9
  label?: string;
10
10
  shouldFilter?: boolean;
11
11
  filter?: (value: string, search: string, keywords?: string[]) => number;
@@ -20,7 +20,7 @@ export declare const CommandInput: React.ForwardRefExoticComponent<Omit<Omit<Pic
20
20
  ref?: React.Ref<HTMLInputElement>;
21
21
  } & {
22
22
  asChild?: boolean;
23
- }, "key" | "asChild" | keyof React.InputHTMLAttributes<HTMLInputElement>>, "onChange" | "value" | "type"> & {
23
+ }, "asChild" | "key" | keyof React.InputHTMLAttributes<HTMLInputElement>>, "onChange" | "type" | "value"> & {
24
24
  value?: string;
25
25
  onValueChange?: (search: string) => void;
26
26
  } & React.RefAttributes<HTMLInputElement>, "ref"> & React.RefAttributes<HTMLInputElement>>;
@@ -30,7 +30,7 @@ export declare const CommandList: React.ForwardRefExoticComponent<Omit<{
30
30
  ref?: React.Ref<HTMLDivElement>;
31
31
  } & {
32
32
  asChild?: boolean;
33
- }, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild"> & {
33
+ }, "asChild" | "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
34
34
  label?: string;
35
35
  } & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
36
36
  export declare const CommandEmpty: React.ForwardRefExoticComponent<Omit<{
@@ -39,14 +39,14 @@ export declare const CommandEmpty: React.ForwardRefExoticComponent<Omit<{
39
39
  ref?: React.Ref<HTMLDivElement>;
40
40
  } & {
41
41
  asChild?: boolean;
42
- }, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild"> & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
42
+ }, "asChild" | "key" | keyof React.HTMLAttributes<HTMLDivElement>> & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
43
43
  export declare const CommandGroup: React.ForwardRefExoticComponent<Omit<{
44
44
  children?: React.ReactNode;
45
45
  } & Omit<Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
46
46
  ref?: React.Ref<HTMLDivElement>;
47
47
  } & {
48
48
  asChild?: boolean;
49
- }, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild">, "value" | "heading"> & {
49
+ }, "asChild" | "key" | keyof React.HTMLAttributes<HTMLDivElement>>, "heading" | "value"> & {
50
50
  heading?: React.ReactNode;
51
51
  value?: string;
52
52
  forceMount?: boolean;
@@ -57,7 +57,7 @@ export declare const CommandItem: React.ForwardRefExoticComponent<Omit<{
57
57
  ref?: React.Ref<HTMLDivElement>;
58
58
  } & {
59
59
  asChild?: boolean;
60
- }, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild">, "onSelect" | "value" | "disabled"> & {
60
+ }, "asChild" | "key" | keyof React.HTMLAttributes<HTMLDivElement>>, "disabled" | "onSelect" | "value"> & {
61
61
  disabled?: boolean;
62
62
  onSelect?: (value: string) => void;
63
63
  value?: string;