@revikornmann/muka-ui 0.17.0 → 0.19.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 +81 -60
- package/cli/bin/muka-ui.js +12 -5
- package/cli/commands/brand.js +33 -14
- package/cli/commands/init.js +21 -17
- package/cli/commands/install-skill.js +84 -26
- package/cli/templates/AGENTS.md +128 -0
- package/cli/templates/CLAUDE.md +8 -88
- package/cli/templates/muka-ui-guidelines.md +50 -14
- package/dist/cjs/components/ActionSheet/ActionSheet.js +1 -1
- package/dist/cjs/components/Breadcrumb/Breadcrumb.css +11 -3
- package/dist/cjs/components/Breadcrumb/Breadcrumb.js +19 -1
- package/dist/cjs/components/Breadcrumb/Breadcrumb.js.map +1 -1
- package/dist/cjs/components/Combobox/Combobox.css +116 -132
- package/dist/cjs/components/Combobox/Combobox.js +175 -52
- package/dist/cjs/components/Combobox/Combobox.js.map +1 -1
- package/dist/cjs/components/Combobox/index.js.map +1 -1
- package/dist/cjs/components/ContextSelect/ContextSelect.css +117 -0
- package/dist/cjs/components/ContextSelect/ContextSelect.js +49 -0
- package/dist/cjs/components/ContextSelect/ContextSelect.js.map +1 -0
- package/dist/cjs/components/ContextSelect/index.js +11 -0
- package/dist/cjs/components/ContextSelect/index.js.map +1 -0
- package/dist/cjs/components/DropdownSelect/DropdownSelect.css +225 -0
- package/dist/cjs/components/DropdownSelect/DropdownSelect.js +106 -0
- package/dist/cjs/components/DropdownSelect/DropdownSelect.js.map +1 -0
- package/dist/cjs/components/DropdownSelect/index.js +6 -0
- package/dist/cjs/components/DropdownSelect/index.js.map +1 -0
- package/dist/cjs/components/Icon/custom/ArrowDropUpDownIcon.js +11 -0
- package/dist/cjs/components/Icon/custom/ArrowDropUpDownIcon.js.map +1 -0
- package/dist/cjs/components/Icon/custom/ArrowReturnIcon.js +11 -0
- package/dist/cjs/components/Icon/custom/ArrowReturnIcon.js.map +1 -0
- package/dist/cjs/components/Icon/iconRegistry.js +11 -0
- package/dist/cjs/components/Icon/iconRegistry.js.map +1 -1
- package/dist/cjs/components/Input/Input.css +45 -8
- package/dist/cjs/components/Input/Input.js +14 -3
- package/dist/cjs/components/Input/Input.js.map +1 -1
- package/dist/cjs/components/Menu/Menu.css +213 -17
- package/dist/cjs/components/Menu/Menu.js +429 -39
- package/dist/cjs/components/Menu/Menu.js.map +1 -1
- package/dist/cjs/components/Menu/index.js +5 -1
- package/dist/cjs/components/Menu/index.js.map +1 -1
- package/dist/cjs/components/ProgressTracker/ProgressTracker.css +273 -22
- package/dist/cjs/components/ProgressTracker/ProgressTracker.js +109 -9
- package/dist/cjs/components/ProgressTracker/ProgressTracker.js.map +1 -1
- package/dist/cjs/components/Scrollbar/Scrollbar.css +124 -0
- package/dist/cjs/components/Scrollbar/Scrollbar.js +118 -0
- package/dist/cjs/components/Scrollbar/Scrollbar.js.map +1 -0
- package/dist/cjs/components/Scrollbar/index.js +11 -0
- package/dist/cjs/components/Scrollbar/index.js.map +1 -0
- package/dist/cjs/components/index.js +13 -16
- package/dist/cjs/components/index.js.map +1 -1
- package/dist/esm/components/ActionSheet/ActionSheet.js +1 -1
- package/dist/esm/components/Breadcrumb/Breadcrumb.css +11 -3
- package/dist/esm/components/Breadcrumb/Breadcrumb.js +19 -1
- package/dist/esm/components/Breadcrumb/Breadcrumb.js.map +1 -1
- package/dist/esm/components/Combobox/Combobox.css +116 -132
- package/dist/esm/components/Combobox/Combobox.js +177 -54
- package/dist/esm/components/Combobox/Combobox.js.map +1 -1
- package/dist/esm/components/Combobox/index.js +1 -1
- package/dist/esm/components/Combobox/index.js.map +1 -1
- package/dist/esm/components/ContextSelect/ContextSelect.css +117 -0
- package/dist/esm/components/ContextSelect/ContextSelect.js +45 -0
- package/dist/esm/components/ContextSelect/ContextSelect.js.map +1 -0
- package/dist/esm/components/ContextSelect/index.js +3 -0
- package/dist/esm/components/ContextSelect/index.js.map +1 -0
- package/dist/esm/components/DropdownSelect/DropdownSelect.css +225 -0
- package/dist/esm/components/DropdownSelect/DropdownSelect.js +102 -0
- package/dist/esm/components/DropdownSelect/DropdownSelect.js.map +1 -0
- package/dist/esm/components/DropdownSelect/index.js +2 -0
- package/dist/esm/components/DropdownSelect/index.js.map +1 -0
- package/dist/esm/components/Icon/custom/ArrowDropUpDownIcon.js +7 -0
- package/dist/esm/components/Icon/custom/ArrowDropUpDownIcon.js.map +1 -0
- package/dist/esm/components/Icon/custom/ArrowReturnIcon.js +7 -0
- package/dist/esm/components/Icon/custom/ArrowReturnIcon.js.map +1 -0
- package/dist/esm/components/Icon/iconRegistry.js +11 -0
- package/dist/esm/components/Icon/iconRegistry.js.map +1 -1
- package/dist/esm/components/Input/Input.css +45 -8
- package/dist/esm/components/Input/Input.js +14 -3
- package/dist/esm/components/Input/Input.js.map +1 -1
- package/dist/esm/components/Menu/Menu.css +213 -17
- package/dist/esm/components/Menu/Menu.js +428 -40
- package/dist/esm/components/Menu/Menu.js.map +1 -1
- package/dist/esm/components/Menu/index.js +1 -1
- package/dist/esm/components/Menu/index.js.map +1 -1
- package/dist/esm/components/ProgressTracker/ProgressTracker.css +273 -22
- package/dist/esm/components/ProgressTracker/ProgressTracker.js +110 -10
- package/dist/esm/components/ProgressTracker/ProgressTracker.js.map +1 -1
- package/dist/esm/components/Scrollbar/Scrollbar.css +124 -0
- package/dist/esm/components/Scrollbar/Scrollbar.js +114 -0
- package/dist/esm/components/Scrollbar/Scrollbar.js.map +1 -0
- package/dist/esm/components/Scrollbar/index.js +3 -0
- package/dist/esm/components/Scrollbar/index.js.map +1 -0
- package/dist/esm/components/index.js +4 -2
- package/dist/esm/components/index.js.map +1 -1
- package/dist/styles/components/Breadcrumb.css +11 -3
- package/dist/styles/components/Combobox.css +116 -132
- package/dist/styles/components/ContextSelect.css +117 -0
- package/dist/styles/components/DropdownSelect.css +225 -0
- package/dist/styles/components/Input.css +45 -8
- package/dist/styles/components/Menu.css +213 -17
- package/dist/styles/components/ProgressTracker.css +273 -22
- package/dist/styles/components/Scrollbar.css +124 -0
- package/dist/styles/components/Typography.css +89 -0
- package/dist/styles/index.css +1526 -480
- package/dist/styles/muka-dark.css +1526 -480
- package/dist/styles/muka-light.css +1526 -480
- package/dist/styles/tokens-bouwplan-dark.css +9 -6
- package/dist/styles/tokens-bouwplan-light.css +9 -6
- package/dist/styles/tokens-fscl-dark.css +29 -26
- package/dist/styles/tokens-fscl-light.css +29 -26
- package/dist/styles/tokens-grip-dark.css +8 -5
- package/dist/styles/tokens-grip-light.css +8 -5
- package/dist/styles/tokens-muka-dark.css +8 -5
- package/dist/styles/tokens-muka-light.css +8 -5
- package/dist/styles/tokens-wireframe-dark.css +53 -50
- package/dist/styles/tokens-wireframe-light.css +53 -50
- package/dist/styles/wireframe-dark.css +1571 -525
- package/dist/styles/wireframe-light.css +1571 -525
- package/dist/types/components/ActionSheet/ActionSheet.d.ts +1 -1
- package/dist/types/components/Breadcrumb/Breadcrumb.d.ts +27 -3
- package/dist/types/components/Breadcrumb/Breadcrumb.d.ts.map +1 -1
- package/dist/types/components/Combobox/Combobox.d.ts +38 -38
- package/dist/types/components/Combobox/Combobox.d.ts.map +1 -1
- package/dist/types/components/Combobox/index.d.ts +1 -1
- package/dist/types/components/Combobox/index.d.ts.map +1 -1
- package/dist/types/components/ContextSelect/ContextSelect.d.ts +62 -0
- package/dist/types/components/ContextSelect/ContextSelect.d.ts.map +1 -0
- package/dist/types/components/ContextSelect/index.d.ts +3 -0
- package/dist/types/components/ContextSelect/index.d.ts.map +1 -0
- package/dist/types/components/DataTable/DataTable.d.ts +1 -1
- package/dist/types/components/DropdownSelect/DropdownSelect.d.ts +71 -0
- package/dist/types/components/DropdownSelect/DropdownSelect.d.ts.map +1 -0
- package/dist/types/components/DropdownSelect/index.d.ts +2 -0
- package/dist/types/components/DropdownSelect/index.d.ts.map +1 -0
- package/dist/types/components/Icon/custom/ArrowDropUpDownIcon.d.ts +9 -0
- package/dist/types/components/Icon/custom/ArrowDropUpDownIcon.d.ts.map +1 -0
- package/dist/types/components/Icon/custom/ArrowReturnIcon.d.ts +9 -0
- package/dist/types/components/Icon/custom/ArrowReturnIcon.d.ts.map +1 -0
- package/dist/types/components/Icon/iconRegistry.d.ts.map +1 -1
- package/dist/types/components/Input/Input.d.ts +5 -0
- package/dist/types/components/Input/Input.d.ts.map +1 -1
- package/dist/types/components/Menu/Menu.d.ts +99 -29
- package/dist/types/components/Menu/Menu.d.ts.map +1 -1
- package/dist/types/components/Menu/index.d.ts +2 -2
- package/dist/types/components/Menu/index.d.ts.map +1 -1
- package/dist/types/components/ProgressTracker/ProgressTracker.d.ts +29 -8
- package/dist/types/components/ProgressTracker/ProgressTracker.d.ts.map +1 -1
- package/dist/types/components/Scrollbar/Scrollbar.d.ts +19 -0
- package/dist/types/components/Scrollbar/Scrollbar.d.ts.map +1 -0
- package/dist/types/components/Scrollbar/index.d.ts +3 -0
- package/dist/types/components/Scrollbar/index.d.ts.map +1 -0
- package/dist/types/components/index.d.ts +5 -4
- package/dist/types/components/index.d.ts.map +1 -1
- package/docs/consumers/README.md +116 -37
- package/docs/consumers/brand.md +261 -0
- package/docs/consumers/figma-console-mcp.md +116 -0
- package/docs/consumers/setup-instructions.md +98 -12
- package/docs/consumers/skills.md +79 -0
- package/package.json +4 -6
- package/scripts/postinstall-nudge.js +8 -4
- package/skills/add-brand/SKILL.md +204 -0
- package/skills/add-brand/reference.md +160 -0
- package/skills/figma-to-code/SKILL.md +127 -0
- package/skills/pull-from-figma/SKILL.md +123 -0
- package/skills/push-to-figma/SKILL.md +172 -0
- package/skills/setup-muka/SKILL.md +73 -13
- package/tokens/README.md +40 -18
- package/tokens/t2-alias/brand/bouwplan/fonts.json +1 -1
- package/tokens/t2-alias/brand/fscl/fonts.json +3 -3
- package/tokens/t2-alias/brand/wireframe/fonts.json +2 -2
- package/tokens/t4-components/menu.json +20 -5
- package/dist/cjs/components/ContextMenu/ContextMenu.js +0 -186
- package/dist/cjs/components/ContextMenu/ContextMenu.js.map +0 -1
- package/dist/cjs/components/ContextMenu/index.js +0 -17
- package/dist/cjs/components/ContextMenu/index.js.map +0 -1
- package/dist/esm/components/ContextMenu/ContextMenu.js +0 -145
- package/dist/esm/components/ContextMenu/ContextMenu.js.map +0 -1
- package/dist/esm/components/ContextMenu/index.js +0 -2
- package/dist/esm/components/ContextMenu/index.js.map +0 -1
- package/dist/types/components/ContextMenu/ContextMenu.d.ts +0 -188
- package/dist/types/components/ContextMenu/ContextMenu.d.ts.map +0 -1
- package/dist/types/components/ContextMenu/index.d.ts +0 -3
- package/dist/types/components/ContextMenu/index.d.ts.map +0 -1
|
@@ -34,132 +34,518 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
};
|
|
35
35
|
})();
|
|
36
36
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
-
exports.MenuSubContent = exports.MenuSubTrigger = exports.MenuSub = exports.MenuRadioItem = exports.MenuRadioGroup = exports.MenuCheckboxItem = exports.MenuGroup = exports.MenuSeparator = exports.MenuItem = exports.MenuContent = exports.MenuTrigger = exports.Menu = void 0;
|
|
37
|
+
exports.MenuSubContent = exports.MenuSubTrigger = exports.MenuSub = exports.MenuRadioItem = exports.MenuRadioGroup = exports.MenuCheckboxItem = exports.MenuGroup = exports.MenuSeparator = exports.MenuItem = exports.MenuSearch = exports.MenuContent = exports.MenuTrigger = exports.DropdownMenu = exports.Menu = void 0;
|
|
38
|
+
exports.useMenuSearch = useMenuSearch;
|
|
39
|
+
exports.highlightMenuMatch = highlightMenuMatch;
|
|
38
40
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
39
41
|
const react_1 = __importStar(require("react"));
|
|
40
42
|
const DropdownMenuPrimitive = __importStar(require("@radix-ui/react-dropdown-menu"));
|
|
43
|
+
const ContextMenuPrimitive = __importStar(require("@radix-ui/react-context-menu"));
|
|
41
44
|
const Icon_1 = require("../Icon");
|
|
45
|
+
const Scrollbar_1 = require("../Scrollbar");
|
|
42
46
|
require("./Menu.css");
|
|
43
|
-
const MenuContext = (0, react_1.createContext)({});
|
|
47
|
+
const MenuContext = (0, react_1.createContext)({ trigger: 'click', open: false });
|
|
48
|
+
const MenuSearchContext = (0, react_1.createContext)({
|
|
49
|
+
query: '',
|
|
50
|
+
setQuery: () => { },
|
|
51
|
+
searchable: false,
|
|
52
|
+
highlightedId: null,
|
|
53
|
+
registerSearchNavItem: () => () => { },
|
|
54
|
+
moveHighlight: () => { },
|
|
55
|
+
selectHighlighted: () => { },
|
|
56
|
+
});
|
|
57
|
+
const MenuSearchHitsContext = (0, react_1.createContext)(null);
|
|
58
|
+
function useReportVisibleSearchResult(visible) {
|
|
59
|
+
const report = (0, react_1.useContext)(MenuSearchHitsContext);
|
|
60
|
+
(0, react_1.useLayoutEffect)(() => {
|
|
61
|
+
if (!visible || !report)
|
|
62
|
+
return undefined;
|
|
63
|
+
report(1);
|
|
64
|
+
return () => report(-1);
|
|
65
|
+
}, [visible, report]);
|
|
66
|
+
}
|
|
44
67
|
/**
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
|
|
48
|
-
|
|
68
|
+
* Registers an enabled, visible row for MenuSearch arrow/Enter navigation.
|
|
69
|
+
* Returns whether this row is the current search highlight.
|
|
70
|
+
*/
|
|
71
|
+
function useSearchNavHighlight(options) {
|
|
72
|
+
const { enabled, disabled, select } = options;
|
|
73
|
+
const { searchable, highlightedId, registerSearchNavItem } = useMenuSearch();
|
|
74
|
+
const id = react_1.default.useId();
|
|
75
|
+
const selectRef = (0, react_1.useRef)(select);
|
|
76
|
+
selectRef.current = select;
|
|
77
|
+
(0, react_1.useLayoutEffect)(() => {
|
|
78
|
+
if (!searchable || !enabled || disabled)
|
|
79
|
+
return undefined;
|
|
80
|
+
return registerSearchNavItem({
|
|
81
|
+
id,
|
|
82
|
+
disabled: false,
|
|
83
|
+
select: () => selectRef.current(),
|
|
84
|
+
});
|
|
85
|
+
}, [searchable, enabled, disabled, id, registerSearchNavItem]);
|
|
86
|
+
return searchable && enabled && !disabled && highlightedId === id;
|
|
87
|
+
}
|
|
88
|
+
function useMenuSearch() {
|
|
89
|
+
return (0, react_1.useContext)(MenuSearchContext);
|
|
90
|
+
}
|
|
91
|
+
function useMenuPrimitives() {
|
|
92
|
+
const { trigger } = (0, react_1.useContext)(MenuContext);
|
|
93
|
+
return trigger === 'context' ? ContextMenuPrimitive : DropdownMenuPrimitive;
|
|
94
|
+
}
|
|
95
|
+
/** Bold the first case-insensitive match of `query` within `text`. */
|
|
96
|
+
function highlightMenuMatch(text, query) {
|
|
97
|
+
if (!query)
|
|
98
|
+
return text;
|
|
99
|
+
const lower = text.toLowerCase();
|
|
100
|
+
const q = query.toLowerCase();
|
|
101
|
+
const index = lower.indexOf(q);
|
|
102
|
+
if (index === -1)
|
|
103
|
+
return text;
|
|
104
|
+
const before = text.slice(0, index);
|
|
105
|
+
const match = text.slice(index, index + query.length);
|
|
106
|
+
const after = text.slice(index + query.length);
|
|
107
|
+
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [before, (0, jsx_runtime_1.jsx)("span", { className: "muka-menu__match", children: match }), after] }));
|
|
108
|
+
}
|
|
109
|
+
function getSearchableText(children) {
|
|
110
|
+
if (typeof children === 'string' || typeof children === 'number') {
|
|
111
|
+
return String(children);
|
|
112
|
+
}
|
|
113
|
+
if (Array.isArray(children)) {
|
|
114
|
+
return children.map(getSearchableText).join('');
|
|
115
|
+
}
|
|
116
|
+
if (react_1.default.isValidElement(children)) {
|
|
117
|
+
const props = children.props;
|
|
118
|
+
return getSearchableText(props.children);
|
|
119
|
+
}
|
|
120
|
+
return '';
|
|
121
|
+
}
|
|
122
|
+
function matchesSearch(children, query) {
|
|
123
|
+
if (!query)
|
|
124
|
+
return true;
|
|
125
|
+
return getSearchableText(children).toLowerCase().includes(query.toLowerCase());
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Menu (Dropdown Menu)
|
|
49
129
|
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* <Menu>
|
|
53
|
-
* <MenuTrigger asChild>
|
|
54
|
-
* <Button>Open Menu</Button>
|
|
55
|
-
* </MenuTrigger>
|
|
56
|
-
* <MenuContent>
|
|
57
|
-
* <MenuItem onSelect={() => console.log('Edit')}>Edit</MenuItem>
|
|
58
|
-
* <MenuItem onSelect={() => console.log('Delete')} destructive>Delete</MenuItem>
|
|
59
|
-
* </MenuContent>
|
|
60
|
-
* </Menu>
|
|
61
|
-
* ```
|
|
130
|
+
* Shared action-menu surface for click and right-click triggers.
|
|
131
|
+
* Built on Radix DropdownMenu / ContextMenu.
|
|
62
132
|
*/
|
|
63
|
-
const Menu = ({ children, open, onOpenChange, defaultOpen, modal = true, }) => {
|
|
64
|
-
|
|
133
|
+
const Menu = ({ children, trigger = 'click', open, onOpenChange, defaultOpen, modal = true, }) => {
|
|
134
|
+
const [uncontrolledOpen, setUncontrolledOpen] = (0, react_1.useState)(Boolean(defaultOpen));
|
|
135
|
+
const isControlled = open !== undefined;
|
|
136
|
+
const isOpen = isControlled ? Boolean(open) : uncontrolledOpen;
|
|
137
|
+
const handleOpenChange = (0, react_1.useCallback)((next) => {
|
|
138
|
+
if (!isControlled) {
|
|
139
|
+
setUncontrolledOpen(next);
|
|
140
|
+
}
|
|
141
|
+
onOpenChange?.(next);
|
|
142
|
+
}, [isControlled, onOpenChange]);
|
|
143
|
+
const value = (0, react_1.useMemo)(() => ({ trigger, open: isOpen }), [trigger, isOpen]);
|
|
144
|
+
if (trigger === 'context') {
|
|
145
|
+
return ((0, jsx_runtime_1.jsx)(MenuContext.Provider, { value: value, children: (0, jsx_runtime_1.jsx)(ContextMenuPrimitive.Root, { onOpenChange: handleOpenChange, modal: modal, children: children }) }));
|
|
146
|
+
}
|
|
147
|
+
return ((0, jsx_runtime_1.jsx)(MenuContext.Provider, { value: value, children: (0, jsx_runtime_1.jsx)(DropdownMenuPrimitive.Root, { open: open, onOpenChange: handleOpenChange, defaultOpen: defaultOpen, modal: modal, children: children }) }));
|
|
65
148
|
};
|
|
66
149
|
exports.Menu = Menu;
|
|
150
|
+
/** Alias for Figma / docs naming parity with "Dropdown Menu". */
|
|
151
|
+
exports.DropdownMenu = exports.Menu;
|
|
67
152
|
/**
|
|
68
|
-
* MenuTrigger
|
|
153
|
+
* MenuTrigger — opens the menu on click or right-click depending on `Menu.trigger`.
|
|
69
154
|
*/
|
|
70
|
-
exports.MenuTrigger = react_1.default.forwardRef(({ children, asChild = false, className = '' }, ref) => {
|
|
71
|
-
|
|
155
|
+
exports.MenuTrigger = react_1.default.forwardRef(({ children, asChild = false, disabled = false, className = '' }, ref) => {
|
|
156
|
+
const { trigger } = (0, react_1.useContext)(MenuContext);
|
|
157
|
+
const Primitives = useMenuPrimitives();
|
|
158
|
+
if (trigger === 'context') {
|
|
159
|
+
const triggerClasses = ['muka-context-menu__trigger', className]
|
|
160
|
+
.filter(Boolean)
|
|
161
|
+
.join(' ');
|
|
162
|
+
return ((0, jsx_runtime_1.jsx)(ContextMenuPrimitive.Trigger, { ref: ref, asChild: asChild, disabled: disabled, className: triggerClasses || undefined, children: children }));
|
|
163
|
+
}
|
|
164
|
+
return ((0, jsx_runtime_1.jsx)(Primitives.Trigger, { ref: ref, asChild: asChild, disabled: disabled, className: className || undefined, children: children }));
|
|
72
165
|
});
|
|
73
166
|
exports.MenuTrigger.displayName = 'MenuTrigger';
|
|
74
167
|
/**
|
|
75
|
-
* MenuContent
|
|
168
|
+
* MenuContent — dropdown / context menu surface with optional search + scrollbar.
|
|
76
169
|
*/
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
170
|
+
function isMenuSearchElement(child) {
|
|
171
|
+
return (react_1.default.isValidElement(child) &&
|
|
172
|
+
typeof child.type !== 'string' &&
|
|
173
|
+
Boolean(child.type &&
|
|
174
|
+
typeof child.type === 'object' &&
|
|
175
|
+
'displayName' in child.type &&
|
|
176
|
+
child.type.displayName === 'MenuSearch'));
|
|
177
|
+
}
|
|
178
|
+
function partitionMenuChildren(children) {
|
|
179
|
+
const search = [];
|
|
180
|
+
const items = [];
|
|
181
|
+
react_1.default.Children.forEach(children, (child) => {
|
|
182
|
+
if (isMenuSearchElement(child)) {
|
|
183
|
+
search.push(child);
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
items.push(child);
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
return { search, items };
|
|
190
|
+
}
|
|
191
|
+
exports.MenuContent = react_1.default.forwardRef(({ children, align = 'start', side = 'bottom', sideOffset = 4, alignOffset = 0, className = '', style, loop = true, onOpenAutoFocus, onCloseAutoFocus, searchable = false, scrollbar = false, }, ref) => {
|
|
192
|
+
const { trigger, open } = (0, react_1.useContext)(MenuContext);
|
|
193
|
+
const [query, setQuery] = (0, react_1.useState)('');
|
|
194
|
+
const [searchHits, setSearchHits] = (0, react_1.useState)(0);
|
|
195
|
+
const [highlightedId, setHighlightedId] = (0, react_1.useState)(null);
|
|
196
|
+
const viewportRef = (0, react_1.useRef)(null);
|
|
197
|
+
const searchNavItemsRef = (0, react_1.useRef)([]);
|
|
198
|
+
// Clear search when the menu closes so the next open shows all options.
|
|
199
|
+
(0, react_1.useEffect)(() => {
|
|
200
|
+
if (!open) {
|
|
201
|
+
setQuery('');
|
|
202
|
+
setHighlightedId(null);
|
|
203
|
+
}
|
|
204
|
+
}, [open]);
|
|
205
|
+
const reportSearchHits = (0, react_1.useCallback)((delta) => {
|
|
206
|
+
setSearchHits((count) => count + delta);
|
|
207
|
+
}, []);
|
|
208
|
+
const registerSearchNavItem = (0, react_1.useCallback)((item) => {
|
|
209
|
+
searchNavItemsRef.current.push(item);
|
|
210
|
+
return () => {
|
|
211
|
+
searchNavItemsRef.current = searchNavItemsRef.current.filter((entry) => entry.id !== item.id);
|
|
212
|
+
};
|
|
213
|
+
}, []);
|
|
214
|
+
const moveHighlight = (0, react_1.useCallback)((delta) => {
|
|
215
|
+
const enabled = searchNavItemsRef.current.filter((item) => !item.disabled);
|
|
216
|
+
if (enabled.length === 0) {
|
|
217
|
+
setHighlightedId(null);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
setHighlightedId((current) => {
|
|
221
|
+
const index = enabled.findIndex((item) => item.id === current);
|
|
222
|
+
const from = index === -1 ? (delta > 0 ? -1 : 0) : index;
|
|
223
|
+
const next = (from + delta + enabled.length) % enabled.length;
|
|
224
|
+
return enabled[next].id;
|
|
225
|
+
});
|
|
226
|
+
}, []);
|
|
227
|
+
const highlightedIdRef = (0, react_1.useRef)(null);
|
|
228
|
+
highlightedIdRef.current = highlightedId;
|
|
229
|
+
const selectHighlighted = (0, react_1.useCallback)(() => {
|
|
230
|
+
const enabled = searchNavItemsRef.current.filter((item) => !item.disabled);
|
|
231
|
+
const currentId = highlightedIdRef.current;
|
|
232
|
+
const target = enabled.find((item) => item.id === currentId) ?? enabled[0] ?? null;
|
|
233
|
+
target?.select();
|
|
234
|
+
}, []);
|
|
235
|
+
// After children register, pin highlight to the first enabled row (or keep
|
|
236
|
+
// the current one when it is still in the filtered list).
|
|
237
|
+
(0, react_1.useLayoutEffect)(() => {
|
|
238
|
+
if (!searchable) {
|
|
239
|
+
searchNavItemsRef.current = [];
|
|
240
|
+
if (highlightedIdRef.current !== null)
|
|
241
|
+
setHighlightedId(null);
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
const enabled = searchNavItemsRef.current.filter((item) => !item.disabled);
|
|
245
|
+
setHighlightedId((current) => {
|
|
246
|
+
if (current && enabled.some((item) => item.id === current))
|
|
247
|
+
return current;
|
|
248
|
+
return enabled[0]?.id ?? null;
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
(0, react_1.useLayoutEffect)(() => {
|
|
252
|
+
if (!highlightedId || !viewportRef.current)
|
|
253
|
+
return;
|
|
254
|
+
const active = viewportRef.current.querySelector('.muka-menu__item[data-highlighted]');
|
|
255
|
+
if (typeof active?.scrollIntoView === 'function') {
|
|
256
|
+
active.scrollIntoView({ block: 'nearest' });
|
|
257
|
+
}
|
|
258
|
+
}, [highlightedId]);
|
|
259
|
+
const handleQueryChange = (0, react_1.useCallback)((next) => {
|
|
260
|
+
setQuery(next);
|
|
261
|
+
// Force re-pin to the first match on the next layout pass.
|
|
262
|
+
setHighlightedId(null);
|
|
263
|
+
}, []);
|
|
264
|
+
const searchValue = (0, react_1.useMemo)(() => ({
|
|
265
|
+
query,
|
|
266
|
+
setQuery: handleQueryChange,
|
|
267
|
+
searchable,
|
|
268
|
+
highlightedId,
|
|
269
|
+
registerSearchNavItem,
|
|
270
|
+
moveHighlight,
|
|
271
|
+
selectHighlighted,
|
|
272
|
+
}), [
|
|
273
|
+
query,
|
|
274
|
+
handleQueryChange,
|
|
275
|
+
searchable,
|
|
276
|
+
highlightedId,
|
|
277
|
+
registerSearchNavItem,
|
|
278
|
+
moveHighlight,
|
|
279
|
+
selectHighlighted,
|
|
280
|
+
]);
|
|
281
|
+
// Prefer the sticky search field so typing + arrow/Enter nav work immediately.
|
|
282
|
+
const handleOpenAutoFocus = (0, react_1.useCallback)((event) => {
|
|
283
|
+
if (onOpenAutoFocus) {
|
|
284
|
+
onOpenAutoFocus(event);
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
if (!searchable)
|
|
288
|
+
return;
|
|
289
|
+
const content = event.currentTarget;
|
|
290
|
+
const input = content?.querySelector?.('.muka-menu__search-input');
|
|
291
|
+
if (input) {
|
|
292
|
+
event.preventDefault();
|
|
293
|
+
input.focus();
|
|
294
|
+
}
|
|
295
|
+
}, [onOpenAutoFocus, searchable]);
|
|
296
|
+
const { search, items } = partitionMenuChildren(children);
|
|
297
|
+
const searchNodes = search.length > 0 ? search : searchable ? (0, jsx_runtime_1.jsx)(exports.MenuSearch, {}) : null;
|
|
298
|
+
const contentClasses = [
|
|
299
|
+
'muka-menu__content',
|
|
300
|
+
searchable && 'muka-menu__content--searchable',
|
|
301
|
+
scrollbar && 'muka-menu__content--scrollbar',
|
|
302
|
+
className,
|
|
303
|
+
]
|
|
304
|
+
.filter(Boolean)
|
|
305
|
+
.join(' ');
|
|
306
|
+
const body = ((0, jsx_runtime_1.jsx)(MenuSearchContext.Provider, { value: searchValue, children: (0, jsx_runtime_1.jsxs)(MenuSearchHitsContext.Provider, { value: reportSearchHits, children: [searchNodes, (0, jsx_runtime_1.jsxs)("div", { className: "muka-menu__body", children: [(0, jsx_runtime_1.jsxs)("div", { className: "muka-menu__viewport", ref: viewportRef, children: [items, searchable && query && searchHits === 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "muka-menu__empty", role: "status", children: "No results" })) : null] }), scrollbar ? (0, jsx_runtime_1.jsx)(Scrollbar_1.Scrollbar, { viewportRef: viewportRef }) : null] })] }) }));
|
|
307
|
+
// Radix types omit onOpenAutoFocus from menu Content (private FocusScope prop),
|
|
308
|
+
// but it is supported at runtime — required so searchable menus focus the field.
|
|
309
|
+
if (trigger === 'context') {
|
|
310
|
+
const contextContentProps = {
|
|
311
|
+
ref,
|
|
312
|
+
alignOffset,
|
|
313
|
+
className: contentClasses,
|
|
314
|
+
style,
|
|
315
|
+
loop,
|
|
316
|
+
onOpenAutoFocus: handleOpenAutoFocus,
|
|
317
|
+
onCloseAutoFocus,
|
|
318
|
+
children: body,
|
|
319
|
+
};
|
|
320
|
+
return ((0, jsx_runtime_1.jsx)(ContextMenuPrimitive.Portal, { children: (0, jsx_runtime_1.jsx)(ContextMenuPrimitive.Content, { ...contextContentProps }) }));
|
|
321
|
+
}
|
|
322
|
+
const dropdownContentProps = {
|
|
323
|
+
ref,
|
|
324
|
+
align,
|
|
325
|
+
side,
|
|
326
|
+
sideOffset,
|
|
327
|
+
alignOffset,
|
|
328
|
+
className: contentClasses,
|
|
329
|
+
style,
|
|
330
|
+
loop,
|
|
331
|
+
onOpenAutoFocus: handleOpenAutoFocus,
|
|
332
|
+
onCloseAutoFocus,
|
|
333
|
+
children: body,
|
|
334
|
+
};
|
|
335
|
+
return ((0, jsx_runtime_1.jsx)(DropdownMenuPrimitive.Portal, { children: (0, jsx_runtime_1.jsx)(DropdownMenuPrimitive.Content, { ...dropdownContentProps }) }));
|
|
80
336
|
});
|
|
81
337
|
exports.MenuContent.displayName = 'MenuContent';
|
|
338
|
+
/**
|
|
339
|
+
* MenuSearch — sticky search header for filtering menu items.
|
|
340
|
+
* Must be used inside a `MenuContent` with `searchable`.
|
|
341
|
+
*/
|
|
342
|
+
exports.MenuSearch = react_1.default.forwardRef(({ placeholder = 'Search', size = 'md', className = '', 'aria-label': ariaLabel }, ref) => {
|
|
343
|
+
const { query, setQuery, searchable, moveHighlight, selectHighlighted, } = useMenuSearch();
|
|
344
|
+
const handleKeyDown = (0, react_1.useCallback)((e) => {
|
|
345
|
+
// Keep typing from driving Radix typeahead, but own arrow/Enter for
|
|
346
|
+
// highlight + commit while focus stays in the search field.
|
|
347
|
+
if (e.key === 'ArrowDown') {
|
|
348
|
+
e.preventDefault();
|
|
349
|
+
e.stopPropagation();
|
|
350
|
+
moveHighlight(1);
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
if (e.key === 'ArrowUp') {
|
|
354
|
+
e.preventDefault();
|
|
355
|
+
e.stopPropagation();
|
|
356
|
+
moveHighlight(-1);
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
if (e.key === 'Enter') {
|
|
360
|
+
e.preventDefault();
|
|
361
|
+
e.stopPropagation();
|
|
362
|
+
selectHighlighted();
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
e.stopPropagation();
|
|
366
|
+
}, [moveHighlight, selectHighlighted]);
|
|
367
|
+
if (!searchable) {
|
|
368
|
+
return null;
|
|
369
|
+
}
|
|
370
|
+
const iconSize = size === 'sm' ? 'xs' : 'sm';
|
|
371
|
+
const rootClasses = [
|
|
372
|
+
'muka-menu__search',
|
|
373
|
+
size === 'sm' && 'muka-menu__search--sm',
|
|
374
|
+
className,
|
|
375
|
+
]
|
|
376
|
+
.filter(Boolean)
|
|
377
|
+
.join(' ');
|
|
378
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: rootClasses, "data-muka-menu-search": "", children: [(0, jsx_runtime_1.jsx)("span", { className: "muka-menu__search-icon", "aria-hidden": "true", children: (0, jsx_runtime_1.jsx)(Icon_1.Icon, { name: "search", variant: "line", size: iconSize }) }), (0, jsx_runtime_1.jsx)("input", { ref: ref, type: "search", className: "muka-menu__search-input", value: query, onChange: (e) => setQuery(e.target.value), onKeyDown: handleKeyDown, placeholder: placeholder, "aria-label": ariaLabel || placeholder, autoComplete: "off", autoCorrect: "off", spellCheck: false }), query.length > 0 && ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "muka-menu__search-clear", onClick: () => setQuery(''), "aria-label": "Clear search", tabIndex: -1, children: (0, jsx_runtime_1.jsx)(Icon_1.Icon, { name: "close", variant: "line", size: iconSize }) }))] }));
|
|
379
|
+
});
|
|
380
|
+
exports.MenuSearch.displayName = 'MenuSearch';
|
|
82
381
|
/**
|
|
83
382
|
* MenuItem - An action item within the menu
|
|
84
383
|
*/
|
|
85
|
-
exports.MenuItem = react_1.default.forwardRef(({ children, onSelect, disabled = false, destructive = false, icon, shortcut, className = '', }, ref) => {
|
|
384
|
+
exports.MenuItem = react_1.default.forwardRef(({ children, onSelect, disabled = false, destructive = false, icon, shortcut, hint, highlighted = false, id, className = '', textValue, }, ref) => {
|
|
385
|
+
const Primitives = useMenuPrimitives();
|
|
386
|
+
const { query, searchable } = useMenuSearch();
|
|
387
|
+
const labelText = textValue ?? getSearchableText(children);
|
|
388
|
+
const hidden = Boolean(searchable && query && !matchesSearch(labelText, query));
|
|
389
|
+
useReportVisibleSearchResult(searchable && Boolean(query) && !hidden);
|
|
390
|
+
const itemRef = (0, react_1.useRef)(null);
|
|
391
|
+
const setRefs = (0, react_1.useCallback)((node) => {
|
|
392
|
+
itemRef.current = node;
|
|
393
|
+
if (typeof ref === 'function')
|
|
394
|
+
ref(node);
|
|
395
|
+
else if (ref)
|
|
396
|
+
ref.current = node;
|
|
397
|
+
}, [ref]);
|
|
398
|
+
const searchHighlighted = useSearchNavHighlight({
|
|
399
|
+
enabled: searchable && !hidden,
|
|
400
|
+
disabled,
|
|
401
|
+
select: () => itemRef.current?.click(),
|
|
402
|
+
});
|
|
403
|
+
if (hidden) {
|
|
404
|
+
return null;
|
|
405
|
+
}
|
|
406
|
+
const isHighlighted = highlighted || searchHighlighted;
|
|
407
|
+
const showEnterHint = (hint === 'enter' || searchHighlighted) && !shortcut;
|
|
86
408
|
const itemClasses = [
|
|
87
409
|
'muka-menu__item',
|
|
410
|
+
showEnterHint && 'muka-menu__item--hint-enter',
|
|
411
|
+
shortcut && 'muka-menu__item--has-shortcut',
|
|
88
412
|
destructive && 'muka-menu__item--destructive',
|
|
89
413
|
disabled && 'muka-menu__item--disabled',
|
|
90
414
|
className,
|
|
91
415
|
]
|
|
92
416
|
.filter(Boolean)
|
|
93
417
|
.join(' ');
|
|
94
|
-
return ((0, jsx_runtime_1.jsxs)(
|
|
418
|
+
return ((0, jsx_runtime_1.jsxs)(Primitives.Item, { ref: setRefs, id: id, className: itemClasses, onSelect: onSelect, disabled: disabled, textValue: labelText, "aria-label": searchable && query ? labelText : undefined, "data-highlighted": isHighlighted ? '' : undefined, children: [icon && (0, jsx_runtime_1.jsx)("span", { className: "muka-menu__item-icon", children: icon }), (0, jsx_runtime_1.jsx)("span", { className: "muka-menu__item-label", children: searchable && query ? highlightMenuMatch(labelText, query) : children }), shortcut ? ((0, jsx_runtime_1.jsx)("span", { className: "muka-menu__item-shortcut", children: shortcut })) : showEnterHint ? ((0, jsx_runtime_1.jsx)("span", { className: "muka-menu__item-hint", "aria-hidden": "true", children: (0, jsx_runtime_1.jsx)(Icon_1.Icon, { name: "arrow-return", size: "md", color: "var(--color-text-muted-default)" }) })) : null] }));
|
|
95
419
|
});
|
|
96
420
|
exports.MenuItem.displayName = 'MenuItem';
|
|
97
421
|
/**
|
|
98
422
|
* MenuSeparator - A visual divider between menu items
|
|
99
423
|
*/
|
|
100
424
|
exports.MenuSeparator = react_1.default.forwardRef(({ className = '' }, ref) => {
|
|
425
|
+
const Primitives = useMenuPrimitives();
|
|
101
426
|
const separatorClasses = ['muka-menu__separator', className].filter(Boolean).join(' ');
|
|
102
|
-
return (0, jsx_runtime_1.jsx)(
|
|
427
|
+
return (0, jsx_runtime_1.jsx)(Primitives.Separator, { ref: ref, className: separatorClasses });
|
|
103
428
|
});
|
|
104
429
|
exports.MenuSeparator.displayName = 'MenuSeparator';
|
|
105
430
|
/**
|
|
106
431
|
* MenuGroup - A labeled group of menu items
|
|
107
432
|
*/
|
|
108
433
|
exports.MenuGroup = react_1.default.forwardRef(({ children, label, className = '' }, ref) => {
|
|
434
|
+
const Primitives = useMenuPrimitives();
|
|
109
435
|
const groupClasses = ['muka-menu__group', className].filter(Boolean).join(' ');
|
|
110
|
-
return ((0, jsx_runtime_1.jsxs)(
|
|
436
|
+
return ((0, jsx_runtime_1.jsxs)(Primitives.Group, { ref: ref, className: groupClasses, children: [label && (0, jsx_runtime_1.jsx)(Primitives.Label, { className: "muka-menu__group-label", children: label }), children] }));
|
|
111
437
|
});
|
|
112
438
|
exports.MenuGroup.displayName = 'MenuGroup';
|
|
113
439
|
/**
|
|
114
440
|
* MenuCheckboxItem - A checkable menu item
|
|
115
441
|
*/
|
|
116
|
-
exports.MenuCheckboxItem = react_1.default.forwardRef(({ children, checked, onCheckedChange, disabled = false, icon, className = '' }, ref) => {
|
|
442
|
+
exports.MenuCheckboxItem = react_1.default.forwardRef(({ children, checked, onCheckedChange, disabled = false, icon, indicator: indicatorPlacement = 'leading', className = '', textValue, }, ref) => {
|
|
443
|
+
const Primitives = useMenuPrimitives();
|
|
444
|
+
const { query, searchable } = useMenuSearch();
|
|
445
|
+
const labelText = textValue ?? getSearchableText(children);
|
|
446
|
+
const hidden = Boolean(searchable && query && !matchesSearch(labelText, query));
|
|
447
|
+
useReportVisibleSearchResult(searchable && Boolean(query) && !hidden);
|
|
448
|
+
const itemRef = (0, react_1.useRef)(null);
|
|
449
|
+
const setRefs = (0, react_1.useCallback)((node) => {
|
|
450
|
+
itemRef.current = node;
|
|
451
|
+
if (typeof ref === 'function')
|
|
452
|
+
ref(node);
|
|
453
|
+
else if (ref)
|
|
454
|
+
ref.current = node;
|
|
455
|
+
}, [ref]);
|
|
456
|
+
const searchHighlighted = useSearchNavHighlight({
|
|
457
|
+
enabled: searchable && !hidden,
|
|
458
|
+
disabled,
|
|
459
|
+
select: () => itemRef.current?.click(),
|
|
460
|
+
});
|
|
461
|
+
if (hidden) {
|
|
462
|
+
return null;
|
|
463
|
+
}
|
|
464
|
+
const trailing = indicatorPlacement === 'trailing' || Boolean(icon);
|
|
465
|
+
// Search highlight owns the trailing slot for the return affordance.
|
|
466
|
+
const showTrailingCheck = trailing && !searchHighlighted;
|
|
117
467
|
const itemClasses = [
|
|
118
468
|
'muka-menu__item',
|
|
119
469
|
'muka-menu__item--checkbox',
|
|
470
|
+
trailing && 'muka-menu__item--checkbox-trailing',
|
|
471
|
+
searchHighlighted && 'muka-menu__item--hint-enter',
|
|
120
472
|
disabled && 'muka-menu__item--disabled',
|
|
121
473
|
className,
|
|
122
474
|
]
|
|
123
475
|
.filter(Boolean)
|
|
124
476
|
.join(' ');
|
|
125
|
-
const indicator = ((0, jsx_runtime_1.jsx)("span", { className:
|
|
126
|
-
|
|
477
|
+
const indicator = ((0, jsx_runtime_1.jsx)("span", { className: [
|
|
478
|
+
'muka-menu__item-indicator',
|
|
479
|
+
trailing && 'muka-menu__item-indicator--trailing',
|
|
480
|
+
]
|
|
481
|
+
.filter(Boolean)
|
|
482
|
+
.join(' '), children: (0, jsx_runtime_1.jsx)(Primitives.ItemIndicator, { children: (0, jsx_runtime_1.jsx)(Icon_1.Icon, { name: "check", size: "md", color: trailing
|
|
483
|
+
? 'var(--color-text-subtle-default)'
|
|
484
|
+
: undefined }) }) }));
|
|
485
|
+
return ((0, jsx_runtime_1.jsxs)(Primitives.CheckboxItem, { ref: setRefs, className: itemClasses, checked: checked, onCheckedChange: onCheckedChange, disabled: disabled, textValue: labelText, "aria-label": searchable && query ? labelText : undefined, "data-highlighted": searchHighlighted ? '' : undefined, children: [icon ? (0, jsx_runtime_1.jsx)("span", { className: "muka-menu__item-icon", children: icon }) : null, !trailing ? indicator : null, (0, jsx_runtime_1.jsx)("span", { className: "muka-menu__item-label", children: searchable && query ? highlightMenuMatch(labelText, query) : children }), showTrailingCheck ? indicator : null, searchHighlighted ? ((0, jsx_runtime_1.jsx)("span", { className: "muka-menu__item-hint", "aria-hidden": "true", children: (0, jsx_runtime_1.jsx)(Icon_1.Icon, { name: "arrow-return", size: "md", color: "var(--color-text-muted-default)" }) })) : null] }));
|
|
127
486
|
});
|
|
128
487
|
exports.MenuCheckboxItem.displayName = 'MenuCheckboxItem';
|
|
129
488
|
/**
|
|
130
489
|
* MenuRadioGroup - A group of radio menu items
|
|
131
490
|
*/
|
|
132
491
|
exports.MenuRadioGroup = react_1.default.forwardRef(({ children, value, onValueChange, className = '' }, ref) => {
|
|
492
|
+
const Primitives = useMenuPrimitives();
|
|
133
493
|
const groupClasses = ['muka-menu__radio-group', className].filter(Boolean).join(' ');
|
|
134
|
-
return ((0, jsx_runtime_1.jsx)(
|
|
494
|
+
return ((0, jsx_runtime_1.jsx)(Primitives.RadioGroup, { ref: ref, className: groupClasses, value: value, onValueChange: onValueChange, children: children }));
|
|
135
495
|
});
|
|
136
496
|
exports.MenuRadioGroup.displayName = 'MenuRadioGroup';
|
|
137
497
|
/**
|
|
138
498
|
* MenuRadioItem - A radio option within a MenuRadioGroup
|
|
139
499
|
*/
|
|
140
|
-
exports.MenuRadioItem = react_1.default.forwardRef(({ children, value, disabled = false, className = '' }, ref) => {
|
|
500
|
+
exports.MenuRadioItem = react_1.default.forwardRef(({ children, value, disabled = false, className = '', indicator = 'dot', textValue, }, ref) => {
|
|
501
|
+
const Primitives = useMenuPrimitives();
|
|
502
|
+
const { query, searchable } = useMenuSearch();
|
|
503
|
+
const labelText = textValue ?? getSearchableText(children);
|
|
504
|
+
const hidden = Boolean(searchable && query && !matchesSearch(labelText, query));
|
|
505
|
+
useReportVisibleSearchResult(searchable && Boolean(query) && !hidden);
|
|
506
|
+
const itemRef = (0, react_1.useRef)(null);
|
|
507
|
+
const setRefs = (0, react_1.useCallback)((node) => {
|
|
508
|
+
itemRef.current = node;
|
|
509
|
+
if (typeof ref === 'function')
|
|
510
|
+
ref(node);
|
|
511
|
+
else if (ref)
|
|
512
|
+
ref.current = node;
|
|
513
|
+
}, [ref]);
|
|
514
|
+
const searchHighlighted = useSearchNavHighlight({
|
|
515
|
+
enabled: searchable && !hidden,
|
|
516
|
+
disabled,
|
|
517
|
+
select: () => itemRef.current?.click(),
|
|
518
|
+
});
|
|
519
|
+
if (hidden) {
|
|
520
|
+
return null;
|
|
521
|
+
}
|
|
522
|
+
const showTrailingCheck = indicator === 'check' && !searchHighlighted;
|
|
141
523
|
const itemClasses = [
|
|
142
524
|
'muka-menu__item',
|
|
143
525
|
'muka-menu__item--radio',
|
|
526
|
+
indicator === 'check' && 'muka-menu__item--radio-check',
|
|
527
|
+
searchHighlighted && 'muka-menu__item--hint-enter',
|
|
144
528
|
disabled && 'muka-menu__item--disabled',
|
|
145
529
|
className,
|
|
146
530
|
]
|
|
147
531
|
.filter(Boolean)
|
|
148
532
|
.join(' ');
|
|
149
|
-
return ((0, jsx_runtime_1.jsxs)(
|
|
533
|
+
return ((0, jsx_runtime_1.jsxs)(Primitives.RadioItem, { ref: setRefs, className: itemClasses, value: value, disabled: disabled, textValue: labelText, "aria-label": searchable && query ? labelText : undefined, "data-highlighted": searchHighlighted ? '' : undefined, children: [indicator === 'dot' && ((0, jsx_runtime_1.jsx)("span", { className: "muka-menu__item-indicator", children: (0, jsx_runtime_1.jsx)(Primitives.ItemIndicator, { children: (0, jsx_runtime_1.jsx)("span", { className: "muka-menu__radio-dot" }) }) })), (0, jsx_runtime_1.jsx)("span", { className: "muka-menu__item-label", children: searchable && query ? highlightMenuMatch(labelText, query) : children }), showTrailingCheck && ((0, jsx_runtime_1.jsx)("span", { className: "muka-menu__item-indicator muka-menu__item-indicator--trailing", children: (0, jsx_runtime_1.jsx)(Primitives.ItemIndicator, { children: (0, jsx_runtime_1.jsx)(Icon_1.Icon, { name: "check", size: "md", color: "var(--color-text-subtle-default)" }) }) })), searchHighlighted ? ((0, jsx_runtime_1.jsx)("span", { className: "muka-menu__item-hint", "aria-hidden": "true", children: (0, jsx_runtime_1.jsx)(Icon_1.Icon, { name: "arrow-return", size: "md", color: "var(--color-text-muted-default)" }) })) : null] }));
|
|
150
534
|
});
|
|
151
535
|
exports.MenuRadioItem.displayName = 'MenuRadioItem';
|
|
152
536
|
/**
|
|
153
537
|
* MenuSub - A submenu container
|
|
154
538
|
*/
|
|
155
539
|
const MenuSub = ({ children, open, onOpenChange, defaultOpen, }) => {
|
|
156
|
-
|
|
540
|
+
const Primitives = useMenuPrimitives();
|
|
541
|
+
return ((0, jsx_runtime_1.jsx)(Primitives.Sub, { open: open, onOpenChange: onOpenChange, defaultOpen: defaultOpen, children: children }));
|
|
157
542
|
};
|
|
158
543
|
exports.MenuSub = MenuSub;
|
|
159
544
|
/**
|
|
160
545
|
* MenuSubTrigger - Trigger that opens a submenu
|
|
161
546
|
*/
|
|
162
547
|
exports.MenuSubTrigger = react_1.default.forwardRef(({ children, icon, disabled = false, className = '' }, ref) => {
|
|
548
|
+
const Primitives = useMenuPrimitives();
|
|
163
549
|
const itemClasses = [
|
|
164
550
|
'muka-menu__item',
|
|
165
551
|
'muka-menu__item--sub-trigger',
|
|
@@ -168,16 +554,20 @@ exports.MenuSubTrigger = react_1.default.forwardRef(({ children, icon, disabled
|
|
|
168
554
|
]
|
|
169
555
|
.filter(Boolean)
|
|
170
556
|
.join(' ');
|
|
171
|
-
return ((0, jsx_runtime_1.jsxs)(
|
|
557
|
+
return ((0, jsx_runtime_1.jsxs)(Primitives.SubTrigger, { ref: ref, className: itemClasses, disabled: disabled, children: [icon && (0, jsx_runtime_1.jsx)("span", { className: "muka-menu__item-icon", children: icon }), (0, jsx_runtime_1.jsx)("span", { className: "muka-menu__item-label", children: children }), (0, jsx_runtime_1.jsx)("span", { className: "muka-menu__item-chevron", children: (0, jsx_runtime_1.jsx)(Icon_1.Icon, { name: "arrow-right", variant: "line", size: "sm" }) })] }));
|
|
172
558
|
});
|
|
173
559
|
exports.MenuSubTrigger.displayName = 'MenuSubTrigger';
|
|
174
560
|
/**
|
|
175
561
|
* MenuSubContent - The dropdown container for submenu items
|
|
176
562
|
*/
|
|
177
563
|
exports.MenuSubContent = react_1.default.forwardRef(({ children, sideOffset = 2, alignOffset = -4, className = '', loop = true }, ref) => {
|
|
564
|
+
const { trigger } = (0, react_1.useContext)(MenuContext);
|
|
178
565
|
const contentClasses = ['muka-menu__content', 'muka-menu__content--sub', className]
|
|
179
566
|
.filter(Boolean)
|
|
180
567
|
.join(' ');
|
|
568
|
+
if (trigger === 'context') {
|
|
569
|
+
return ((0, jsx_runtime_1.jsx)(ContextMenuPrimitive.Portal, { children: (0, jsx_runtime_1.jsx)(ContextMenuPrimitive.SubContent, { ref: ref, sideOffset: sideOffset, alignOffset: alignOffset, className: contentClasses, loop: loop, children: children }) }));
|
|
570
|
+
}
|
|
181
571
|
return ((0, jsx_runtime_1.jsx)(DropdownMenuPrimitive.Portal, { children: (0, jsx_runtime_1.jsx)(DropdownMenuPrimitive.SubContent, { ref: ref, sideOffset: sideOffset, alignOffset: alignOffset, className: contentClasses, loop: loop, children: children }) }));
|
|
182
572
|
});
|
|
183
573
|
exports.MenuSubContent.displayName = 'MenuSubContent';
|