or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

component-api.mdfunctional-api.mdindex.mdmenu-bar.mdthemes.md
tile.json

menu-bar.mddocs/

0

# Menu Bar

1

2

The MenuBar component provides a traditional horizontal menu bar interface similar to desktop applications. It supports both expanded and collapsed (mini) modes, with dropdown context menus for each menu item.

3

4

## Capabilities

5

6

### MenuBar Component

7

8

Horizontal menu bar component that displays menu items and shows dropdown context menus when clicked.

9

10

```typescript { .api }

11

declare component MenuBar {

12

props: {

13

/** Menu bar configuration options */

14

options: MenuBarOptions;

15

};

16

slots: {

17

/** Content before menu items */

18

prefix: {};

19

/** Content after menu items */

20

suffix: {};

21

};

22

}

23

24

interface MenuBarOptions extends Omit<MenuOptions, 'x'|'y'|'getContainer'> {

25

/** Whether the menu bar is in collapsed/mini mode */

26

mini?: boolean;

27

/** Popup direction for dropdown menus in collapsed state */

28

barPopDirection?: MenuPopDirection;

29

/** Array of top-level menu items */

30

items?: MenuItem[];

31

/** Menu theme */

32

theme?: string;

33

/** Other MenuOptions properties... */

34

}

35

```

36

37

**Usage Examples:**

38

39

```vue

40

<template>

41

<!-- Basic menu bar -->

42

<menu-bar :options="basicMenuOptions" />

43

44

<!-- Menu bar with prefix/suffix slots -->

45

<menu-bar :options="advancedMenuOptions">

46

<template #prefix>

47

<div class="app-logo">

48

<img src="/logo.png" alt="App Logo" />

49

</div>

50

</template>

51

52

<template #suffix>

53

<div class="menu-actions">

54

<button @click="showSettings">Settings</button>

55

<button @click="showHelp">Help</button>

56

</div>

57

</template>

58

</menu-bar>

59

60

<!-- Collapsed/mini menu bar -->

61

<menu-bar :options="miniMenuOptions" />

62

</template>

63

64

<script setup>

65

import { ref } from 'vue';

66

67

// Basic menu bar configuration

68

const basicMenuOptions = ref({

69

theme: 'default',

70

items: [

71

{

72

label: 'File',

73

children: [

74

{ label: 'New', shortcut: 'Ctrl+N', onClick: createNew },

75

{ label: 'Open', shortcut: 'Ctrl+O', onClick: openFile },

76

{ label: 'Save', shortcut: 'Ctrl+S', onClick: saveFile },

77

{ divided: true, label: 'Exit', onClick: exitApp }

78

]

79

},

80

{

81

label: 'Edit',

82

children: [

83

{ label: 'Undo', shortcut: 'Ctrl+Z', onClick: undo },

84

{ label: 'Redo', shortcut: 'Ctrl+Y', onClick: redo },

85

{ divided: true, label: 'Cut', shortcut: 'Ctrl+X', onClick: cut },

86

{ label: 'Copy', shortcut: 'Ctrl+C', onClick: copy },

87

{ label: 'Paste', shortcut: 'Ctrl+V', onClick: paste }

88

]

89

},

90

{

91

label: 'View',

92

children: [

93

{ label: 'Zoom In', shortcut: 'Ctrl++', onClick: zoomIn },

94

{ label: 'Zoom Out', shortcut: 'Ctrl+-', onClick: zoomOut },

95

{ label: 'Reset Zoom', shortcut: 'Ctrl+0', onClick: resetZoom }

96

]

97

}

98

]

99

});

100

101

// Advanced menu bar with dynamic content

102

const advancedMenuOptions = ref({

103

theme: 'win10',

104

minWidth: 150,

105

keyboardControl: true,

106

items: [

107

{

108

label: 'File',

109

children: [

110

{ label: 'New Project', onClick: createProject },

111

{

112

label: 'Recent Projects',

113

children: getRecentProjects() // Dynamic submenu

114

},

115

{ divided: true, label: 'Import', onClick: importData },

116

{ label: 'Export', onClick: exportData }

117

]

118

},

119

{

120

label: 'Tools',

121

children: [

122

{ label: 'Preferences', onClick: showPreferences },

123

{ label: 'Extensions', onClick: showExtensions },

124

{

125

label: 'Developer',

126

children: [

127

{ label: 'Console', onClick: showConsole },

128

{ label: 'Debugger', onClick: showDebugger }

129

]

130

}

131

]

132

}

133

]

134

});

135

136

// Mini/collapsed menu bar

137

const miniMenuOptions = ref({

138

mini: true,

139

theme: 'flat dark',

140

barPopDirection: 'bl',

141

items: [

142

{

143

label: 'Menu', // This label is not shown in mini mode

144

children: [

145

{ label: 'File Operations', children: getFileOperations() },

146

{ label: 'Edit Operations', children: getEditOperations() },

147

{ label: 'View Options', children: getViewOptions() },

148

{ divided: true, label: 'Settings', onClick: showSettings },

149

{ label: 'About', onClick: showAbout }

150

]

151

}

152

]

153

});

154

155

function getRecentProjects() {

156

return [

157

{ label: 'Project Alpha', onClick: () => openProject('alpha') },

158

{ label: 'Project Beta', onClick: () => openProject('beta') },

159

{ label: 'Project Gamma', onClick: () => openProject('gamma') }

160

];

161

}

162

</script>

163

```

164

165

### Menu Bar Modes

166

167

The MenuBar component supports two display modes:

168

169

#### Expanded Mode (Default)

170

171

Shows all top-level menu items horizontally across the bar.

172

173

```typescript { .api }

174

interface ExpandedMenuBarConfig {

175

mini: false; // or undefined

176

items: MenuItem[]; // Multiple top-level items displayed

177

}

178

```

179

180

#### Mini/Collapsed Mode

181

182

Shows a single menu button that opens a dropdown containing all menu items.

183

184

```typescript { .api }

185

interface MiniMenuBarConfig {

186

mini: true;

187

items: MenuItem[]; // All items shown in single dropdown

188

barPopDirection?: MenuPopDirection; // Direction for dropdown

189

}

190

```

191

192

**Mode Comparison:**

193

194

```vue

195

<template>

196

<!-- Expanded mode - shows "File", "Edit", "View" horizontally -->

197

<menu-bar :options="expandedOptions" />

198

199

<!-- Mini mode - shows single hamburger menu button -->

200

<menu-bar :options="miniOptions" />

201

</template>

202

203

<script setup>

204

const expandedOptions = {

205

mini: false,

206

items: [

207

{ label: 'File', children: [...] },

208

{ label: 'Edit', children: [...] },

209

{ label: 'View', children: [...] }

210

]

211

};

212

213

const miniOptions = {

214

mini: true,

215

barPopDirection: 'bl',

216

items: [

217

{

218

label: 'Menu', // Not displayed in mini mode

219

children: [

220

{ label: 'File', children: [...] },

221

{ label: 'Edit', children: [...] },

222

{ label: 'View', children: [...] }

223

]

224

}

225

]

226

};

227

</script>

228

```

229

230

## Advanced Menu Bar Patterns

231

232

### Responsive Menu Bar

233

234

```vue

235

<template>

236

<menu-bar :options="responsiveMenuOptions" />

237

</template>

238

239

<script setup>

240

import { ref, computed, onMounted, onUnmounted } from 'vue';

241

242

const screenWidth = ref(window.innerWidth);

243

244

const responsiveMenuOptions = computed(() => ({

245

mini: screenWidth.value < 768, // Mini mode on mobile

246

theme: screenWidth.value < 768 ? 'flat' : 'default',

247

barPopDirection: 'bl',

248

items: getMenuItems()

249

}));

250

251

function updateScreenWidth() {

252

screenWidth.value = window.innerWidth;

253

}

254

255

onMounted(() => {

256

window.addEventListener('resize', updateScreenWidth);

257

});

258

259

onUnmounted(() => {

260

window.removeEventListener('resize', updateScreenWidth);

261

});

262

</script>

263

```

264

265

### Menu Bar with State Management

266

267

```vue

268

<template>

269

<menu-bar :options="stateAwareMenuOptions" />

270

</template>

271

272

<script setup>

273

import { ref, computed } from 'vue';

274

275

const isProjectOpen = ref(false);

276

const hasSelection = ref(false);

277

const canUndo = ref(false);

278

const canRedo = ref(false);

279

280

const stateAwareMenuOptions = computed(() => ({

281

theme: 'win10',

282

items: [

283

{

284

label: 'File',

285

children: [

286

{ label: 'New Project', onClick: createProject },

287

{ label: 'Open Project', onClick: openProject },

288

{

289

label: 'Close Project',

290

disabled: !isProjectOpen.value,

291

onClick: closeProject

292

},

293

{ divided: true, label: 'Save', shortcut: 'Ctrl+S', onClick: save },

294

{

295

label: 'Save As...',

296

disabled: !isProjectOpen.value,

297

onClick: saveAs

298

}

299

]

300

},

301

{

302

label: 'Edit',

303

children: [

304

{

305

label: 'Undo',

306

shortcut: 'Ctrl+Z',

307

disabled: !canUndo.value,

308

onClick: undo

309

},

310

{

311

label: 'Redo',

312

shortcut: 'Ctrl+Y',

313

disabled: !canRedo.value,

314

onClick: redo

315

},

316

{ divided: true },

317

{

318

label: 'Cut',

319

shortcut: 'Ctrl+X',

320

disabled: !hasSelection.value,

321

onClick: cut

322

},

323

{

324

label: 'Copy',

325

shortcut: 'Ctrl+C',

326

disabled: !hasSelection.value,

327

onClick: copy

328

}

329

]

330

}

331

]

332

}));

333

</script>

334

```

335

336

### Menu Bar with Custom Styling

337

338

```vue

339

<template>

340

<menu-bar

341

:options="customMenuOptions"

342

class="custom-menu-bar"

343

>

344

<template #prefix>

345

<div class="brand-section">

346

<img src="/logo.svg" alt="Brand" class="brand-logo" />

347

<span class="brand-text">My App</span>

348

</div>

349

</template>

350

351

<template #suffix>

352

<div class="user-section">

353

<span class="user-name">{{ currentUser.name }}</span>

354

<button @click="showUserMenu" class="user-menu-btn">

355

<img :src="currentUser.avatar" alt="User" class="user-avatar" />

356

</button>

357

</div>

358

</template>

359

</menu-bar>

360

</template>

361

362

<style scoped>

363

.custom-menu-bar {

364

background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);

365

color: white;

366

padding: 0 16px;

367

box-shadow: 0 2px 8px rgba(0,0,0,0.1);

368

}

369

370

.brand-section {

371

display: flex;

372

align-items: center;

373

gap: 8px;

374

}

375

376

.brand-logo {

377

width: 24px;

378

height: 24px;

379

}

380

381

.user-section {

382

display: flex;

383

align-items: center;

384

gap: 12px;

385

}

386

387

.user-avatar {

388

width: 32px;

389

height: 32px;

390

border-radius: 50%;

391

}

392

</style>

393

```

394

395

## Menu Bar Configuration

396

397

### MenuBarOptions Interface

398

399

Complete configuration interface extending MenuOptions with menu bar specific properties.

400

401

```typescript { .api }

402

interface MenuBarOptions extends Omit<MenuOptions, 'x'|'y'|'getContainer'> {

403

/** Whether the menu bar is in collapsed/mini mode */

404

mini?: boolean;

405

/** Popup direction for dropdown menus in collapsed state */

406

barPopDirection?: MenuPopDirection;

407

408

// Inherited from MenuOptions:

409

/** Array of top-level menu items */

410

items?: MenuItem[];

411

/** Menu theme */

412

theme?: string;

413

/** Z-index for dropdown menus */

414

zIndex?: number;

415

/** Custom CSS class */

416

customClass?: string;

417

/** Enable keyboard navigation */

418

keyboardControl?: boolean;

419

/** Maximum width for dropdown menus */

420

maxWidth?: number;

421

/** Maximum height for dropdown menus */

422

maxHeight?: number;

423

/** Minimum width for dropdown menus */

424

minWidth?: number;

425

/** Custom icon font class */

426

iconFontClass?: string;

427

/** Reserve icon width for items without icons */

428

preserveIconWidth?: boolean;

429

/** Menu close callback */

430

onClose?: (lastClickItem: MenuItem | undefined) => void;

431

/** Keyboard focus movement callbacks */

432

onKeyFocusMoveLeft?: () => void;

433

onKeyFocusMoveRight?: () => void;

434

}

435

```

436

437

### MenuBar Keyboard Navigation

438

439

The MenuBar component supports keyboard navigation when `keyboardControl` is enabled:

440

441

- **Arrow Left/Right**: Navigate between top-level menu items

442

- **Arrow Down**: Open dropdown menu

443

- **Arrow Up**: Close dropdown menu

444

- **Enter/Space**: Activate menu item

445

- **Escape**: Close all menus

446

447

```vue

448

<template>

449

<menu-bar :options="keyboardMenuOptions" />

450

</template>

451

452

<script setup>

453

const keyboardMenuOptions = {

454

keyboardControl: true,

455

items: [...],

456

onKeyFocusMoveLeft: () => {

457

// Handle left navigation

458

console.log('Focus moved left');

459

},

460

onKeyFocusMoveRight: () => {

461

// Handle right navigation

462

console.log('Focus moved right');

463

}

464

};

465

</script>

466

```