@godxjp/ui 22.0.0 → 23.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.
- package/README.md +4 -4
- package/dist/components/data-display/scroll-area.d.ts +24 -4
- package/dist/components/data-display/scroll-area.js +34 -72
- package/dist/components/data-entry/cascader.js +8 -6
- package/dist/components/data-entry/checkbox.js +2 -0
- package/dist/components/data-entry/choice-hit-target.d.ts +24 -0
- package/dist/components/data-entry/choice-hit-target.js +12 -0
- package/dist/components/data-entry/control-surface.d.ts +8 -0
- package/dist/components/data-entry/control-surface.js +10 -1
- package/dist/components/data-entry/form-field.js +3 -2
- package/dist/components/data-entry/radio.js +3 -2
- package/dist/components/data-entry/search-select.js +225 -95
- package/dist/components/data-entry/select.d.ts +76 -14
- package/dist/components/data-entry/select.js +495 -163
- package/dist/components/data-entry/slider.d.ts +3 -11
- package/dist/components/data-entry/slider.js +582 -93
- package/dist/components/data-entry/switch.js +2 -0
- package/dist/components/navigation/dropdown-menu.d.ts +11 -3
- package/dist/components/navigation/dropdown-menu.js +190 -15
- package/dist/components/navigation/index.d.ts +0 -3
- package/dist/components/navigation/index.js +0 -78
- package/dist/components/ui/index.d.ts +0 -3
- package/dist/components/ui/index.js +0 -3
- package/dist/components/ui/input-otp.d.ts +1 -1
- package/dist/components/ui/segmented.js +3 -1
- package/dist/components/ui/tag-input.js +3 -5
- package/dist/lib/select-options.d.ts +21 -0
- package/dist/lib/select-options.js +46 -0
- package/dist/props/components/data-display.prop.d.ts +9 -6
- package/dist/props/components/data-entry.prop.d.ts +323 -30
- package/dist/props/components/index.d.ts +1 -1
- package/dist/props/components/navigation.prop.d.ts +17 -0
- package/dist/props/registry.d.ts +63 -0
- package/dist/props/registry.js +69 -0
- package/dist/styles/control.css +205 -23
- package/dist/styles/data-display-layout.css +18 -10
- package/dist/styles/focus-ring.css +2 -2
- package/dist/styles/fonts.css +88 -4
- package/dist/styles/navigation-layout.css +11 -236
- package/dist/styles/shell-layout.css +144 -139
- package/dist/styles/table-layout.css +7 -3
- package/dist/tokens/components/control.css +4 -0
- package/dist/tokens/components/data-display.css +0 -5
- package/dist/tokens/components/navigation.css +3 -5
- package/dist/tokens/components/scroll-area.css +7 -0
- package/docs/CUSTOMER-THEMING.md +7 -1
- package/docs/FRAME-COVERAGE-REPORT.md +4 -7
- package/docs/data-display/data-table/index.tsx +27 -1
- package/docs/data-display/popover.tsx +4 -6
- package/docs/data-display/scroll-area.tsx +51 -45
- package/docs/data-entry/date-picker.tsx +2 -3
- package/docs/data-entry/input-otp.tsx +5 -2
- package/docs/data-entry/segmented.tsx +40 -1
- package/docs/data-entry/select-matrix.tsx +1 -2
- package/docs/data-entry/select.tsx +108 -3
- package/docs/data-entry/slider.tsx +125 -47
- package/docs/feedback/dialog.tsx +1 -2
- package/docs/feedback/sheet.tsx +1 -2
- package/docs/layout/app-shell-states.tsx +143 -0
- package/docs/navigation/dropdown-menu.tsx +75 -4
- package/docs/roadmap/parity-audit-data-entry.md +44 -33
- package/docs/roadmap/parity-audit-layout-navigation-general.md +304 -310
- package/docs/roadmap/parity-backlog.md +2 -3
- package/package.json +20 -44
- package/scripts/ui-audit.mjs +1 -1
- package/scripts/visual-audit-rules.mjs +0 -7
- package/scripts/visual-audit.mjs +6 -27
- package/dist/components/navigation/context-menu.d.ts +0 -21
- package/dist/components/navigation/context-menu.js +0 -149
- package/dist/components/navigation/menubar.d.ts +0 -28
- package/dist/components/navigation/menubar.js +0 -136
- package/dist/components/navigation/navigation-menu.d.ts +0 -10
- package/dist/components/navigation/navigation-menu.js +0 -91
- package/dist/components/ui/context-menu.d.ts +0 -1
- package/dist/components/ui/context-menu.js +0 -2
- package/dist/components/ui/menubar.d.ts +0 -1
- package/dist/components/ui/menubar.js +0 -2
- package/dist/components/ui/navigation-menu.d.ts +0 -1
- package/dist/components/ui/navigation-menu.js +0 -2
- package/docs/FRAME-A11Y-CI.md +0 -349
- package/docs/navigation/context-menu.tsx +0 -128
- package/docs/navigation/menubar.tsx +0 -141
- 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,6 +73,7 @@ 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
|
|
@@ -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
|
|
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
|
|
72
|
-
|
|
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
|
-
...
|
|
87
|
-
|
|
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
|
|
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:
|
|
157
|
-
offset:
|
|
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"
|
|
@@ -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,
|
|
@@ -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 =
|
|
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;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { SearchSelectOptionProp, SelectFieldNamesProp, SelectOptionGroupProp } from "../props/components/data-entry.prop.js";
|
|
2
|
+
/**
|
|
3
|
+
* One list of rows, whatever shape the call site had.
|
|
4
|
+
*
|
|
5
|
+
* `Select` accepts three spellings of the same thing and they all have to arrive at the engine as
|
|
6
|
+
* ONE flat array, or every feature downstream (filtering, `maxCount`, the hidden `<option>` list)
|
|
7
|
+
* would need to know about all three:
|
|
8
|
+
*
|
|
9
|
+
* • this library's flat rows, `{ value, label, group? }`
|
|
10
|
+
* • antd's nested groups, `{ label, options: [...] }`
|
|
11
|
+
* • a foreign row read through `fieldNames`, `{ id, name, children }`
|
|
12
|
+
*
|
|
13
|
+
* Written once, here, rather than in both `select.tsx` and `search-select.tsx` — the two branches
|
|
14
|
+
* of one component must not disagree about what an option is. Group headings survive as the flat
|
|
15
|
+
* `group` string the rows already carried, so a grouped list renders identically whichever
|
|
16
|
+
* spelling produced it.
|
|
17
|
+
*/
|
|
18
|
+
/** A row whose keys are not known ahead of time — what `fieldNames` exists to read. */
|
|
19
|
+
type RawOption = Record<string, unknown>;
|
|
20
|
+
export declare function normalizeSelectOptions(options: readonly (SearchSelectOptionProp | SelectOptionGroupProp | RawOption)[] | undefined, fieldNames?: SelectFieldNamesProp): SearchSelectOptionProp[];
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
function text(value) {
|
|
2
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "bigint") {
|
|
3
|
+
return String(value);
|
|
4
|
+
}
|
|
5
|
+
return "";
|
|
6
|
+
}
|
|
7
|
+
function groupChildren(option, optionsKey) {
|
|
8
|
+
const children = option[optionsKey];
|
|
9
|
+
return Array.isArray(children) ? children : void 0;
|
|
10
|
+
}
|
|
11
|
+
function normalizeSelectOptions(options, fieldNames) {
|
|
12
|
+
if (!options?.length) return [];
|
|
13
|
+
const labelKey = fieldNames?.label ?? "label";
|
|
14
|
+
const valueKey = fieldNames?.value ?? "value";
|
|
15
|
+
const optionsKey = fieldNames?.options ?? "options";
|
|
16
|
+
const groupLabelKey = fieldNames?.groupLabel ?? labelKey;
|
|
17
|
+
const disabledKey = fieldNames?.disabled ?? "disabled";
|
|
18
|
+
const flat = [];
|
|
19
|
+
const push = (raw, group) => {
|
|
20
|
+
const value = text(raw[valueKey]);
|
|
21
|
+
const label = text(raw[labelKey]);
|
|
22
|
+
flat.push({
|
|
23
|
+
...raw,
|
|
24
|
+
value,
|
|
25
|
+
// A row with no label reads as its own value — the id is at least identifiable, where an
|
|
26
|
+
// empty row is not selectable by sight or by type-to-select.
|
|
27
|
+
label: label || value,
|
|
28
|
+
disabled: raw[disabledKey] === true || void 0,
|
|
29
|
+
// A row inside a group takes the group's heading; a flat row keeps the `group` it declared.
|
|
30
|
+
group: group ?? (typeof raw.group === "string" ? raw.group : void 0)
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
for (const entry of options) {
|
|
34
|
+
const children = groupChildren(entry, optionsKey);
|
|
35
|
+
if (children) {
|
|
36
|
+
const heading = text(entry[groupLabelKey]) || void 0;
|
|
37
|
+
for (const child of children) push(child, heading);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
push(entry, void 0);
|
|
41
|
+
}
|
|
42
|
+
return flat;
|
|
43
|
+
}
|
|
44
|
+
export {
|
|
45
|
+
normalizeSelectOptions
|
|
46
|
+
};
|
|
@@ -403,9 +403,10 @@ export type PermissionMatrixProp = {
|
|
|
403
403
|
export type ScrollAreaAnchorProp = "none" | "bottom";
|
|
404
404
|
/** @see ScrollArea */
|
|
405
405
|
/**
|
|
406
|
-
* @see ScrollArea — which AXES scroll. This is not decoration:
|
|
407
|
-
*
|
|
408
|
-
* is
|
|
406
|
+
* @see ScrollArea — which AXES scroll. This is not decoration: it IS the `overflow` the element
|
|
407
|
+
* carries, so an axis this does not name is `hidden` and its content is CLIPPED, not merely
|
|
408
|
+
* un-barred. It is also the whole replacement for mounting a `<ScrollBar>`, which was how the
|
|
409
|
+
* Radix-era component was told to open a second axis.
|
|
409
410
|
*
|
|
410
411
|
* `both` is a third value rather than an array or a pair of booleans because the axes are one
|
|
411
412
|
* decision — a pane scrolls down, across, or freely — and a closed union is what `check:prop-vocabulary`
|
|
@@ -419,12 +420,14 @@ export type ScrollAreaProp = {
|
|
|
419
420
|
*
|
|
420
421
|
* Reach for `horizontal` for a strip of non-shrinking columns (a board, a lane of cards): the
|
|
421
422
|
* viewport keeps its tab stop, so the strip is scrollable from the keyboard, and the consumer
|
|
422
|
-
* writes no overflow styling of its own.
|
|
423
|
+
* writes no overflow styling of its own. `both` is what replaces a vertical area that also
|
|
424
|
+
* mounted `<ScrollBar orientation="horizontal" />`.
|
|
423
425
|
*/
|
|
424
426
|
orientation?: ScrollAreaOrientationProp;
|
|
425
427
|
/**
|
|
426
|
-
* Ref to the element that actually SCROLLS
|
|
427
|
-
*
|
|
428
|
+
* Ref to the element that actually SCROLLS. Since v23 that is the component's own element, so
|
|
429
|
+
* this and `ref` hand back the SAME node — it is kept because it names the thing precisely, and
|
|
430
|
+
* because the Radix-era shape (a root wrapping a separate viewport) made `ref` the wrong handle.
|
|
428
431
|
*/
|
|
429
432
|
viewportRef?: React.Ref<HTMLDivElement>;
|
|
430
433
|
/** Edge the viewport sticks to as content grows. Default `none` (inert). */
|