or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-components.mdindex.mdinteractive-items.mdmenu-items.mdsubmenus.mdutilities.md

utilities.mddocs/

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

```