0
# Utilities
1
2
Utility functions for advanced usage patterns, context management, and creating isolated menubar instances.
3
4
## Capabilities
5
6
### createMenubarScope
7
8
Creates a scoped context for menubar components to avoid conflicts when nesting menubars or using multiple menubar instances.
9
10
```typescript { .api }
11
/**
12
* Creates a scoped context for menubar components
13
* @returns Scope hook function for context isolation
14
*/
15
function createMenubarScope(): ScopeHook;
16
17
type ScopeHook = () => Scope;
18
19
interface Scope {
20
__scopeMenubar?: string;
21
}
22
```
23
24
**Usage Examples:**
25
26
```typescript
27
'use client';
28
import * as Menubar from "@radix-ui/react-menubar";
29
import { createMenubarScope } from "@radix-ui/react-menubar";
30
31
// Creating isolated menubar scopes
32
function MultipleMenubars() {
33
const scope1 = createMenubarScope();
34
const scope2 = createMenubarScope();
35
36
return (
37
<div>
38
<Menubar.Root {...scope1()}>
39
<Menubar.Menu {...scope1()}>
40
<Menubar.Trigger {...scope1()}>File</Menubar.Trigger>
41
<Menubar.Portal {...scope1()}>
42
<Menubar.Content {...scope1()}>
43
<Menubar.Item {...scope1()}>New</Menubar.Item>
44
</Menubar.Content>
45
</Menubar.Portal>
46
</Menubar.Menu>
47
</Menubar.Root>
48
49
<Menubar.Root {...scope2()}>
50
<Menubar.Menu {...scope2()}>
51
<Menubar.Trigger {...scope2()}>Edit</Menubar.Trigger>
52
<Menubar.Portal {...scope2()}>
53
<Menubar.Content {...scope2()}>
54
<Menubar.Item {...scope2()}>Cut</Menubar.Item>
55
</Menubar.Content>
56
</Menubar.Portal>
57
</Menubar.Menu>
58
</Menubar.Root>
59
</div>
60
);
61
}
62
63
// Nested menubar components with scoping
64
function NestedMenubarExample() {
65
const outerScope = createMenubarScope();
66
const innerScope = createMenubarScope();
67
68
return (
69
<div className="app-container">
70
{/* Main application menubar */}
71
<Menubar.Root {...outerScope()}>
72
<Menubar.Menu {...outerScope()}>
73
<Menubar.Trigger {...outerScope()}>File</Menubar.Trigger>
74
<Menubar.Portal {...outerScope()}>
75
<Menubar.Content {...outerScope()}>
76
<Menubar.Item {...outerScope()}>New</Menubar.Item>
77
<Menubar.Item {...outerScope()}>Open</Menubar.Item>
78
</Menubar.Content>
79
</Menubar.Portal>
80
</Menubar.Menu>
81
</Menubar.Root>
82
83
{/* Secondary context-specific menubar */}
84
<div className="editor-panel">
85
<Menubar.Root {...innerScope()}>
86
<Menubar.Menu {...innerScope()}>
87
<Menubar.Trigger {...innerScope()}>Tools</Menubar.Trigger>
88
<Menubar.Portal {...innerScope()}>
89
<Menubar.Content {...innerScope()}>
90
<Menubar.Item {...innerScope()}>Format</Menubar.Item>
91
<Menubar.Item {...innerScope()}>Validate</Menubar.Item>
92
</Menubar.Content>
93
</Menubar.Portal>
94
</Menubar.Menu>
95
</Menubar.Root>
96
</div>
97
</div>
98
);
99
}
100
```
101
102
## Advanced Usage Patterns
103
104
### Custom Hook for Menubar State
105
106
```typescript
107
'use client';
108
import * as React from 'react';
109
import * as Menubar from "@radix-ui/react-menubar";
110
111
// Custom hook for managing menubar state
112
function useMenubarState(defaultValue?: string) {
113
const [activeMenu, setActiveMenu] = React.useState(defaultValue ?? "");
114
const [menuHistory, setMenuHistory] = React.useState<string[]>([]);
115
116
const openMenu = React.useCallback((value: string) => {
117
setActiveMenu(value);
118
setMenuHistory(prev => [...prev.filter(v => v !== value), value]);
119
}, []);
120
121
const closeMenu = React.useCallback(() => {
122
setActiveMenu("");
123
}, []);
124
125
const toggleMenu = React.useCallback((value: string) => {
126
setActiveMenu(prev => prev === value ? "" : value);
127
}, []);
128
129
return {
130
activeMenu,
131
menuHistory,
132
openMenu,
133
closeMenu,
134
toggleMenu,
135
// For controlled menubar
136
value: activeMenu,
137
onValueChange: setActiveMenu
138
};
139
}
140
141
// Usage of custom hook
142
function StatefulMenubar() {
143
const menuState = useMenubarState();
144
145
return (
146
<Menubar.Root
147
value={menuState.value}
148
onValueChange={menuState.onValueChange}
149
>
150
<Menubar.Menu value="file">
151
<Menubar.Trigger>File</Menubar.Trigger>
152
<Menubar.Portal>
153
<Menubar.Content>
154
<Menubar.Item>New</Menubar.Item>
155
<Menubar.Item>Open</Menubar.Item>
156
</Menubar.Content>
157
</Menubar.Portal>
158
</Menubar.Menu>
159
160
<Menubar.Menu value="edit">
161
<Menubar.Trigger>Edit</Menubar.Trigger>
162
<Menubar.Portal>
163
<Menubar.Content>
164
<Menubar.Item>Cut</Menubar.Item>
165
<Menubar.Item>Copy</Menubar.Item>
166
</Menubar.Content>
167
</Menubar.Portal>
168
</Menubar.Menu>
169
</Menubar.Root>
170
);
171
}
172
```
173
174
### Menubar Component Factory
175
176
```typescript
177
'use client';
178
import * as React from 'react';
179
import * as Menubar from "@radix-ui/react-menubar";
180
import { createMenubarScope } from "@radix-ui/react-menubar";
181
182
// Factory function for creating configured menubar components
183
function createMenubar(options: MenubarOptions = {}) {
184
const scope = createMenubarScope();
185
const {
186
defaultLoop = true,
187
defaultDirection = 'ltr',
188
className = '',
189
...defaultProps
190
} = options;
191
192
const ConfiguredRoot = React.forwardRef<
193
React.ComponentRef<typeof Menubar.Root>,
194
Omit<React.ComponentProps<typeof Menubar.Root>, 'loop' | 'dir'>
195
>((props, ref) => (
196
<Menubar.Root
197
{...scope()}
198
ref={ref}
199
loop={defaultLoop}
200
dir={defaultDirection}
201
className={`${className} ${props.className || ''}`.trim()}
202
{...defaultProps}
203
{...props}
204
/>
205
));
206
207
const ConfiguredMenu = React.forwardRef<
208
React.ComponentRef<typeof Menubar.Menu>,
209
React.ComponentProps<typeof Menubar.Menu>
210
>((props, ref) => (
211
<Menubar.Menu {...scope()} {...props} />
212
));
213
214
const ConfiguredTrigger = React.forwardRef<
215
React.ComponentRef<typeof Menubar.Trigger>,
216
React.ComponentProps<typeof Menubar.Trigger>
217
>((props, ref) => (
218
<Menubar.Trigger {...scope()} ref={ref} {...props} />
219
));
220
221
// ... configure other components similarly
222
223
return {
224
Root: ConfiguredRoot,
225
Menu: ConfiguredMenu,
226
Trigger: ConfiguredTrigger,
227
// ... other configured components
228
};
229
}
230
231
interface MenubarOptions {
232
defaultLoop?: boolean;
233
defaultDirection?: 'ltr' | 'rtl';
234
className?: string;
235
[key: string]: any;
236
}
237
238
// Usage of factory
239
const MyMenubar = createMenubar({
240
defaultLoop: false,
241
defaultDirection: 'rtl',
242
className: 'my-custom-menubar'
243
});
244
245
function FactoryMenubarExample() {
246
return (
247
<MyMenubar.Root>
248
<MyMenubar.Menu>
249
<MyMenubar.Trigger>File</MyMenubar.Trigger>
250
{/* ... */}
251
</MyMenubar.Menu>
252
</MyMenubar.Root>
253
);
254
}
255
```
256
257
### Accessibility Helpers
258
259
```typescript
260
'use client';
261
import * as Menubar from "@radix-ui/react-menubar";
262
263
// Helper functions for accessibility
264
function getMenubarAccessibilityProps(label: string) {
265
return {
266
'aria-label': label,
267
role: 'menubar'
268
};
269
}
270
271
function getMenuItemAccessibilityProps(
272
label: string,
273
shortcut?: string,
274
description?: string
275
) {
276
return {
277
'aria-label': shortcut ? `${label} ${shortcut}` : label,
278
'aria-description': description,
279
textValue: label
280
};
281
}
282
283
// Usage with accessibility helpers
284
function AccessibleMenubar() {
285
return (
286
<Menubar.Root {...getMenubarAccessibilityProps("Main menu")}>
287
<Menubar.Menu>
288
<Menubar.Trigger>File</Menubar.Trigger>
289
<Menubar.Portal>
290
<Menubar.Content>
291
<Menubar.Item
292
{...getMenuItemAccessibilityProps(
293
"New File",
294
"Ctrl+N",
295
"Create a new document"
296
)}
297
onSelect={handleNew}
298
>
299
New File
300
</Menubar.Item>
301
302
<Menubar.Item
303
{...getMenuItemAccessibilityProps(
304
"Open File",
305
"Ctrl+O",
306
"Open an existing document"
307
)}
308
onSelect={handleOpen}
309
>
310
Open File
311
</Menubar.Item>
312
</Menubar.Content>
313
</Menubar.Portal>
314
</Menubar.Menu>
315
</Menubar.Root>
316
);
317
}
318
```
319
320
## Type Definitions
321
322
```typescript { .api }
323
// Utility types
324
type ScopeHook = () => Scope;
325
326
interface Scope {
327
__scopeMenubar?: string;
328
}
329
330
// Configuration types
331
interface MenubarOptions {
332
defaultLoop?: boolean;
333
defaultDirection?: 'ltr' | 'rtl';
334
className?: string;
335
[key: string]: any;
336
}
337
338
// State management types
339
interface MenubarState {
340
activeMenu: string;
341
menuHistory: string[];
342
openMenu: (value: string) => void;
343
closeMenu: () => void;
344
toggleMenu: (value: string) => void;
345
value: string;
346
onValueChange: (value: string) => void;
347
}
348
```