@godxjp/ui 22.0.0 → 23.1.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 (115) hide show
  1. package/README.md +4 -4
  2. package/dist/app/date-format-labels.d.ts +14 -2
  3. package/dist/app/date-format-labels.js +1 -1
  4. package/dist/app/date-formats.d.ts +14 -2
  5. package/dist/app/date-formats.js +9 -2
  6. package/dist/components/data-display/data-table.d.ts +8 -1
  7. package/dist/components/data-display/data-table.js +9 -2
  8. package/dist/components/data-display/scroll-area.d.ts +24 -4
  9. package/dist/components/data-display/scroll-area.js +34 -72
  10. package/dist/components/data-entry/cascader.js +8 -6
  11. package/dist/components/data-entry/checkbox.js +3 -1
  12. package/dist/components/data-entry/choice-hit-target.d.ts +24 -0
  13. package/dist/components/data-entry/choice-hit-target.js +12 -0
  14. package/dist/components/data-entry/control-surface.d.ts +8 -0
  15. package/dist/components/data-entry/control-surface.js +10 -1
  16. package/dist/components/data-entry/form-field.js +5 -4
  17. package/dist/components/data-entry/input.js +1 -1
  18. package/dist/components/data-entry/number-input.js +27 -6
  19. package/dist/components/data-entry/radio.js +4 -3
  20. package/dist/components/data-entry/search-select.js +225 -95
  21. package/dist/components/data-entry/select.d.ts +76 -14
  22. package/dist/components/data-entry/select.js +495 -163
  23. package/dist/components/data-entry/slider.d.ts +3 -11
  24. package/dist/components/data-entry/slider.js +582 -93
  25. package/dist/components/data-entry/switch.js +3 -1
  26. package/dist/components/feedback/dialog.js +2 -2
  27. package/dist/components/feedback/sheet.js +2 -22
  28. package/dist/components/navigation/dropdown-menu.d.ts +11 -3
  29. package/dist/components/navigation/dropdown-menu.js +190 -15
  30. package/dist/components/navigation/filter-bar.js +2 -1
  31. package/dist/components/navigation/index.d.ts +0 -3
  32. package/dist/components/navigation/index.js +0 -78
  33. package/dist/components/navigation/tabs.js +16 -4
  34. package/dist/components/ui/index.d.ts +0 -3
  35. package/dist/components/ui/index.js +0 -3
  36. package/dist/components/ui/input-otp.d.ts +1 -1
  37. package/dist/components/ui/segmented.js +3 -1
  38. package/dist/components/ui/tag-input.js +3 -5
  39. package/dist/i18n/messages/en.json +5 -1
  40. package/dist/i18n/messages/ja.json +5 -1
  41. package/dist/i18n/messages/vi.json +5 -1
  42. package/dist/lib/breakpoint-token.d.ts +8 -0
  43. package/dist/lib/breakpoint-token.js +29 -0
  44. package/dist/lib/select-options.d.ts +21 -0
  45. package/dist/lib/select-options.js +46 -0
  46. package/dist/props/components/data-display.prop.d.ts +17 -7
  47. package/dist/props/components/data-entry.prop.d.ts +323 -30
  48. package/dist/props/components/index.d.ts +1 -1
  49. package/dist/props/components/navigation.prop.d.ts +25 -1
  50. package/dist/props/registry.d.ts +69 -1
  51. package/dist/props/registry.js +75 -0
  52. package/dist/props/vocabulary/data.prop.d.ts +5 -0
  53. package/dist/props/vocabulary/index.d.ts +1 -1
  54. package/dist/styles/control.css +246 -28
  55. package/dist/styles/data-display-layout.css +18 -10
  56. package/dist/styles/focus-ring.css +28 -0
  57. package/dist/styles/fonts.css +88 -4
  58. package/dist/styles/form-layout.css +4 -0
  59. package/dist/styles/layout.css +2 -1
  60. package/dist/styles/navigation-layout.css +24 -238
  61. package/dist/styles/shell-layout.css +148 -139
  62. package/dist/styles/table-layout.css +12 -5
  63. package/dist/tokens/components/control.css +12 -3
  64. package/dist/tokens/components/data-display.css +0 -5
  65. package/dist/tokens/components/navigation.css +5 -5
  66. package/dist/tokens/components/scroll-area.css +7 -0
  67. package/docs/CONSUMER-RULES.md +7 -2
  68. package/docs/CUSTOMER-THEMING.md +25 -9
  69. package/docs/DATETIME.md +11 -2
  70. package/docs/FRAME-COVERAGE-REPORT.md +4 -7
  71. package/docs/SPACING.md +35 -5
  72. package/docs/TOKENS.md +1 -1
  73. package/docs/data-display/data-table/index.tsx +44 -3
  74. package/docs/data-display/popover.tsx +4 -6
  75. package/docs/data-display/scroll-area.tsx +51 -45
  76. package/docs/data-display/table.tsx +1 -1
  77. package/docs/data-entry/date-picker.tsx +2 -3
  78. package/docs/data-entry/input-otp.tsx +5 -2
  79. package/docs/data-entry/segmented-in-filter-row.tsx +77 -0
  80. package/docs/data-entry/segmented.tsx +40 -1
  81. package/docs/data-entry/select-matrix.tsx +1 -2
  82. package/docs/data-entry/select.tsx +108 -3
  83. package/docs/data-entry/slider.tsx +125 -47
  84. package/docs/feedback/dialog.tsx +1 -2
  85. package/docs/feedback/sheet.tsx +1 -2
  86. package/docs/layout/app-shell-states.tsx +143 -0
  87. package/docs/navigation/dropdown-menu.tsx +77 -6
  88. package/docs/navigation/tabs.tsx +72 -0
  89. package/docs/roadmap/ai-chat-components.md +1 -2
  90. package/docs/roadmap/parity-audit-data-entry.md +44 -33
  91. package/docs/roadmap/parity-audit-layout-navigation-general.md +304 -310
  92. package/docs/roadmap/parity-backlog.md +2 -3
  93. package/docs/roadmap/tree-components.md +0 -1
  94. package/package.json +25 -55
  95. package/scripts/_agent-setup.mjs +18 -0
  96. package/scripts/guinea-pig-skill.md +12 -6
  97. package/scripts/ui-audit.mjs +185 -7
  98. package/scripts/visual-audit-rules.mjs +0 -7
  99. package/scripts/visual-audit.mjs +6 -27
  100. package/dist/components/navigation/context-menu.d.ts +0 -21
  101. package/dist/components/navigation/context-menu.js +0 -149
  102. package/dist/components/navigation/menubar.d.ts +0 -28
  103. package/dist/components/navigation/menubar.js +0 -136
  104. package/dist/components/navigation/navigation-menu.d.ts +0 -10
  105. package/dist/components/navigation/navigation-menu.js +0 -91
  106. package/dist/components/ui/context-menu.d.ts +0 -1
  107. package/dist/components/ui/context-menu.js +0 -2
  108. package/dist/components/ui/menubar.d.ts +0 -1
  109. package/dist/components/ui/menubar.js +0 -2
  110. package/dist/components/ui/navigation-menu.d.ts +0 -1
  111. package/dist/components/ui/navigation-menu.js +0 -2
  112. package/docs/FRAME-A11Y-CI.md +0 -349
  113. package/docs/navigation/context-menu.tsx +0 -128
  114. package/docs/navigation/menubar.tsx +0 -141
  115. package/docs/navigation/navigation-menu.tsx +0 -158
@@ -5,6 +5,7 @@ import { Switch as AriaSwitch } from "react-aria-components";
5
5
  import { Loader2 } from "lucide-react";
6
6
  import { cn } from "../../lib/utils.js";
7
7
  import { useFieldIdentity, useMirroredInputAttributes } from "../../lib/field-a11y.js";
8
+ import { withOwnHitTarget } from "./choice-hit-target.js";
8
9
  const Switch = React.forwardRef(
9
10
  ({
10
11
  className,
@@ -72,13 +73,14 @@ const Switch = React.forwardRef(
72
73
  event.preventDefault();
73
74
  handleCheckedChange(!isChecked);
74
75
  },
76
+ render: (domProps) => /* @__PURE__ */ jsx("label", { ...domProps, children: withOwnHitTarget(domProps.children) }),
75
77
  className: cn(
76
78
  // `.ui-switch:disabled, .ui-switch[data-disabled]` in styles/control.css already
77
79
  // declares both and reads --disabled-opacity. The utility was layered after
78
80
  // components, so it silently outranked that token. Byte-identical: the token
79
81
  // defaults to 0.5. (`shadow-xs` STAYS — .ui-switch declares --shadow-sm, so this
80
82
  // utility is the switch's real resting elevation, not a duplicate.)
81
- "peer ui-switch shadow-xs transition-all outline-none",
83
+ "peer ui-switch shadow-xs transition-all",
82
84
  className
83
85
  ),
84
86
  children: [
@@ -149,7 +149,7 @@ function DialogContent({
149
149
  showCloseButton ? /* @__PURE__ */ jsxs(
150
150
  DialogClose,
151
151
  {
152
- className: "ui-focus-ring transition-opacity focus:outline-hidden disabled:pointer-events-none",
152
+ className: "ui-focus-ring transition-opacity disabled:pointer-events-none",
153
153
  children: [
154
154
  /* @__PURE__ */ jsx(X, { className: "ui-dialog-close-icon", "aria-hidden": "true" }),
155
155
  /* @__PURE__ */ jsx("span", { className: "sr-only", children: t("feedback.alert.dismiss") })
@@ -265,7 +265,7 @@ function AlertDialogContent({
265
265
  {
266
266
  type: "button",
267
267
  "data-slot": "dialog-close",
268
- className: "ui-focus-ring transition-opacity focus:outline-hidden disabled:pointer-events-none",
268
+ className: "ui-focus-ring transition-opacity disabled:pointer-events-none",
269
269
  "aria-label": t("feedback.alert.dismiss"),
270
270
  onClick: () => {
271
271
  state.setOpen(false);
@@ -6,7 +6,7 @@ import { chain, mergeRefs } from "@react-aria/utils";
6
6
  import { Dialog as RacDialog, Modal, ModalOverlay } from "react-aria-components";
7
7
  import { cva } from "class-variance-authority";
8
8
  import { X } from "lucide-react";
9
- import { useMediaQuery } from "../../lib/hooks.js";
9
+ import { useMaxWidthBreakpoint } from "../../lib/breakpoint-token.js";
10
10
  import { cn } from "../../lib/utils.js";
11
11
  import { Slot } from "../../lib/slot.js";
12
12
  import { overlayHeaderToneClass } from "./overlay-header-tone.js";
@@ -15,28 +15,8 @@ import { useTranslation } from "../../i18n/use-translation.js";
15
15
  const toCssLength = (v) => typeof v === "number" ? `${v}px` : v;
16
16
  const SHEET_BREAKPOINT_TOKEN = "--sheet-responsive-breakpoint-width";
17
17
  const SHEET_BREAKPOINT_FALLBACK_QUERY = "(max-width: 768px)";
18
- function cssLengthToPx(value, rootFontSize) {
19
- const match = /^(-?\d*\.?\d+)(px|rem|em)?$/.exec(value.trim());
20
- if (match == null) return void 0;
21
- const amount = Number(match[1]);
22
- if (!Number.isFinite(amount)) return void 0;
23
- return match[2] === "rem" || match[2] === "em" ? amount * rootFontSize : amount;
24
- }
25
- function readSheetBreakpointQuery() {
26
- if (typeof document === "undefined" || typeof window.getComputedStyle !== "function") {
27
- return SHEET_BREAKPOINT_FALLBACK_QUERY;
28
- }
29
- const rootStyle = window.getComputedStyle(document.documentElement);
30
- const rootFontSize = cssLengthToPx(rootStyle.fontSize || "16px", 16) ?? 16;
31
- const px = cssLengthToPx(rootStyle.getPropertyValue(SHEET_BREAKPOINT_TOKEN), rootFontSize);
32
- return px == null ? SHEET_BREAKPOINT_FALLBACK_QUERY : `(max-width: ${String(px)}px)`;
33
- }
34
18
  function useSheetResponsiveMode(responsive = "side") {
35
- const [query, setQuery] = React.useState(SHEET_BREAKPOINT_FALLBACK_QUERY);
36
- React.useEffect(() => {
37
- setQuery(readSheetBreakpointQuery());
38
- }, []);
39
- const compact = useMediaQuery(query);
19
+ const compact = useMaxWidthBreakpoint(SHEET_BREAKPOINT_TOKEN, SHEET_BREAKPOINT_FALLBACK_QUERY);
40
20
  if (responsive === "side" || responsive === "bottom") return responsive;
41
21
  return compact ? "bottom" : "side";
42
22
  }
@@ -1,7 +1,7 @@
1
1
  import * as React from "react";
2
2
  import { type MenuItemRenderProps, type PopoverProps, type PopoverRenderProps } from "react-aria-components";
3
- import type { DropdownMenuPlacementProp } from "../../props/components/navigation.prop.js";
4
- export type { DropdownMenuPlacementProp } from "../../props/components/navigation.prop.js";
3
+ import type { DropdownMenuPlacementProp, DropdownMenuTriggerActionProp } from "../../props/components/navigation.prop.js";
4
+ export type { DropdownMenuPlacementProp, DropdownMenuTriggerActionProp, } from "../../props/components/navigation.prop.js";
5
5
  /**
6
6
  * Dịch trạng thái item của RAC sang bộ `data-*` mà Radix phát ra và CSS của kho đang bám.
7
7
  *
@@ -47,9 +47,17 @@ interface DropdownMenuPropsOwn {
47
47
  * `useLocale`), nên prop này còn trong API vì hợp đồng, nhưng không tự đảo hướng.
48
48
  */
49
49
  dir?: "ltr" | "rtl";
50
+ /** Ant Design `trigger`. @see DropdownMenuTriggerActionProp. Default `['click']`. */
51
+ trigger?: readonly DropdownMenuTriggerActionProp[];
52
+ /** Ant Design `disabled` — no gesture opens the menu (antd empties the trigger list). */
53
+ disabled?: boolean;
54
+ /** Ant Design `mouseEnterDelay`, in SECONDS, for `trigger={['hover']}`. Default 0.15. */
55
+ mouseEnterDelay?: number;
56
+ /** Ant Design `mouseLeaveDelay`, in SECONDS. Default 0.1 — long enough to cross the gap. */
57
+ mouseLeaveDelay?: number;
50
58
  }
51
59
  type DropdownMenuProps = React.PropsWithChildren<DropdownMenuPropsOwn>;
52
- export declare function DropdownMenu({ children, open, defaultOpen, onOpenChange, modal, dir, }: DropdownMenuProps): React.JSX.Element;
60
+ export declare function DropdownMenu({ children, open, defaultOpen, onOpenChange, modal, dir, trigger, disabled, mouseEnterDelay, mouseLeaveDelay, }: DropdownMenuProps): React.JSX.Element;
53
61
  interface DropdownMenuTriggerProps extends React.ComponentPropsWithoutRef<"button"> {
54
62
  /** Giao hành vi mở cho phần tử con thay vì `<button>` mặc định — như Radix. */
55
63
  asChild?: boolean;
@@ -12,9 +12,11 @@ import {
12
12
  OverlayTriggerStateContext,
13
13
  Popover,
14
14
  Pressable,
15
+ RootMenuTriggerStateContext,
15
16
  Separator,
16
17
  OverlayArrow,
17
- SubmenuTrigger
18
+ SubmenuTrigger,
19
+ useLocale
18
20
  } from "react-aria-components";
19
21
  import { Check, ChevronRight } from "lucide-react";
20
22
  import { cn } from "../../lib/utils.js";
@@ -58,39 +60,201 @@ function selectEvent(name) {
58
60
  return typeof CustomEvent === "function" ? new CustomEvent(name, { bubbles: false, cancelable: true }) : { defaultPrevented: false, preventDefault() {
59
61
  } };
60
62
  }
61
- const DropdownMenuModalContext = React.createContext({ modal: true });
63
+ const DROPDOWN_MENU_DEFAULT_TRIGGER = ["click"];
64
+ const DROPDOWN_MENU_MOUSE_ENTER_DELAY = 0.15;
65
+ const DROPDOWN_MENU_MOUSE_LEAVE_DELAY = 0.1;
66
+ const SECOND_IN_MS = 1e3;
67
+ const DropdownMenuModalContext = React.createContext({
68
+ modal: true,
69
+ contextMenuGesture: false,
70
+ disabled: false,
71
+ hover: null,
72
+ racOwnsContextMenu: false,
73
+ openedByHover: { current: false }
74
+ });
75
+ function clearContextMenuPoint(state) {
76
+ state?.setPoint(null);
77
+ }
78
+ function chainGestureProps(own, incoming) {
79
+ const merged = { ...own };
80
+ for (const [key, handler] of Object.entries(incoming)) {
81
+ const existing = own[key];
82
+ merged[key] = typeof existing === "function" && typeof handler === "function" ? (event) => {
83
+ existing(event);
84
+ if (!event.defaultPrevented) handler(event);
85
+ } : handler;
86
+ }
87
+ return merged;
88
+ }
89
+ function isContextMenuKey(event) {
90
+ return event.key === "ContextMenu" || event.key === "F10" && event.shiftKey;
91
+ }
62
92
  function DropdownMenu({
63
93
  children,
64
94
  open,
65
95
  defaultOpen,
66
96
  onOpenChange,
67
97
  modal = true,
68
- dir
98
+ dir,
99
+ trigger = DROPDOWN_MENU_DEFAULT_TRIGGER,
100
+ disabled = false,
101
+ mouseEnterDelay = DROPDOWN_MENU_MOUSE_ENTER_DELAY,
102
+ mouseLeaveDelay = DROPDOWN_MENU_MOUSE_LEAVE_DELAY
69
103
  }) {
70
104
  void dir;
71
- const options = React.useMemo(() => ({ modal }), [modal]);
72
- return /* @__PURE__ */ jsx(DropdownMenuModalContext.Provider, { value: options, children: /* @__PURE__ */ jsx(MenuTrigger, { isOpen: open, defaultOpen, onOpenChange, children }) });
105
+ const wantsHover = !disabled && trigger.includes("hover");
106
+ const wantsClick = !disabled && trigger.includes("click");
107
+ const wantsContextMenu = !disabled && trigger.includes("contextMenu");
108
+ const racOwnsContextMenu = wantsContextMenu && !wantsClick && !wantsHover;
109
+ const [uncontrolled, setUncontrolled] = React.useState(defaultOpen ?? false);
110
+ const isOpen = open ?? uncontrolled;
111
+ const isOpenRef = React.useRef(isOpen);
112
+ isOpenRef.current = isOpen;
113
+ const openedByHover = React.useRef(false);
114
+ const setOpen = React.useCallback(
115
+ (next) => {
116
+ if (next === isOpenRef.current) return;
117
+ if (next && disabled) return;
118
+ if (!next) openedByHover.current = false;
119
+ if (open === void 0) setUncontrolled(next);
120
+ onOpenChange?.(next);
121
+ },
122
+ [disabled, open, onOpenChange]
123
+ );
124
+ const enterTimer = React.useRef(null);
125
+ const leaveTimer = React.useRef(null);
126
+ React.useEffect(
127
+ () => () => {
128
+ if (enterTimer.current) clearTimeout(enterTimer.current);
129
+ if (leaveTimer.current) clearTimeout(leaveTimer.current);
130
+ },
131
+ []
132
+ );
133
+ const hover = React.useMemo(() => {
134
+ if (!wantsHover) return null;
135
+ return {
136
+ enter: () => {
137
+ if (leaveTimer.current) clearTimeout(leaveTimer.current);
138
+ if (isOpenRef.current) return;
139
+ enterTimer.current = setTimeout(() => {
140
+ openedByHover.current = true;
141
+ setOpen(true);
142
+ }, mouseEnterDelay * SECOND_IN_MS);
143
+ },
144
+ leave: () => {
145
+ if (enterTimer.current) clearTimeout(enterTimer.current);
146
+ leaveTimer.current = setTimeout(() => setOpen(false), mouseLeaveDelay * SECOND_IN_MS);
147
+ }
148
+ };
149
+ }, [wantsHover, mouseEnterDelay, mouseLeaveDelay, setOpen]);
150
+ const options = React.useMemo(
151
+ () => ({
152
+ // A HOVER MENU CANNOT BE MODAL. A modal RAC popover paints a full-bleed underlay the instant
153
+ // it opens; that underlay lands under the pointer, the trigger gets `pointerleave`, and the
154
+ // menu closes before the reader can move onto it. antd's hover dropdown is non-modal for the
155
+ // same reason. Every other mode keeps the caller's `modal`.
156
+ modal: wantsHover ? false : modal,
157
+ contextMenuGesture: wantsContextMenu,
158
+ disabled,
159
+ hover,
160
+ racOwnsContextMenu,
161
+ openedByHover
162
+ }),
163
+ [modal, wantsContextMenu, disabled, hover, racOwnsContextMenu, wantsHover]
164
+ );
165
+ return /* @__PURE__ */ jsx(DropdownMenuModalContext.Provider, { value: options, children: /* @__PURE__ */ jsx(
166
+ MenuTrigger,
167
+ {
168
+ isOpen,
169
+ onOpenChange: setOpen,
170
+ trigger: racOwnsContextMenu ? "contextMenu" : "press",
171
+ children
172
+ }
173
+ ) });
73
174
  }
74
175
  function DropdownMenuTrigger({ asChild, children, ...props }) {
75
176
  const state = React.useContext(OverlayTriggerStateContext);
177
+ const menuState = React.useContext(
178
+ RootMenuTriggerStateContext
179
+ );
180
+ const { contextMenuGesture, disabled, hover, racOwnsContextMenu } = React.useContext(DropdownMenuModalContext);
76
181
  const dataState = state?.isOpen ? "open" : "closed";
182
+ const wantsContextMenu = contextMenuGesture;
183
+ const isDisabled = disabled || props.disabled;
184
+ const { direction } = useLocale();
185
+ const openAtTriggerBox = (element) => {
186
+ const rect = element.getBoundingClientRect();
187
+ menuState?.setPoint({ x: direction === "rtl" ? rect.right : rect.left, y: rect.bottom });
188
+ menuState?.open("first");
189
+ };
190
+ const gestureProps = {};
191
+ if (wantsContextMenu && !isDisabled) {
192
+ gestureProps.onKeyDown = (event) => {
193
+ if (!isContextMenuKey(event)) return;
194
+ event.preventDefault();
195
+ openAtTriggerBox(event.currentTarget);
196
+ };
197
+ if (!racOwnsContextMenu) {
198
+ gestureProps.onContextMenu = (event) => {
199
+ event.preventDefault();
200
+ menuState?.setPoint({ x: event.clientX, y: event.clientY });
201
+ menuState?.open(null);
202
+ };
203
+ }
204
+ }
205
+ if (!racOwnsContextMenu) {
206
+ gestureProps.onPointerDownCapture = () => clearContextMenuPoint(menuState);
207
+ gestureProps.onKeyDownCapture = (event) => {
208
+ if (!isContextMenuKey(event)) clearContextMenuPoint(menuState);
209
+ };
210
+ }
211
+ if (hover && !isDisabled) {
212
+ gestureProps.onPointerEnter = (event) => {
213
+ if (event.pointerType === "mouse") hover.enter();
214
+ };
215
+ gestureProps.onPointerLeave = (event) => {
216
+ if (event.pointerType === "mouse") hover.leave();
217
+ };
218
+ }
77
219
  if (asChild && React.isValidElement(children)) {
78
- return /* @__PURE__ */ jsx(Pressable, { children: React.cloneElement(children, {
220
+ return /* @__PURE__ */ jsx(Pressable, { isDisabled, children: React.cloneElement(children, {
79
221
  "data-slot": "dropdown-menu-trigger",
80
- "data-state": dataState
222
+ "data-state": dataState,
223
+ // A right click target is usually a region rather than a button, and Shift+F10 needs it
224
+ // focusable — so it is given a tab stop unless the consumer already placed one.
225
+ ...wantsContextMenu && children.props.tabIndex === void 0 ? { tabIndex: 0 } : {},
226
+ ...chainGestureProps(
227
+ children.props,
228
+ gestureProps
229
+ )
81
230
  }) });
82
231
  }
83
232
  return /* @__PURE__ */ jsx(
84
233
  AriaButton,
85
234
  {
86
- ...props,
87
- isDisabled: props.disabled,
235
+ ...chainGestureProps(
236
+ props,
237
+ gestureProps
238
+ ),
239
+ isDisabled,
88
240
  "data-slot": "dropdown-menu-trigger",
89
241
  "data-state": dataState,
90
242
  children
91
243
  }
92
244
  );
93
245
  }
246
+ function useHoverSurfaceProps() {
247
+ const { hover } = React.useContext(DropdownMenuModalContext);
248
+ if (!hover) return {};
249
+ return {
250
+ onPointerEnter: (event) => {
251
+ if (event.pointerType === "mouse") hover.enter();
252
+ },
253
+ onPointerLeave: (event) => {
254
+ if (event.pointerType === "mouse") hover.leave();
255
+ }
256
+ };
257
+ }
94
258
  function DropdownMenuPortal({ children }) {
95
259
  return /* @__PURE__ */ jsx(Fragment, { children });
96
260
  }
@@ -129,7 +293,7 @@ function DropdownMenuContent({
129
293
  className,
130
294
  side,
131
295
  align,
132
- sideOffset = 4,
296
+ sideOffset,
133
297
  placement,
134
298
  arrow,
135
299
  width,
@@ -144,17 +308,25 @@ function DropdownMenuContent({
144
308
  void hideWhenDetached;
145
309
  void sticky;
146
310
  void forceMount;
147
- const { modal } = React.useContext(DropdownMenuModalContext);
311
+ const { modal, openedByHover } = React.useContext(DropdownMenuModalContext);
312
+ const hoverSurfaceProps = useHoverSurfaceProps();
313
+ const menuState = React.useContext(
314
+ RootMenuTriggerStateContext
315
+ );
148
316
  const anchor = placement ? DROPDOWN_MENU_PLACEMENT[placement] : void 0;
149
317
  const overlayPortalContainer = useOverlayPortalContainer();
318
+ const pointAnchored = menuState?.point != null;
319
+ const hasExplicitAnchor = placement !== void 0 || side !== void 0 || align !== void 0;
320
+ const resolvedPlacement = hasExplicitAnchor || !pointAnchored ? toPlacement(side ?? anchor?.side, align ?? anchor?.align) : "bottom start";
321
+ const resolvedOffset = sideOffset ?? (pointAnchored && !hasExplicitAnchor ? 0 : 4);
150
322
  return /* @__PURE__ */ jsx(DropdownMenuPortal, { children: /* @__PURE__ */ jsxs(
151
323
  Popover,
152
324
  {
153
325
  UNSTABLE_portalContainer: overlayPortalContainer,
154
326
  "data-slot": "dropdown-menu-content",
155
327
  isNonModal: !modal,
156
- placement: toPlacement(side ?? anchor?.side, align ?? anchor?.align),
157
- offset: sideOffset,
328
+ placement: resolvedPlacement,
329
+ offset: resolvedOffset,
158
330
  crossOffset: alignOffset,
159
331
  shouldFlip: avoidCollisions,
160
332
  containerPadding: collisionPadding,
@@ -168,15 +340,16 @@ function DropdownMenuContent({
168
340
  "div",
169
341
  {
170
342
  ...props,
343
+ ...hoverSurfaceProps,
171
344
  role: void 0,
172
345
  ...radixSurfaceState(state),
173
- "data-align": align ?? anchor?.align ?? "center",
346
+ "data-align": align ?? anchor?.align ?? (pointAnchored && !hasExplicitAnchor ? "start" : "center"),
174
347
  "data-width": width
175
348
  }
176
349
  )
177
350
  ),
178
351
  children: [
179
- /* @__PURE__ */ jsx(Menu, { shouldFocusWrap: loop, children }),
352
+ /* @__PURE__ */ jsx(Menu, { shouldFocusWrap: loop, autoFocus: openedByHover.current ? false : void 0, children }),
180
353
  arrow ? /* @__PURE__ */ jsx(OverlayArrow, { children: /* @__PURE__ */ jsx(
181
354
  "svg",
182
355
  {
@@ -388,6 +561,7 @@ function DropdownMenuSubContent({
388
561
  void hideWhenDetached;
389
562
  void sticky;
390
563
  const overlayPortalContainer = useOverlayPortalContainer();
564
+ const hoverSurfaceProps = useHoverSurfaceProps();
391
565
  return /* @__PURE__ */ jsx(
392
566
  Popover,
393
567
  {
@@ -408,6 +582,7 @@ function DropdownMenuSubContent({
408
582
  "div",
409
583
  {
410
584
  ...props,
585
+ ...hoverSurfaceProps,
411
586
  role: void 0,
412
587
  ...radixSurfaceState(state),
413
588
  "data-align": align ?? "center"
@@ -96,7 +96,8 @@ function Toolbar({
96
96
  defaultValue: filter.defaultSelected,
97
97
  onValueChange: (selected) => filter.onSelectedChange?.(selected),
98
98
  placeholder: filter.placeholder,
99
- disabled: disabled || filter.disabled
99
+ disabled: disabled || filter.disabled,
100
+ clearLabel: typeof filter.label === "string" ? t("navigation.filterBar.clearFilter", { label: filter.label }) : void 0
100
101
  }
101
102
  )
102
103
  },
@@ -1,8 +1,5 @@
1
1
  export { Tabs, TabsContent, TabsList, TabsTrigger } from "./tabs.js";
2
- export { ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, } from "./context-menu.js";
3
2
  export { Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationEllipsis, PaginationPrevious, PaginationNext, } from "./pagination.js";
4
- export { Menubar, MenubarCheckboxItem, MenubarContent, MenubarItem, MenubarLabel, MenubarMenu, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, } from "./menubar.js";
5
- export { NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, } from "./navigation-menu.js";
6
3
  export { Steps } from "./steps.js";
7
4
  export { FilterBar, FilterBarGroup, Toolbar, ToolbarGroup } from "./filter-bar.js";
8
5
  export type { FilterBarChipProp, FilterBarChipProps, FilterBarFilterProp, FilterBarFilterProps, FilterBarGroupProp, FilterBarGroupProps, FilterBarOverflowProp, FilterBarProp, FilterBarProps, FilterBarSearchProp, FilterBarSearchProps, ToolbarGroupProps, ToolbarProps, } from "./filter-bar.js";
@@ -1,20 +1,4 @@
1
1
  import { Tabs, TabsContent, TabsList, TabsTrigger } from "./tabs.js";
2
- import {
3
- ContextMenu,
4
- ContextMenuCheckboxItem,
5
- ContextMenuContent,
6
- ContextMenuItem,
7
- ContextMenuLabel,
8
- ContextMenuPortal,
9
- ContextMenuRadioGroup,
10
- ContextMenuRadioItem,
11
- ContextMenuSeparator,
12
- ContextMenuShortcut,
13
- ContextMenuSub,
14
- ContextMenuSubContent,
15
- ContextMenuSubTrigger,
16
- ContextMenuTrigger
17
- } from "./context-menu.js";
18
2
  import {
19
3
  Pagination,
20
4
  PaginationContent,
@@ -24,32 +8,6 @@ import {
24
8
  PaginationPrevious,
25
9
  PaginationNext
26
10
  } from "./pagination.js";
27
- import {
28
- Menubar,
29
- MenubarCheckboxItem,
30
- MenubarContent,
31
- MenubarItem,
32
- MenubarLabel,
33
- MenubarMenu,
34
- MenubarRadioGroup,
35
- MenubarRadioItem,
36
- MenubarSeparator,
37
- MenubarShortcut,
38
- MenubarSub,
39
- MenubarSubContent,
40
- MenubarSubTrigger,
41
- MenubarTrigger
42
- } from "./menubar.js";
43
- import {
44
- NavigationMenu,
45
- NavigationMenuContent,
46
- NavigationMenuIndicator,
47
- NavigationMenuItem,
48
- NavigationMenuLink,
49
- NavigationMenuList,
50
- NavigationMenuTrigger,
51
- NavigationMenuViewport
52
- } from "./navigation-menu.js";
53
11
  import { Steps } from "./steps.js";
54
12
  import { FilterBar, FilterBarGroup, Toolbar, ToolbarGroup } from "./filter-bar.js";
55
13
  import { AppSettingPicker } from "./app-setting-picker.js";
@@ -74,20 +32,6 @@ import {
74
32
  export {
75
33
  AppSettingPicker,
76
34
  AppSettingToggle,
77
- ContextMenu,
78
- ContextMenuCheckboxItem,
79
- ContextMenuContent,
80
- ContextMenuItem,
81
- ContextMenuLabel,
82
- ContextMenuPortal,
83
- ContextMenuRadioGroup,
84
- ContextMenuRadioItem,
85
- ContextMenuSeparator,
86
- ContextMenuShortcut,
87
- ContextMenuSub,
88
- ContextMenuSubContent,
89
- ContextMenuSubTrigger,
90
- ContextMenuTrigger,
91
35
  DropdownMenu,
92
36
  DropdownMenuCheckboxItem,
93
37
  DropdownMenuContent,
@@ -105,28 +49,6 @@ export {
105
49
  DropdownMenuTrigger,
106
50
  FilterBar,
107
51
  FilterBarGroup,
108
- Menubar,
109
- MenubarCheckboxItem,
110
- MenubarContent,
111
- MenubarItem,
112
- MenubarLabel,
113
- MenubarMenu,
114
- MenubarRadioGroup,
115
- MenubarRadioItem,
116
- MenubarSeparator,
117
- MenubarShortcut,
118
- MenubarSub,
119
- MenubarSubContent,
120
- MenubarSubTrigger,
121
- MenubarTrigger,
122
- NavigationMenu,
123
- NavigationMenuContent,
124
- NavigationMenuIndicator,
125
- NavigationMenuItem,
126
- NavigationMenuLink,
127
- NavigationMenuList,
128
- NavigationMenuTrigger,
129
- NavigationMenuViewport,
130
52
  Pagination,
131
53
  PaginationContent,
132
54
  PaginationEllipsis,
@@ -9,6 +9,7 @@ import {
9
9
  } from "react-aria-components";
10
10
  import { MoreHorizontal, Plus, X } from "lucide-react";
11
11
  import { useTranslation } from "../../i18n/use-translation.js";
12
+ import { useMaxWidthBreakpoint } from "../../lib/breakpoint-token.js";
12
13
  import { cn } from "../../lib/utils.js";
13
14
  import {
14
15
  DropdownMenu,
@@ -57,6 +58,13 @@ function resolveTabsAxis(tabPlacement, orientation) {
57
58
  orientation: orientation ?? "horizontal"
58
59
  };
59
60
  }
61
+ const TABS_PLACEMENT_BREAKPOINT_TOKEN = "--tabs-placement-responsive-breakpoint-width";
62
+ const TABS_PLACEMENT_BREAKPOINT_FALLBACK_QUERY = "(max-width: 768px)";
63
+ function foldVerticalPlacement(axis, narrow) {
64
+ if (!narrow || axis.orientation !== "vertical") return axis;
65
+ const trailing = axis.placement === "end" || axis.placement === "bottom";
66
+ return { placement: trailing ? "bottom" : "top", orientation: "horizontal" };
67
+ }
60
68
  function Tabs({
61
69
  className,
62
70
  orientation,
@@ -85,9 +93,13 @@ function Tabs({
85
93
  }) {
86
94
  const { t } = useTranslation();
87
95
  const resolvedDefault = resolveFallbackTabValue(items, defaultValue);
88
- const { placement, orientation: resolvedOrientation } = resolveTabsAxis(
89
- tabPlacement,
90
- orientation
96
+ const narrow = useMaxWidthBreakpoint(
97
+ TABS_PLACEMENT_BREAKPOINT_TOKEN,
98
+ TABS_PLACEMENT_BREAKPOINT_FALLBACK_QUERY
99
+ );
100
+ const { placement, orientation: resolvedOrientation } = foldVerticalPlacement(
101
+ resolveTabsAxis(tabPlacement, orientation),
102
+ narrow
91
103
  );
92
104
  const selectionSuppressed = value === void 0 && items != null && items.length > 0 && resolvedDefault === void 0;
93
105
  const frame = React.useMemo(
@@ -470,7 +482,7 @@ const TabsContent = React.forwardRef(
470
482
  ref,
471
483
  id: value,
472
484
  shouldForceMount: forceMount,
473
- className: cn("ui-focus-ring flex-1 outline-none", className),
485
+ className: cn("ui-focus-ring flex-1", className),
474
486
  render: (domProps, renderProps) => withDomProps("div", "tabs-content", props, domProps, {
475
487
  // A force-mounted panel whose tab is not selected: Radix marked it `data-state="inactive"`
476
488
  // and `hidden`; RAC marks it inert. The state hook is re-emitted so CSS keys the same way.
@@ -10,9 +10,6 @@ export * from "./card.js";
10
10
  export * from "./checkbox.js";
11
11
  export * from "./color-picker.js";
12
12
  export * from "./command.js";
13
- export * from "./context-menu.js";
14
- export * from "./menubar.js";
15
- export * from "./navigation-menu.js";
16
13
  export * from "./resizable.js";
17
14
  export * from "./date-picker.js";
18
15
  export * from "./dialog.js";
@@ -11,9 +11,6 @@ export * from "./card.js";
11
11
  export * from "./checkbox.js";
12
12
  export * from "./color-picker.js";
13
13
  export * from "./command.js";
14
- export * from "./context-menu.js";
15
- export * from "./menubar.js";
16
- export * from "./navigation-menu.js";
17
14
  export * from "./resizable.js";
18
15
  export * from "./date-picker.js";
19
16
  export * from "./dialog.js";
@@ -16,7 +16,7 @@ import * as React from "react";
16
16
  * too, and `input-otp` writes its own internal state inside its paste handler, which a wrapper
17
17
  * that only intercepted `onChange` could not undo.
18
18
  */
19
- export declare const InputOTP: React.ForwardRefExoticComponent<Omit<React.InputHTMLAttributes<HTMLInputElement>, "children" | "defaultValue" | "onChange" | "size" | "value"> & {
19
+ export declare const InputOTP: React.ForwardRefExoticComponent<Omit<React.InputHTMLAttributes<HTMLInputElement>, "children" | "defaultValue" | "onChange" | "size" | "style" | "value"> & {
20
20
  maxLength: number;
21
21
  value?: string;
22
22
  defaultValue?: string;
@@ -2,6 +2,7 @@
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import { Radio as AriaRadio, RadioGroup as AriaRadioGroup } from "react-aria-components";
5
+ import { withOwnHitTarget } from "../data-entry/choice-hit-target.js";
5
6
  import { cn } from "../../lib/utils.js";
6
7
  const Segmented = React.forwardRef(function Segmented2({
7
8
  options,
@@ -44,7 +45,8 @@ const Segmented = React.forwardRef(function Segmented2({
44
45
  "label",
45
46
  {
46
47
  ...domProps,
47
- "data-state": state.isSelected ? "checked" : "unchecked"
48
+ "data-state": state.isSelected ? "checked" : "unchecked",
49
+ children: withOwnHitTarget(domProps.children)
48
50
  }
49
51
  ),
50
52
  children: [
@@ -7,11 +7,9 @@ import { cn } from "../../lib/utils.js";
7
7
  import {
8
8
  applyMaxTagCount,
9
9
  controlSurfaceAttrs,
10
- resolveAllowClear
10
+ resolveAllowClear,
11
+ splitByTokenSeparators
11
12
  } from "../data-entry/control-surface.js";
12
- function escapeForCharClass(character) {
13
- return character.replace(/[\\\]^-]/g, "\\$&");
14
- }
15
13
  const TagInput = React.forwardRef(
16
14
  ({
17
15
  value,
@@ -51,7 +49,7 @@ const TagInput = React.forwardRef(
51
49
  };
52
50
  const addAll = (raw) => {
53
51
  let next = [...tags];
54
- const pieces = tokenSeparators.length ? raw.split(new RegExp(`[${tokenSeparators.map(escapeForCharClass).join("")}]`)) : [raw];
52
+ const pieces = splitByTokenSeparators(raw, tokenSeparators);
55
53
  for (const piece of pieces) {
56
54
  const tag = piece.trim();
57
55
  if (!tag || next.includes(tag)) continue;