Genel Bakış
Menü çubuğu, uygulama menülerine kalıcı erişim sağlayan yatay bir navigasyon çubuğudur. Menü çubukları komutları Dosya, Düzenle ve Görüntüle gibi mantıksal kategorilere düzenleyerek kullanıcıların klavye veya fare etkileşimi aracılığıyla uygulama özelliklerini keşfetmesine ve çalıştırmasına yardımcı olur.
app.ts
import {Component, signal, viewChild} from '@angular/core';
import {MenuBar, Menu, MenuContent, MenuItem} from '@angular/aria/menu';
import {OverlayModule} from '@angular/cdk/overlay';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [MenuBar, Menu, MenuContent, MenuItem, OverlayModule],
})
export class App {
fileMenu = viewChild<Menu<string>>('fileMenu');
shareMenu = viewChild<Menu<string>>('shareMenu');
editMenu = viewChild<Menu<string>>('editMenu');
viewMenu = viewChild<Menu<string>>('viewMenu');
insertMenu = viewChild<Menu<string>>('insertMenu');
imageMenu = viewChild<Menu<string>>('imageMenu');
chartMenu = viewChild<Menu<string>>('chartMenu');
formatMenu = viewChild<Menu<string>>('formatMenu');
textMenu = viewChild<Menu<string>>('textMenu');
sizeMenu = viewChild<Menu<string>>('sizeMenu');
paragraphMenu = viewChild<Menu<string>>('paragraphMenu');
alignMenu = viewChild<Menu<string>>('alignMenu');
rendered = signal(false);
onFocusIn() {
this.rendered.set(true);
}
}
app.html
<div ngMenuBar (focusin)="onFocusIn()">
<div
ngMenuItem
#fileEl
#fileItem="ngMenuItem"
class="menu-bar-item"
value="File"
[submenu]="fileMenu()"
>
File
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: fileEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #fileMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="New">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>article</span
>
<span class="label">New</span>
<span class="shortcut">⌘N</span>
</div>
<div ngMenuItem value="Open">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>folder</span
>
<span class="label">Open</span>
<span class="shortcut">⌘O</span>
</div>
<div ngMenuItem value="Make a copy">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>file_copy</span
>
<span class="label">Make a copy</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem #shareEl value="Share" [submenu]="shareMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>person_add</span
>
<span class="label">Share</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!fileItem.expanded()"
[cdkConnectedOverlay]="{origin: shareEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #shareMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Share with others">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>person_add</span
>
<span class="label">Share with others</span>
</div>
<div ngMenuItem value="Publish to web">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>public</span
>
<span class="label">Publish to web</span>
</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem value="Download">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>download</span
>
<span class="label">Download</span>
</div>
<div ngMenuItem value="Print">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>print</span
>
<span class="label">Print</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Rename">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">edit</span>
<span class="label">Rename</span>
</div>
<div ngMenuItem value="Delete">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>delete</span
>
<span class="label">Move to trash</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#editEl
#editItem="ngMenuItem"
class="menu-bar-item"
value="Edit"
[submenu]="editMenu()"
>
Edit
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: editEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #editMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Undo">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">undo</span>
<span class="label">Undo</span>
<span class="shortcut">⌘Z</span>
</div>
<div ngMenuItem value="Redo">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">redo</span>
<span class="label">Redo</span>
<span class="shortcut">⌘Y</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Cut">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>content_cut</span
>
<span class="label">Cut</span>
<span class="shortcut">⌘X</span>
</div>
<div ngMenuItem value="Copy">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>content_copy</span
>
<span class="label">Copy</span>
<span class="shortcut">⌘C</span>
</div>
<div ngMenuItem value="Paste">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>content_paste</span
>
<span class="label">Paste</span>
<span class="shortcut">⌘V</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Find and replace">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>find_replace</span
>
<span class="label">Find and replace</span>
<span class="shortcut">⇧⌘H</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#viewEl
#viewItem="ngMenuItem"
class="menu-bar-item"
value="View"
[submenu]="viewMenu()"
>
View
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: viewEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #viewMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Show print layout" [disabled]="true">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>check</span
>
<span class="label">Show print layout</span>
</div>
<div ngMenuItem value="Show ruler" [disabled]="true">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>check</span
>
<span class="label">Show ruler</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Zoom in">
<span class="label">Zoom in</span>
<span class="shortcut">⌘+</span>
</div>
<div ngMenuItem value="Zoom out">
<span class="label">Zoom out</span>
<span class="shortcut">⌘-</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Full screen">
<span class="label">Full screen</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#insertEl
#insertItem="ngMenuItem"
class="menu-bar-item"
value="Insert"
[submenu]="insertMenu()"
>
Insert
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: insertEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #insertMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem #imageEl value="Image" [submenu]="imageMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>image</span
>
<span class="label">Image</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!insertItem.expanded()"
[cdkConnectedOverlay]="{origin: imageEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #imageMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Upload from computer">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>upload</span
>
<span class="label">Upload from computer</span>
</div>
<div ngMenuItem value="Search the web">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>search</span
>
<span class="label">Search the web</span>
</div>
<div ngMenuItem value="By URL">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>link</span
>
<span class="label">By URL</span>
</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem value="Table">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>table_chart</span
>
<span class="label">Table</span>
</div>
<div ngMenuItem #chartEl value="Chart" [submenu]="chartMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>insert_chart</span
>
<span class="label">Chart</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!insertItem.expanded()"
[cdkConnectedOverlay]="{origin: chartEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #chartMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Bar">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>bar_chart</span
>
<span class="label">Bar</span>
</div>
<div ngMenuItem value="Column">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>insert_chart</span
>
<span class="label">Column</span>
</div>
<div ngMenuItem value="Line">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>show_chart</span
>
<span class="label">Line</span>
</div>
<div ngMenuItem value="Pie">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>pie_chart</span
>
<span class="label">Pie</span>
</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem value="Horizontal line">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>horizontal_rule</span
>
<span class="label">Horizontal line</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#formatEl
#formatItem="ngMenuItem"
class="menu-bar-item"
value="Format"
[submenu]="formatMenu()"
>
Format
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: formatEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #formatMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem #textEl #textItem="ngMenuItem" value="Text" [submenu]="textMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_bold</span
>
<span class="label">Text</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!formatItem.expanded()"
[cdkConnectedOverlay]="{origin: textEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #textMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Bold">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_bold</span
>
<span class="label">Bold</span>
<span class="shortcut">⌘B</span>
</div>
<div ngMenuItem value="Italic">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_italic</span
>
<span class="label">Italic</span>
<span class="shortcut">⌘I</span>
</div>
<div ngMenuItem value="Underline">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_underlined</span
>
<span class="label">Underline</span>
<span class="shortcut">⌘U</span>
</div>
<div ngMenuItem value="Strikethrough">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>strikethrough_s</span
>
<span class="label">Strikethrough</span>
<span class="shortcut">⇧⌘X</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem #sizeEl value="Size" [submenu]="sizeMenu()">
<span class="label">Size</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!textItem.expanded()"
[cdkConnectedOverlay]="{origin: sizeEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #sizeMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Increase font size">
<span class="label">Increase font size</span>
<span class="shortcut">⇧⌘.</span>
</div>
<div ngMenuItem value="Decrease font size">
<span class="label">Decrease font size</span>
<span class="shortcut">⇧⌘,</span>
</div>
</ng-template>
</div>
</ng-template>
</ng-template>
</div>
</ng-template>
<div ngMenuItem #paragraphEl value="Paragraph styles" [submenu]="paragraphMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_justify</span
>
<span class="label">Paragraph styles</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!formatItem.expanded()"
[cdkConnectedOverlay]="{origin: paragraphEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #paragraphMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Normal text">Normal text</div>
<div ngMenuItem value="Heading 1">Heading 1</div>
<div ngMenuItem value="Heading 2">Heading 2</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem #alignEl [submenu]="alignMenu()" value="Align & indent">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_indent_increase</span
>
<span class="label">Align & indent</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!formatItem.expanded()"
[cdkConnectedOverlay]="{origin: alignEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #alignMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Align left">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_left</span
>
<span class="label">Align left</span>
</div>
<div ngMenuItem value="Align center">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_center</span
>
<span class="label">Align center</span>
</div>
<div ngMenuItem value="Align right">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_right</span
>
<span class="label">Align right</span>
</div>
<div ngMenuItem value="Justify">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_justify</span
>
<span class="label">Justify</span>
</div>
</ng-template>
</div>
</ng-template>
</ng-template>
</div>
</ng-template>
</div>
app.css
@import url('https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined');
:host {
display: flex;
justify-content: center;
font-family: var(--inter-font);
--border-color: color-mix(in srgb, var(--full-contrast) 20%, var(--page-background));
}
[ngMenuBar] {
display: flex;
gap: 0.25rem;
padding: 0.25rem;
border-radius: 0.5rem;
border: 1px solid var(--border-color);
background-color: var(--page-background);
}
[ngMenu] {
margin: 0;
width: 15rem;
padding: 0.25rem;
border-radius: 0.5rem;
border: 1px solid var(--border-color);
background-color: var(--page-background);
}
[ngMenu][data-visible='false'] {
display: none;
}
[ngMenuItem] {
outline: none;
display: flex;
cursor: pointer;
align-items: center;
gap: 0.5rem;
padding: 0.5rem;
font-size: 0.875rem;
border-radius: 0.25rem;
}
[ngMenuItem]:not([aria-disabled='true']):hover,
[ngMenuItem]:not([aria-disabled='true']):focus,
[ngMenuItem][aria-expanded='true'] {
background: color-mix(in srgb, var(--border-color) 10%, transparent);
}
[ngMenuItem]:focus {
outline: 2px solid var(--vivid-pink);
}
[ngMenuItem] .icon {
opacity: 0.875;
font-size: 1.25rem;
}
[ngMenuItem] .label {
flex: 1;
opacity: 0.875;
font-size: 0.875rem;
}
[ngMenuItem]:not([aria-expanded='true']) .arrow {
opacity: 0.5;
}
[ngMenu] .separator {
border-top: 1px solid var(--border-color);
margin: 0.25rem 0;
opacity: 0.25;
}
app.ts
import {Component, signal, viewChild} from '@angular/core';
import {MenuBar, Menu, MenuContent, MenuItem} from '@angular/aria/menu';
import {OverlayModule} from '@angular/cdk/overlay';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [MenuBar, Menu, MenuContent, MenuItem, OverlayModule],
})
export class App {
fileMenu = viewChild<Menu<string>>('fileMenu');
shareMenu = viewChild<Menu<string>>('shareMenu');
editMenu = viewChild<Menu<string>>('editMenu');
viewMenu = viewChild<Menu<string>>('viewMenu');
insertMenu = viewChild<Menu<string>>('insertMenu');
imageMenu = viewChild<Menu<string>>('imageMenu');
chartMenu = viewChild<Menu<string>>('chartMenu');
formatMenu = viewChild<Menu<string>>('formatMenu');
textMenu = viewChild<Menu<string>>('textMenu');
sizeMenu = viewChild<Menu<string>>('sizeMenu');
paragraphMenu = viewChild<Menu<string>>('paragraphMenu');
alignMenu = viewChild<Menu<string>>('alignMenu');
rendered = signal(false);
onFocusIn() {
this.rendered.set(true);
}
}
app.html
<div ngMenuBar class="material-menubar" (focusin)="onFocusIn()">
<div
ngMenuItem
#fileEl
#fileItem="ngMenuItem"
class="menu-bar-item"
value="File"
[submenu]="fileMenu()"
>
File
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: fileEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #fileMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="New">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>article</span
>
<span class="label">New</span>
<span class="shortcut">⌘N</span>
</div>
<div ngMenuItem value="Open">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>folder</span
>
<span class="label">Open</span>
<span class="shortcut">⌘O</span>
</div>
<div ngMenuItem value="Make a copy">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>file_copy</span
>
<span class="label">Make a copy</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem #shareEl value="Share" [submenu]="shareMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>person_add</span
>
<span class="label">Share</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!fileItem.expanded()"
[cdkConnectedOverlay]="{origin: shareEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #shareMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Share with others">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>person_add</span
>
<span class="label">Share with others</span>
</div>
<div ngMenuItem value="Publish to web">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>public</span
>
<span class="label">Publish to web</span>
</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem value="Download">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>download</span
>
<span class="label">Download</span>
</div>
<div ngMenuItem value="Print">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>print</span
>
<span class="label">Print</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Rename">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">edit</span>
<span class="label">Rename</span>
</div>
<div ngMenuItem value="Delete">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>delete</span
>
<span class="label">Move to trash</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#editEl
#editItem="ngMenuItem"
class="menu-bar-item"
value="Edit"
[submenu]="editMenu()"
>
Edit
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: editEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #editMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Undo">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">undo</span>
<span class="label">Undo</span>
<span class="shortcut">⌘Z</span>
</div>
<div ngMenuItem value="Redo">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">redo</span>
<span class="label">Redo</span>
<span class="shortcut">⌘Y</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Cut">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>content_cut</span
>
<span class="label">Cut</span>
<span class="shortcut">⌘X</span>
</div>
<div ngMenuItem value="Copy">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>content_copy</span
>
<span class="label">Copy</span>
<span class="shortcut">⌘C</span>
</div>
<div ngMenuItem value="Paste">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>content_paste</span
>
<span class="label">Paste</span>
<span class="shortcut">⌘V</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Find and replace">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>find_replace</span
>
<span class="label">Find and replace</span>
<span class="shortcut">⇧⌘H</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#viewEl
#viewItem="ngMenuItem"
class="menu-bar-item"
value="View"
[submenu]="viewMenu()"
>
View
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: viewEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #viewMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Show print layout" [disabled]="true">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>check</span
>
<span class="label">Show print layout</span>
</div>
<div ngMenuItem value="Show ruler" [disabled]="true">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>check</span
>
<span class="label">Show ruler</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Zoom in">
<span class="label">Zoom in</span>
<span class="shortcut">⌘+</span>
</div>
<div ngMenuItem value="Zoom out">
<span class="label">Zoom out</span>
<span class="shortcut">⌘-</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Full screen">
<span class="label">Full screen</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#insertEl
#insertItem="ngMenuItem"
class="menu-bar-item"
value="Insert"
[submenu]="insertMenu()"
>
Insert
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: insertEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #insertMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem #imageEl value="Image" [submenu]="imageMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>image</span
>
<span class="label">Image</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!insertItem.expanded()"
[cdkConnectedOverlay]="{origin: imageEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #imageMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Upload from computer">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>upload</span
>
<span class="label">Upload from computer</span>
</div>
<div ngMenuItem value="Search the web">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>search</span
>
<span class="label">Search the web</span>
</div>
<div ngMenuItem value="By URL">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>link</span
>
<span class="label">By URL</span>
</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem value="Table">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>table_chart</span
>
<span class="label">Table</span>
</div>
<div ngMenuItem #chartEl value="Chart" [submenu]="chartMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>insert_chart</span
>
<span class="label">Chart</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!insertItem.expanded()"
[cdkConnectedOverlay]="{origin: chartEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #chartMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Bar">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>bar_chart</span
>
<span class="label">Bar</span>
</div>
<div ngMenuItem value="Column">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>insert_chart</span
>
<span class="label">Column</span>
</div>
<div ngMenuItem value="Line">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>show_chart</span
>
<span class="label">Line</span>
</div>
<div ngMenuItem value="Pie">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>pie_chart</span
>
<span class="label">Pie</span>
</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem value="Horizontal line">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>horizontal_rule</span
>
<span class="label">Horizontal line</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#formatEl
#formatItem="ngMenuItem"
class="menu-bar-item"
value="Format"
[submenu]="formatMenu()"
>
Format
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: formatEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #formatMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem #textEl #textItem="ngMenuItem" value="Text" [submenu]="textMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_bold</span
>
<span class="label">Text</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!formatItem.expanded()"
[cdkConnectedOverlay]="{origin: textEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #textMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Bold">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_bold</span
>
<span class="label">Bold</span>
<span class="shortcut">⌘B</span>
</div>
<div ngMenuItem value="Italic">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_italic</span
>
<span class="label">Italic</span>
<span class="shortcut">⌘I</span>
</div>
<div ngMenuItem value="Underline">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_underlined</span
>
<span class="label">Underline</span>
<span class="shortcut">⌘U</span>
</div>
<div ngMenuItem value="Strikethrough">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>strikethrough_s</span
>
<span class="label">Strikethrough</span>
<span class="shortcut">⇧⌘X</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem #sizeEl value="Size" [submenu]="sizeMenu()">
<span class="label">Size</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!textItem.expanded()"
[cdkConnectedOverlay]="{origin: sizeEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #sizeMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Increase font size">
<span class="label">Increase font size</span>
<span class="shortcut">⇧⌘.</span>
</div>
<div ngMenuItem value="Decrease font size">
<span class="label">Decrease font size</span>
<span class="shortcut">⇧⌘,</span>
</div>
</ng-template>
</div>
</ng-template>
</ng-template>
</div>
</ng-template>
<div ngMenuItem #paragraphEl value="Paragraph styles" [submenu]="paragraphMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_justify</span
>
<span class="label">Paragraph styles</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!formatItem.expanded()"
[cdkConnectedOverlay]="{origin: paragraphEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #paragraphMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Normal text">Normal text</div>
<div ngMenuItem value="Heading 1">Heading 1</div>
<div ngMenuItem value="Heading 2">Heading 2</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem #alignEl [submenu]="alignMenu()" value="Align & indent">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_indent_increase</span
>
<span class="label">Align & indent</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!formatItem.expanded()"
[cdkConnectedOverlay]="{origin: alignEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #alignMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Align left">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_left</span
>
<span class="label">Align left</span>
</div>
<div ngMenuItem value="Align center">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_center</span
>
<span class="label">Align center</span>
</div>
<div ngMenuItem value="Align right">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_right</span
>
<span class="label">Align right</span>
</div>
<div ngMenuItem value="Justify">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_justify</span
>
<span class="label">Justify</span>
</div>
</ng-template>
</div>
</ng-template>
</ng-template>
</div>
</ng-template>
</div>
app.css
@import url('https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined');
:host {
display: flex;
justify-content: center;
font-family: var(--inter-font);
--border-color: color-mix(in srgb, var(--full-contrast) 20%, var(--page-background));
}
[ngMenuBar] {
display: flex;
gap: 0.25rem;
padding: 0.25rem;
border-radius: 0.5rem;
border: 1px solid var(--border-color);
background-color: var(--page-background);
}
[ngMenu] {
gap: 3px;
width: 15rem;
display: flex;
flex-direction: column;
padding: 0.25rem;
border-radius: 1rem;
background-color: var(--page-background);
box-shadow: 0 1px 2px 1px color-mix(in srgb, var(--primary-contrast) 25%, transparent);
}
[ngMenu][data-visible='false'] {
display: none;
}
[ngMenuItem] {
outline: none;
display: flex;
cursor: pointer;
align-items: center;
gap: 0.5rem;
padding: 0.75rem;
font-size: 0.875rem;
border-radius: 0.75rem;
}
[ngMenuItem]:not([aria-disabled='true']):hover,
[ngMenuItem]:not([aria-disabled='true']):focus,
[ngMenuItem][aria-expanded='true'] {
color: color-mix(in srgb, var(--vivid-pink) 70%, var(--primary-contrast));
background: color-mix(in srgb, var(--vivid-pink) 5%, transparent);
}
[ngMenuItem]:focus {
outline: 2px solid var(--vivid-pink);
}
[ngMenuItem] .icon {
opacity: 0.875;
font-size: 1.25rem;
}
[ngMenuItem] .label {
flex: 1;
opacity: 0.875;
font-size: 0.875rem;
}
[ngMenuItem]:not([aria-expanded='true']) .arrow {
opacity: 0.5;
}
[ngMenu] .separator {
border-top: 1px solid var(--quaternary-contrast);
margin: 0.25rem 0;
opacity: 0.25;
}
app.ts
import {Component, signal, viewChild} from '@angular/core';
import {MenuBar, Menu, MenuContent, MenuItem} from '@angular/aria/menu';
import {OverlayModule} from '@angular/cdk/overlay';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [MenuBar, Menu, MenuContent, MenuItem, OverlayModule],
})
export class App {
fileMenu = viewChild<Menu<string>>('fileMenu');
shareMenu = viewChild<Menu<string>>('shareMenu');
editMenu = viewChild<Menu<string>>('editMenu');
viewMenu = viewChild<Menu<string>>('viewMenu');
insertMenu = viewChild<Menu<string>>('insertMenu');
imageMenu = viewChild<Menu<string>>('imageMenu');
chartMenu = viewChild<Menu<string>>('chartMenu');
formatMenu = viewChild<Menu<string>>('formatMenu');
textMenu = viewChild<Menu<string>>('textMenu');
sizeMenu = viewChild<Menu<string>>('sizeMenu');
paragraphMenu = viewChild<Menu<string>>('paragraphMenu');
alignMenu = viewChild<Menu<string>>('alignMenu');
rendered = signal(false);
onFocusIn() {
this.rendered.set(true);
}
}
app.html
<div ngMenuBar class="retro-menubar" (focusin)="onFocusIn()">
<div
ngMenuItem
#fileEl
#fileItem="ngMenuItem"
class="menu-bar-item"
value="File"
[submenu]="fileMenu()"
>
File
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: fileEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #fileMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="New">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>article</span
>
<span class="label">New</span>
<span class="shortcut">⌘N</span>
</div>
<div ngMenuItem value="Open">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>folder</span
>
<span class="label">Open</span>
<span class="shortcut">⌘O</span>
</div>
<div ngMenuItem value="Make a copy">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>file_copy</span
>
<span class="label">Make a copy</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem #shareEl value="Share" [submenu]="shareMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>person_add</span
>
<span class="label">Share</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!fileItem.expanded()"
[cdkConnectedOverlay]="{origin: shareEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #shareMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Share with others">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>person_add</span
>
<span class="label">Share with others</span>
</div>
<div ngMenuItem value="Publish to web">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>public</span
>
<span class="label">Publish to web</span>
</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem value="Download">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>download</span
>
<span class="label">Download</span>
</div>
<div ngMenuItem value="Print">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>print</span
>
<span class="label">Print</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Rename">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">edit</span>
<span class="label">Rename</span>
</div>
<div ngMenuItem value="Delete">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>delete</span
>
<span class="label">Move to trash</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#editEl
#editItem="ngMenuItem"
class="menu-bar-item"
value="Edit"
[submenu]="editMenu()"
>
Edit
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: editEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #editMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Undo">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">undo</span>
<span class="label">Undo</span>
<span class="shortcut">⌘Z</span>
</div>
<div ngMenuItem value="Redo">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">redo</span>
<span class="label">Redo</span>
<span class="shortcut">⌘Y</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Cut">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>content_cut</span
>
<span class="label">Cut</span>
<span class="shortcut">⌘X</span>
</div>
<div ngMenuItem value="Copy">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>content_copy</span
>
<span class="label">Copy</span>
<span class="shortcut">⌘C</span>
</div>
<div ngMenuItem value="Paste">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>content_paste</span
>
<span class="label">Paste</span>
<span class="shortcut">⌘V</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Find and replace">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>find_replace</span
>
<span class="label">Find and replace</span>
<span class="shortcut">⇧⌘H</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#viewEl
#viewItem="ngMenuItem"
class="menu-bar-item"
value="View"
[submenu]="viewMenu()"
>
View
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: viewEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #viewMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Show print layout" [disabled]="true">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>check</span
>
<span class="label">Show print layout</span>
</div>
<div ngMenuItem value="Show ruler" [disabled]="true">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>check</span
>
<span class="label">Show ruler</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Zoom in">
<span class="label">Zoom in</span>
<span class="shortcut">⌘+</span>
</div>
<div ngMenuItem value="Zoom out">
<span class="label">Zoom out</span>
<span class="shortcut">⌘-</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Full screen">
<span class="label">Full screen</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#insertEl
#insertItem="ngMenuItem"
class="menu-bar-item"
value="Insert"
[submenu]="insertMenu()"
>
Insert
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: insertEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #insertMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem #imageEl value="Image" [submenu]="imageMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>image</span
>
<span class="label">Image</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!insertItem.expanded()"
[cdkConnectedOverlay]="{origin: imageEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #imageMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Upload from computer">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>upload</span
>
<span class="label">Upload from computer</span>
</div>
<div ngMenuItem value="Search the web">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>search</span
>
<span class="label">Search the web</span>
</div>
<div ngMenuItem value="By URL">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>link</span
>
<span class="label">By URL</span>
</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem value="Table">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>table_chart</span
>
<span class="label">Table</span>
</div>
<div ngMenuItem #chartEl value="Chart" [submenu]="chartMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>insert_chart</span
>
<span class="label">Chart</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!insertItem.expanded()"
[cdkConnectedOverlay]="{origin: chartEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #chartMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Bar">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>bar_chart</span
>
<span class="label">Bar</span>
</div>
<div ngMenuItem value="Column">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>insert_chart</span
>
<span class="label">Column</span>
</div>
<div ngMenuItem value="Line">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>show_chart</span
>
<span class="label">Line</span>
</div>
<div ngMenuItem value="Pie">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>pie_chart</span
>
<span class="label">Pie</span>
</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem value="Horizontal line">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>horizontal_rule</span
>
<span class="label">Horizontal line</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#formatEl
#formatItem="ngMenuItem"
class="menu-bar-item"
value="Format"
[submenu]="formatMenu()"
>
Format
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: formatEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #formatMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem #textEl #textItem="ngMenuItem" value="Text" [submenu]="textMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_bold</span
>
<span class="label">Text</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!formatItem.expanded()"
[cdkConnectedOverlay]="{origin: textEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #textMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Bold">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_bold</span
>
<span class="label">Bold</span>
<span class="shortcut">⌘B</span>
</div>
<div ngMenuItem value="Italic">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_italic</span
>
<span class="label">Italic</span>
<span class="shortcut">⌘I</span>
</div>
<div ngMenuItem value="Underline">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_underlined</span
>
<span class="label">Underline</span>
<span class="shortcut">⌘U</span>
</div>
<div ngMenuItem value="Strikethrough">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>strikethrough_s</span
>
<span class="label">Strikethrough</span>
<span class="shortcut">⇧⌘X</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem #sizeEl value="Size" [submenu]="sizeMenu()">
<span class="label">Size</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!textItem.expanded()"
[cdkConnectedOverlay]="{origin: sizeEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #sizeMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Increase font size">
<span class="label">Increase font size</span>
<span class="shortcut">⇧⌘.</span>
</div>
<div ngMenuItem value="Decrease font size">
<span class="label">Decrease font size</span>
<span class="shortcut">⇧⌘,</span>
</div>
</ng-template>
</div>
</ng-template>
</ng-template>
</div>
</ng-template>
<div ngMenuItem #paragraphEl value="Paragraph styles" [submenu]="paragraphMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_justify</span
>
<span class="label">Paragraph styles</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!formatItem.expanded()"
[cdkConnectedOverlay]="{origin: paragraphEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #paragraphMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Normal text">Normal text</div>
<div ngMenuItem value="Heading 1">Heading 1</div>
<div ngMenuItem value="Heading 2">Heading 2</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem #alignEl [submenu]="alignMenu()" value="Align & indent">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_indent_increase</span
>
<span class="label">Align & indent</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!formatItem.expanded()"
[cdkConnectedOverlay]="{origin: alignEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #alignMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Align left">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_left</span
>
<span class="label">Align left</span>
</div>
<div ngMenuItem value="Align center">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_center</span
>
<span class="label">Align center</span>
</div>
<div ngMenuItem value="Align right">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_right</span
>
<span class="label">Align right</span>
</div>
<div ngMenuItem value="Justify">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_justify</span
>
<span class="label">Justify</span>
</div>
</ng-template>
</div>
</ng-template>
</ng-template>
</div>
</ng-template>
</div>
app.css
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap');
@import url('https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined');
:host {
display: flex;
justify-content: center;
font-size: 0.8rem;
--border-color: color-mix(in srgb, var(--full-contrast) 20%, var(--page-background));
font-family: 'Press Start 2P';
--retro-button-color: var(--vivid-pink);
--retro-shadow-light: color-mix(in srgb, #fff 20%, transparent);
--retro-shadow-dark: color-mix(in srgb, #000 20%, transparent);
--retro-elevated-shadow:
inset 4px 4px 0px 0px var(--retro-shadow-light),
inset -4px -4px 0px 0px var(--retro-shadow-dark), 4px 0px 0px 0px var(--tertiary-contrast),
0px 4px 0px 0px var(--tertiary-contrast), -4px 0px 0px 0px var(--tertiary-contrast),
0px -4px 0px 0px var(--tertiary-contrast);
--retro-flat-shadow:
4px 0px 0px 0px var(--tertiary-contrast), 0px 4px 0px 0px var(--tertiary-contrast),
-4px 0px 0px 0px var(--tertiary-contrast), 0px -4px 0px 0px var(--tertiary-contrast);
--retro-clickable-shadow:
inset 4px 4px 0px 0px var(--retro-shadow-light),
inset -4px -4px 0px 0px var(--retro-shadow-dark), 4px 0px 0px 0px var(--tertiary-contrast),
0px 4px 0px 0px var(--tertiary-contrast), -4px 0px 0px 0px var(--tertiary-contrast),
0px -4px 0px 0px var(--tertiary-contrast), 8px 8px 0px 0px var(--tertiary-contrast);
--retro-pressed-shadow:
inset 4px 4px 0px 0px var(--retro-shadow-dark),
inset -4px -4px 0px 0px var(--retro-shadow-light), 4px 0px 0px 0px var(--tertiary-contrast),
0px 4px 0px 0px var(--tertiary-contrast), -4px 0px 0px 0px var(--tertiary-contrast),
0px -4px 0px 0px var(--tertiary-contrast), 0px 0px 0px 0px var(--tertiary-contrast);
}
[ngMenuBar] {
display: flex;
gap: 0.25rem;
padding: 0.25rem;
background-color: var(--page-background);
box-shadow: var(--retro-flat-shadow);
}
[ngMenu] {
margin-top: 8px;
width: 15rem;
padding: 0.25rem;
background-color: var(--page-background);
box-shadow: var(--retro-flat-shadow);
}
[ngMenu][data-visible='false'] {
display: none;
}
[ngMenuItem] {
outline: none;
display: flex;
cursor: pointer;
align-items: center;
gap: 0.5rem;
padding: 0.5rem;
font-size: 0.875rem;
}
[ngMenuItem]:not([aria-disabled='true']):hover,
[ngMenuItem]:not([aria-disabled='true']):focus,
[ngMenuItem][aria-expanded='true'] {
background: color-mix(in srgb, var(--border-color) 10%, transparent);
}
[ngMenuItem]:focus {
outline: 4px dashed var(--vivid-pink);
outline-offset: -4px;
}
[ngMenuItem] .icon {
opacity: 0.875;
font-size: 1.25rem;
}
[ngMenuItem] .label {
flex: 1;
opacity: 0.875;
font-size: 0.875rem;
}
[ngMenuItem]:not([aria-expanded='true']) .arrow {
opacity: 0.5;
}
[ngMenu] .separator {
border-top: 4px solid #000;
margin: 0.25rem 0;
opacity: 0.25;
}
Kullanım
Menü çubukları, uygulama komutlarını kalıcı ve keşfedilebilir navigasyona düzenlemek için iyi çalışır.
Menü çubuğu kullanın:
- Uygulama komut çubukları oluştururken (Dosya, Düzenle, Görüntüle, Ekle, Biçimlendir gibi)
- Arayüz genelinde görünen kalıcı navigasyon oluştururken
- Komutları mantıksal üst düzey kategorilere düzenlerken
- Klavye destekli yatay menü navigasyonuna ihtiyaç duyulduğunda
- Masaüstü tarzı uygulama arayüzleri oluştururken
Menü çubuğundan kaçının:
- Bireysel eylemler için açılır menüler oluştururken (bunun yerine tetikleyicili Menu kullanın)
- Bağlam menüleri oluştururken (Menu rehber kalıbını kullanın)
- Basit bağımsız eylem listeleri için (bunun yerine Menu kullanın)
- Yatay alanın sınırlı olduğu mobil arayüzlerde
- Navigasyon bir kenar çubuğu veya başlık navigasyon kalıbına ait olduğunda
Özellikler
- Yatay navigasyon - Sol/Sağ ok tuşları üst düzey kategoriler arasında hareket eder
- Kalıcı görünürlük - Her zaman görünür, modal veya kapatılabilir değil
- Üzerine gelindiğinde açma - İlk klavye veya tıklama etkileşiminden sonra alt menüler üzerine gelindiğinde açılır
- İç içe alt menüler - Birden fazla menü derinlik seviyesini destekler
- Klavye navigasyonu - Ok tuşları, Enter/Boşluk, Escape ve yazarak arama
- Devre dışı durumlar - Tüm menü çubuğunu veya bireysel öğeleri devre dışı bırakın
- RTL desteği - Otomatik sağdan sola dil navigasyonu
Örnekler
Temel menü çubuğu
Bir menü çubuğu, üst düzey kategorilere düzenlenmiş uygulama komutlarına kalıcı erişim sağlar. Kullanıcılar kategoriler arasında Sol/Sağ ok tuşlarıyla gezinir ve menüleri Enter veya Aşağı ok tuşuyla açar.
app.ts
import {Component, signal, viewChild} from '@angular/core';
import {MenuBar, Menu, MenuContent, MenuItem} from '@angular/aria/menu';
import {OverlayModule} from '@angular/cdk/overlay';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [MenuBar, Menu, MenuContent, MenuItem, OverlayModule],
})
export class App {
fileMenu = viewChild<Menu<string>>('fileMenu');
shareMenu = viewChild<Menu<string>>('shareMenu');
editMenu = viewChild<Menu<string>>('editMenu');
viewMenu = viewChild<Menu<string>>('viewMenu');
insertMenu = viewChild<Menu<string>>('insertMenu');
imageMenu = viewChild<Menu<string>>('imageMenu');
chartMenu = viewChild<Menu<string>>('chartMenu');
formatMenu = viewChild<Menu<string>>('formatMenu');
textMenu = viewChild<Menu<string>>('textMenu');
sizeMenu = viewChild<Menu<string>>('sizeMenu');
paragraphMenu = viewChild<Menu<string>>('paragraphMenu');
alignMenu = viewChild<Menu<string>>('alignMenu');
rendered = signal(false);
onFocusIn() {
this.rendered.set(true);
}
}
app.html
<div ngMenuBar (focusin)="onFocusIn()">
<div
ngMenuItem
#fileEl
#fileItem="ngMenuItem"
class="menu-bar-item"
value="File"
[submenu]="fileMenu()"
>
File
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: fileEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #fileMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="New">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>article</span
>
<span class="label">New</span>
<span class="shortcut">⌘N</span>
</div>
<div ngMenuItem value="Open">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>folder</span
>
<span class="label">Open</span>
<span class="shortcut">⌘O</span>
</div>
<div ngMenuItem value="Make a copy">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>file_copy</span
>
<span class="label">Make a copy</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem #shareEl value="Share" [submenu]="shareMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>person_add</span
>
<span class="label">Share</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!fileItem.expanded()"
[cdkConnectedOverlay]="{origin: shareEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #shareMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Share with others">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>person_add</span
>
<span class="label">Share with others</span>
</div>
<div ngMenuItem value="Publish to web">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>public</span
>
<span class="label">Publish to web</span>
</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem value="Download">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>download</span
>
<span class="label">Download</span>
</div>
<div ngMenuItem value="Print">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>print</span
>
<span class="label">Print</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Rename">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">edit</span>
<span class="label">Rename</span>
</div>
<div ngMenuItem value="Delete">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>delete</span
>
<span class="label">Move to trash</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#editEl
#editItem="ngMenuItem"
class="menu-bar-item"
value="Edit"
[submenu]="editMenu()"
>
Edit
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: editEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #editMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Undo">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">undo</span>
<span class="label">Undo</span>
<span class="shortcut">⌘Z</span>
</div>
<div ngMenuItem value="Redo">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">redo</span>
<span class="label">Redo</span>
<span class="shortcut">⌘Y</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Cut">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>content_cut</span
>
<span class="label">Cut</span>
<span class="shortcut">⌘X</span>
</div>
<div ngMenuItem value="Copy">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>content_copy</span
>
<span class="label">Copy</span>
<span class="shortcut">⌘C</span>
</div>
<div ngMenuItem value="Paste">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>content_paste</span
>
<span class="label">Paste</span>
<span class="shortcut">⌘V</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Find and replace">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>find_replace</span
>
<span class="label">Find and replace</span>
<span class="shortcut">⇧⌘H</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#viewEl
#viewItem="ngMenuItem"
class="menu-bar-item"
value="View"
[submenu]="viewMenu()"
>
View
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: viewEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #viewMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Show print layout" [disabled]="true">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>check</span
>
<span class="label">Show print layout</span>
</div>
<div ngMenuItem value="Show ruler" [disabled]="true">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>check</span
>
<span class="label">Show ruler</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Zoom in">
<span class="label">Zoom in</span>
<span class="shortcut">⌘+</span>
</div>
<div ngMenuItem value="Zoom out">
<span class="label">Zoom out</span>
<span class="shortcut">⌘-</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Full screen">
<span class="label">Full screen</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#insertEl
#insertItem="ngMenuItem"
class="menu-bar-item"
value="Insert"
[submenu]="insertMenu()"
>
Insert
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: insertEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #insertMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem #imageEl value="Image" [submenu]="imageMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>image</span
>
<span class="label">Image</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!insertItem.expanded()"
[cdkConnectedOverlay]="{origin: imageEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #imageMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Upload from computer">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>upload</span
>
<span class="label">Upload from computer</span>
</div>
<div ngMenuItem value="Search the web">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>search</span
>
<span class="label">Search the web</span>
</div>
<div ngMenuItem value="By URL">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>link</span
>
<span class="label">By URL</span>
</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem value="Table">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>table_chart</span
>
<span class="label">Table</span>
</div>
<div ngMenuItem #chartEl value="Chart" [submenu]="chartMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>insert_chart</span
>
<span class="label">Chart</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!insertItem.expanded()"
[cdkConnectedOverlay]="{origin: chartEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #chartMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Bar">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>bar_chart</span
>
<span class="label">Bar</span>
</div>
<div ngMenuItem value="Column">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>insert_chart</span
>
<span class="label">Column</span>
</div>
<div ngMenuItem value="Line">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>show_chart</span
>
<span class="label">Line</span>
</div>
<div ngMenuItem value="Pie">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>pie_chart</span
>
<span class="label">Pie</span>
</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem value="Horizontal line">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>horizontal_rule</span
>
<span class="label">Horizontal line</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#formatEl
#formatItem="ngMenuItem"
class="menu-bar-item"
value="Format"
[submenu]="formatMenu()"
>
Format
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: formatEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #formatMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem #textEl #textItem="ngMenuItem" value="Text" [submenu]="textMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_bold</span
>
<span class="label">Text</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!formatItem.expanded()"
[cdkConnectedOverlay]="{origin: textEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #textMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Bold">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_bold</span
>
<span class="label">Bold</span>
<span class="shortcut">⌘B</span>
</div>
<div ngMenuItem value="Italic">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_italic</span
>
<span class="label">Italic</span>
<span class="shortcut">⌘I</span>
</div>
<div ngMenuItem value="Underline">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_underlined</span
>
<span class="label">Underline</span>
<span class="shortcut">⌘U</span>
</div>
<div ngMenuItem value="Strikethrough">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>strikethrough_s</span
>
<span class="label">Strikethrough</span>
<span class="shortcut">⇧⌘X</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem #sizeEl value="Size" [submenu]="sizeMenu()">
<span class="label">Size</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!textItem.expanded()"
[cdkConnectedOverlay]="{origin: sizeEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #sizeMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Increase font size">
<span class="label">Increase font size</span>
<span class="shortcut">⇧⌘.</span>
</div>
<div ngMenuItem value="Decrease font size">
<span class="label">Decrease font size</span>
<span class="shortcut">⇧⌘,</span>
</div>
</ng-template>
</div>
</ng-template>
</ng-template>
</div>
</ng-template>
<div ngMenuItem #paragraphEl value="Paragraph styles" [submenu]="paragraphMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_justify</span
>
<span class="label">Paragraph styles</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!formatItem.expanded()"
[cdkConnectedOverlay]="{origin: paragraphEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #paragraphMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Normal text">Normal text</div>
<div ngMenuItem value="Heading 1">Heading 1</div>
<div ngMenuItem value="Heading 2">Heading 2</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem #alignEl [submenu]="alignMenu()" value="Align & indent">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_indent_increase</span
>
<span class="label">Align & indent</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!formatItem.expanded()"
[cdkConnectedOverlay]="{origin: alignEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #alignMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Align left">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_left</span
>
<span class="label">Align left</span>
</div>
<div ngMenuItem value="Align center">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_center</span
>
<span class="label">Align center</span>
</div>
<div ngMenuItem value="Align right">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_right</span
>
<span class="label">Align right</span>
</div>
<div ngMenuItem value="Justify">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_justify</span
>
<span class="label">Justify</span>
</div>
</ng-template>
</div>
</ng-template>
</ng-template>
</div>
</ng-template>
</div>
app.css
@import url('https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined');
:host {
display: flex;
justify-content: center;
font-family: var(--inter-font);
--border-color: color-mix(in srgb, var(--full-contrast) 20%, var(--page-background));
}
[ngMenuBar] {
display: flex;
gap: 0.25rem;
padding: 0.25rem;
border-radius: 0.5rem;
border: 1px solid var(--border-color);
background-color: var(--page-background);
}
[ngMenu] {
margin: 0;
width: 15rem;
padding: 0.25rem;
border-radius: 0.5rem;
border: 1px solid var(--border-color);
background-color: var(--page-background);
}
[ngMenu][data-visible='false'] {
display: none;
}
[ngMenuItem] {
outline: none;
display: flex;
cursor: pointer;
align-items: center;
gap: 0.5rem;
padding: 0.5rem;
font-size: 0.875rem;
border-radius: 0.25rem;
}
[ngMenuItem]:not([aria-disabled='true']):hover,
[ngMenuItem]:not([aria-disabled='true']):focus,
[ngMenuItem][aria-expanded='true'] {
background: color-mix(in srgb, var(--border-color) 10%, transparent);
}
[ngMenuItem]:focus {
outline: 2px solid var(--vivid-pink);
}
[ngMenuItem] .icon {
opacity: 0.875;
font-size: 1.25rem;
}
[ngMenuItem] .label {
flex: 1;
opacity: 0.875;
font-size: 0.875rem;
}
[ngMenuItem]:not([aria-expanded='true']) .arrow {
opacity: 0.5;
}
[ngMenu] .separator {
border-top: 1px solid var(--border-color);
margin: 0.25rem 0;
opacity: 0.25;
}
app.ts
import {Component, signal, viewChild} from '@angular/core';
import {MenuBar, Menu, MenuContent, MenuItem} from '@angular/aria/menu';
import {OverlayModule} from '@angular/cdk/overlay';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [MenuBar, Menu, MenuContent, MenuItem, OverlayModule],
})
export class App {
fileMenu = viewChild<Menu<string>>('fileMenu');
shareMenu = viewChild<Menu<string>>('shareMenu');
editMenu = viewChild<Menu<string>>('editMenu');
viewMenu = viewChild<Menu<string>>('viewMenu');
insertMenu = viewChild<Menu<string>>('insertMenu');
imageMenu = viewChild<Menu<string>>('imageMenu');
chartMenu = viewChild<Menu<string>>('chartMenu');
formatMenu = viewChild<Menu<string>>('formatMenu');
textMenu = viewChild<Menu<string>>('textMenu');
sizeMenu = viewChild<Menu<string>>('sizeMenu');
paragraphMenu = viewChild<Menu<string>>('paragraphMenu');
alignMenu = viewChild<Menu<string>>('alignMenu');
rendered = signal(false);
onFocusIn() {
this.rendered.set(true);
}
}
app.html
<div ngMenuBar class="material-menubar" (focusin)="onFocusIn()">
<div
ngMenuItem
#fileEl
#fileItem="ngMenuItem"
class="menu-bar-item"
value="File"
[submenu]="fileMenu()"
>
File
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: fileEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #fileMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="New">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>article</span
>
<span class="label">New</span>
<span class="shortcut">⌘N</span>
</div>
<div ngMenuItem value="Open">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>folder</span
>
<span class="label">Open</span>
<span class="shortcut">⌘O</span>
</div>
<div ngMenuItem value="Make a copy">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>file_copy</span
>
<span class="label">Make a copy</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem #shareEl value="Share" [submenu]="shareMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>person_add</span
>
<span class="label">Share</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!fileItem.expanded()"
[cdkConnectedOverlay]="{origin: shareEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #shareMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Share with others">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>person_add</span
>
<span class="label">Share with others</span>
</div>
<div ngMenuItem value="Publish to web">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>public</span
>
<span class="label">Publish to web</span>
</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem value="Download">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>download</span
>
<span class="label">Download</span>
</div>
<div ngMenuItem value="Print">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>print</span
>
<span class="label">Print</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Rename">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">edit</span>
<span class="label">Rename</span>
</div>
<div ngMenuItem value="Delete">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>delete</span
>
<span class="label">Move to trash</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#editEl
#editItem="ngMenuItem"
class="menu-bar-item"
value="Edit"
[submenu]="editMenu()"
>
Edit
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: editEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #editMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Undo">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">undo</span>
<span class="label">Undo</span>
<span class="shortcut">⌘Z</span>
</div>
<div ngMenuItem value="Redo">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">redo</span>
<span class="label">Redo</span>
<span class="shortcut">⌘Y</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Cut">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>content_cut</span
>
<span class="label">Cut</span>
<span class="shortcut">⌘X</span>
</div>
<div ngMenuItem value="Copy">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>content_copy</span
>
<span class="label">Copy</span>
<span class="shortcut">⌘C</span>
</div>
<div ngMenuItem value="Paste">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>content_paste</span
>
<span class="label">Paste</span>
<span class="shortcut">⌘V</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Find and replace">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>find_replace</span
>
<span class="label">Find and replace</span>
<span class="shortcut">⇧⌘H</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#viewEl
#viewItem="ngMenuItem"
class="menu-bar-item"
value="View"
[submenu]="viewMenu()"
>
View
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: viewEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #viewMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Show print layout" [disabled]="true">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>check</span
>
<span class="label">Show print layout</span>
</div>
<div ngMenuItem value="Show ruler" [disabled]="true">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>check</span
>
<span class="label">Show ruler</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Zoom in">
<span class="label">Zoom in</span>
<span class="shortcut">⌘+</span>
</div>
<div ngMenuItem value="Zoom out">
<span class="label">Zoom out</span>
<span class="shortcut">⌘-</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Full screen">
<span class="label">Full screen</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#insertEl
#insertItem="ngMenuItem"
class="menu-bar-item"
value="Insert"
[submenu]="insertMenu()"
>
Insert
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: insertEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #insertMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem #imageEl value="Image" [submenu]="imageMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>image</span
>
<span class="label">Image</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!insertItem.expanded()"
[cdkConnectedOverlay]="{origin: imageEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #imageMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Upload from computer">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>upload</span
>
<span class="label">Upload from computer</span>
</div>
<div ngMenuItem value="Search the web">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>search</span
>
<span class="label">Search the web</span>
</div>
<div ngMenuItem value="By URL">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>link</span
>
<span class="label">By URL</span>
</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem value="Table">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>table_chart</span
>
<span class="label">Table</span>
</div>
<div ngMenuItem #chartEl value="Chart" [submenu]="chartMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>insert_chart</span
>
<span class="label">Chart</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!insertItem.expanded()"
[cdkConnectedOverlay]="{origin: chartEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #chartMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Bar">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>bar_chart</span
>
<span class="label">Bar</span>
</div>
<div ngMenuItem value="Column">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>insert_chart</span
>
<span class="label">Column</span>
</div>
<div ngMenuItem value="Line">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>show_chart</span
>
<span class="label">Line</span>
</div>
<div ngMenuItem value="Pie">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>pie_chart</span
>
<span class="label">Pie</span>
</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem value="Horizontal line">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>horizontal_rule</span
>
<span class="label">Horizontal line</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#formatEl
#formatItem="ngMenuItem"
class="menu-bar-item"
value="Format"
[submenu]="formatMenu()"
>
Format
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: formatEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #formatMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem #textEl #textItem="ngMenuItem" value="Text" [submenu]="textMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_bold</span
>
<span class="label">Text</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!formatItem.expanded()"
[cdkConnectedOverlay]="{origin: textEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #textMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Bold">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_bold</span
>
<span class="label">Bold</span>
<span class="shortcut">⌘B</span>
</div>
<div ngMenuItem value="Italic">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_italic</span
>
<span class="label">Italic</span>
<span class="shortcut">⌘I</span>
</div>
<div ngMenuItem value="Underline">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_underlined</span
>
<span class="label">Underline</span>
<span class="shortcut">⌘U</span>
</div>
<div ngMenuItem value="Strikethrough">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>strikethrough_s</span
>
<span class="label">Strikethrough</span>
<span class="shortcut">⇧⌘X</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem #sizeEl value="Size" [submenu]="sizeMenu()">
<span class="label">Size</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!textItem.expanded()"
[cdkConnectedOverlay]="{origin: sizeEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #sizeMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Increase font size">
<span class="label">Increase font size</span>
<span class="shortcut">⇧⌘.</span>
</div>
<div ngMenuItem value="Decrease font size">
<span class="label">Decrease font size</span>
<span class="shortcut">⇧⌘,</span>
</div>
</ng-template>
</div>
</ng-template>
</ng-template>
</div>
</ng-template>
<div ngMenuItem #paragraphEl value="Paragraph styles" [submenu]="paragraphMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_justify</span
>
<span class="label">Paragraph styles</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!formatItem.expanded()"
[cdkConnectedOverlay]="{origin: paragraphEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #paragraphMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Normal text">Normal text</div>
<div ngMenuItem value="Heading 1">Heading 1</div>
<div ngMenuItem value="Heading 2">Heading 2</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem #alignEl [submenu]="alignMenu()" value="Align & indent">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_indent_increase</span
>
<span class="label">Align & indent</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!formatItem.expanded()"
[cdkConnectedOverlay]="{origin: alignEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #alignMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Align left">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_left</span
>
<span class="label">Align left</span>
</div>
<div ngMenuItem value="Align center">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_center</span
>
<span class="label">Align center</span>
</div>
<div ngMenuItem value="Align right">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_right</span
>
<span class="label">Align right</span>
</div>
<div ngMenuItem value="Justify">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_justify</span
>
<span class="label">Justify</span>
</div>
</ng-template>
</div>
</ng-template>
</ng-template>
</div>
</ng-template>
</div>
app.css
@import url('https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined');
:host {
display: flex;
justify-content: center;
font-family: var(--inter-font);
--border-color: color-mix(in srgb, var(--full-contrast) 20%, var(--page-background));
}
[ngMenuBar] {
display: flex;
gap: 0.25rem;
padding: 0.25rem;
border-radius: 0.5rem;
border: 1px solid var(--border-color);
background-color: var(--page-background);
}
[ngMenu] {
gap: 3px;
width: 15rem;
display: flex;
flex-direction: column;
padding: 0.25rem;
border-radius: 1rem;
background-color: var(--page-background);
box-shadow: 0 1px 2px 1px color-mix(in srgb, var(--primary-contrast) 25%, transparent);
}
[ngMenu][data-visible='false'] {
display: none;
}
[ngMenuItem] {
outline: none;
display: flex;
cursor: pointer;
align-items: center;
gap: 0.5rem;
padding: 0.75rem;
font-size: 0.875rem;
border-radius: 0.75rem;
}
[ngMenuItem]:not([aria-disabled='true']):hover,
[ngMenuItem]:not([aria-disabled='true']):focus,
[ngMenuItem][aria-expanded='true'] {
color: color-mix(in srgb, var(--vivid-pink) 70%, var(--primary-contrast));
background: color-mix(in srgb, var(--vivid-pink) 5%, transparent);
}
[ngMenuItem]:focus {
outline: 2px solid var(--vivid-pink);
}
[ngMenuItem] .icon {
opacity: 0.875;
font-size: 1.25rem;
}
[ngMenuItem] .label {
flex: 1;
opacity: 0.875;
font-size: 0.875rem;
}
[ngMenuItem]:not([aria-expanded='true']) .arrow {
opacity: 0.5;
}
[ngMenu] .separator {
border-top: 1px solid var(--quaternary-contrast);
margin: 0.25rem 0;
opacity: 0.25;
}
app.ts
import {Component, signal, viewChild} from '@angular/core';
import {MenuBar, Menu, MenuContent, MenuItem} from '@angular/aria/menu';
import {OverlayModule} from '@angular/cdk/overlay';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [MenuBar, Menu, MenuContent, MenuItem, OverlayModule],
})
export class App {
fileMenu = viewChild<Menu<string>>('fileMenu');
shareMenu = viewChild<Menu<string>>('shareMenu');
editMenu = viewChild<Menu<string>>('editMenu');
viewMenu = viewChild<Menu<string>>('viewMenu');
insertMenu = viewChild<Menu<string>>('insertMenu');
imageMenu = viewChild<Menu<string>>('imageMenu');
chartMenu = viewChild<Menu<string>>('chartMenu');
formatMenu = viewChild<Menu<string>>('formatMenu');
textMenu = viewChild<Menu<string>>('textMenu');
sizeMenu = viewChild<Menu<string>>('sizeMenu');
paragraphMenu = viewChild<Menu<string>>('paragraphMenu');
alignMenu = viewChild<Menu<string>>('alignMenu');
rendered = signal(false);
onFocusIn() {
this.rendered.set(true);
}
}
app.html
<div ngMenuBar class="retro-menubar" (focusin)="onFocusIn()">
<div
ngMenuItem
#fileEl
#fileItem="ngMenuItem"
class="menu-bar-item"
value="File"
[submenu]="fileMenu()"
>
File
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: fileEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #fileMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="New">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>article</span
>
<span class="label">New</span>
<span class="shortcut">⌘N</span>
</div>
<div ngMenuItem value="Open">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>folder</span
>
<span class="label">Open</span>
<span class="shortcut">⌘O</span>
</div>
<div ngMenuItem value="Make a copy">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>file_copy</span
>
<span class="label">Make a copy</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem #shareEl value="Share" [submenu]="shareMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>person_add</span
>
<span class="label">Share</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!fileItem.expanded()"
[cdkConnectedOverlay]="{origin: shareEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #shareMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Share with others">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>person_add</span
>
<span class="label">Share with others</span>
</div>
<div ngMenuItem value="Publish to web">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>public</span
>
<span class="label">Publish to web</span>
</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem value="Download">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>download</span
>
<span class="label">Download</span>
</div>
<div ngMenuItem value="Print">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>print</span
>
<span class="label">Print</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Rename">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">edit</span>
<span class="label">Rename</span>
</div>
<div ngMenuItem value="Delete">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>delete</span
>
<span class="label">Move to trash</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#editEl
#editItem="ngMenuItem"
class="menu-bar-item"
value="Edit"
[submenu]="editMenu()"
>
Edit
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: editEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #editMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Undo">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">undo</span>
<span class="label">Undo</span>
<span class="shortcut">⌘Z</span>
</div>
<div ngMenuItem value="Redo">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">redo</span>
<span class="label">Redo</span>
<span class="shortcut">⌘Y</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Cut">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>content_cut</span
>
<span class="label">Cut</span>
<span class="shortcut">⌘X</span>
</div>
<div ngMenuItem value="Copy">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>content_copy</span
>
<span class="label">Copy</span>
<span class="shortcut">⌘C</span>
</div>
<div ngMenuItem value="Paste">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>content_paste</span
>
<span class="label">Paste</span>
<span class="shortcut">⌘V</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Find and replace">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>find_replace</span
>
<span class="label">Find and replace</span>
<span class="shortcut">⇧⌘H</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#viewEl
#viewItem="ngMenuItem"
class="menu-bar-item"
value="View"
[submenu]="viewMenu()"
>
View
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: viewEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #viewMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Show print layout" [disabled]="true">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>check</span
>
<span class="label">Show print layout</span>
</div>
<div ngMenuItem value="Show ruler" [disabled]="true">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>check</span
>
<span class="label">Show ruler</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Zoom in">
<span class="label">Zoom in</span>
<span class="shortcut">⌘+</span>
</div>
<div ngMenuItem value="Zoom out">
<span class="label">Zoom out</span>
<span class="shortcut">⌘-</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Full screen">
<span class="label">Full screen</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#insertEl
#insertItem="ngMenuItem"
class="menu-bar-item"
value="Insert"
[submenu]="insertMenu()"
>
Insert
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: insertEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #insertMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem #imageEl value="Image" [submenu]="imageMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>image</span
>
<span class="label">Image</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!insertItem.expanded()"
[cdkConnectedOverlay]="{origin: imageEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #imageMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Upload from computer">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>upload</span
>
<span class="label">Upload from computer</span>
</div>
<div ngMenuItem value="Search the web">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>search</span
>
<span class="label">Search the web</span>
</div>
<div ngMenuItem value="By URL">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>link</span
>
<span class="label">By URL</span>
</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem value="Table">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>table_chart</span
>
<span class="label">Table</span>
</div>
<div ngMenuItem #chartEl value="Chart" [submenu]="chartMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>insert_chart</span
>
<span class="label">Chart</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!insertItem.expanded()"
[cdkConnectedOverlay]="{origin: chartEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #chartMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Bar">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>bar_chart</span
>
<span class="label">Bar</span>
</div>
<div ngMenuItem value="Column">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>insert_chart</span
>
<span class="label">Column</span>
</div>
<div ngMenuItem value="Line">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>show_chart</span
>
<span class="label">Line</span>
</div>
<div ngMenuItem value="Pie">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>pie_chart</span
>
<span class="label">Pie</span>
</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem value="Horizontal line">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>horizontal_rule</span
>
<span class="label">Horizontal line</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#formatEl
#formatItem="ngMenuItem"
class="menu-bar-item"
value="Format"
[submenu]="formatMenu()"
>
Format
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: formatEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #formatMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem #textEl #textItem="ngMenuItem" value="Text" [submenu]="textMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_bold</span
>
<span class="label">Text</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!formatItem.expanded()"
[cdkConnectedOverlay]="{origin: textEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #textMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Bold">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_bold</span
>
<span class="label">Bold</span>
<span class="shortcut">⌘B</span>
</div>
<div ngMenuItem value="Italic">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_italic</span
>
<span class="label">Italic</span>
<span class="shortcut">⌘I</span>
</div>
<div ngMenuItem value="Underline">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_underlined</span
>
<span class="label">Underline</span>
<span class="shortcut">⌘U</span>
</div>
<div ngMenuItem value="Strikethrough">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>strikethrough_s</span
>
<span class="label">Strikethrough</span>
<span class="shortcut">⇧⌘X</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem #sizeEl value="Size" [submenu]="sizeMenu()">
<span class="label">Size</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!textItem.expanded()"
[cdkConnectedOverlay]="{origin: sizeEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #sizeMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Increase font size">
<span class="label">Increase font size</span>
<span class="shortcut">⇧⌘.</span>
</div>
<div ngMenuItem value="Decrease font size">
<span class="label">Decrease font size</span>
<span class="shortcut">⇧⌘,</span>
</div>
</ng-template>
</div>
</ng-template>
</ng-template>
</div>
</ng-template>
<div ngMenuItem #paragraphEl value="Paragraph styles" [submenu]="paragraphMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_justify</span
>
<span class="label">Paragraph styles</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!formatItem.expanded()"
[cdkConnectedOverlay]="{origin: paragraphEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #paragraphMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Normal text">Normal text</div>
<div ngMenuItem value="Heading 1">Heading 1</div>
<div ngMenuItem value="Heading 2">Heading 2</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem #alignEl [submenu]="alignMenu()" value="Align & indent">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_indent_increase</span
>
<span class="label">Align & indent</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!formatItem.expanded()"
[cdkConnectedOverlay]="{origin: alignEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #alignMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Align left">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_left</span
>
<span class="label">Align left</span>
</div>
<div ngMenuItem value="Align center">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_center</span
>
<span class="label">Align center</span>
</div>
<div ngMenuItem value="Align right">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_right</span
>
<span class="label">Align right</span>
</div>
<div ngMenuItem value="Justify">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_justify</span
>
<span class="label">Justify</span>
</div>
</ng-template>
</div>
</ng-template>
</ng-template>
</div>
</ng-template>
</div>
app.css
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap');
@import url('https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined');
:host {
display: flex;
justify-content: center;
font-size: 0.8rem;
--border-color: color-mix(in srgb, var(--full-contrast) 20%, var(--page-background));
font-family: 'Press Start 2P';
--retro-button-color: var(--vivid-pink);
--retro-shadow-light: color-mix(in srgb, #fff 20%, transparent);
--retro-shadow-dark: color-mix(in srgb, #000 20%, transparent);
--retro-elevated-shadow:
inset 4px 4px 0px 0px var(--retro-shadow-light),
inset -4px -4px 0px 0px var(--retro-shadow-dark), 4px 0px 0px 0px var(--tertiary-contrast),
0px 4px 0px 0px var(--tertiary-contrast), -4px 0px 0px 0px var(--tertiary-contrast),
0px -4px 0px 0px var(--tertiary-contrast);
--retro-flat-shadow:
4px 0px 0px 0px var(--tertiary-contrast), 0px 4px 0px 0px var(--tertiary-contrast),
-4px 0px 0px 0px var(--tertiary-contrast), 0px -4px 0px 0px var(--tertiary-contrast);
--retro-clickable-shadow:
inset 4px 4px 0px 0px var(--retro-shadow-light),
inset -4px -4px 0px 0px var(--retro-shadow-dark), 4px 0px 0px 0px var(--tertiary-contrast),
0px 4px 0px 0px var(--tertiary-contrast), -4px 0px 0px 0px var(--tertiary-contrast),
0px -4px 0px 0px var(--tertiary-contrast), 8px 8px 0px 0px var(--tertiary-contrast);
--retro-pressed-shadow:
inset 4px 4px 0px 0px var(--retro-shadow-dark),
inset -4px -4px 0px 0px var(--retro-shadow-light), 4px 0px 0px 0px var(--tertiary-contrast),
0px 4px 0px 0px var(--tertiary-contrast), -4px 0px 0px 0px var(--tertiary-contrast),
0px -4px 0px 0px var(--tertiary-contrast), 0px 0px 0px 0px var(--tertiary-contrast);
}
[ngMenuBar] {
display: flex;
gap: 0.25rem;
padding: 0.25rem;
background-color: var(--page-background);
box-shadow: var(--retro-flat-shadow);
}
[ngMenu] {
margin-top: 8px;
width: 15rem;
padding: 0.25rem;
background-color: var(--page-background);
box-shadow: var(--retro-flat-shadow);
}
[ngMenu][data-visible='false'] {
display: none;
}
[ngMenuItem] {
outline: none;
display: flex;
cursor: pointer;
align-items: center;
gap: 0.5rem;
padding: 0.5rem;
font-size: 0.875rem;
}
[ngMenuItem]:not([aria-disabled='true']):hover,
[ngMenuItem]:not([aria-disabled='true']):focus,
[ngMenuItem][aria-expanded='true'] {
background: color-mix(in srgb, var(--border-color) 10%, transparent);
}
[ngMenuItem]:focus {
outline: 4px dashed var(--vivid-pink);
outline-offset: -4px;
}
[ngMenuItem] .icon {
opacity: 0.875;
font-size: 1.25rem;
}
[ngMenuItem] .label {
flex: 1;
opacity: 0.875;
font-size: 0.875rem;
}
[ngMenuItem]:not([aria-expanded='true']) .arrow {
opacity: 0.5;
}
[ngMenu] .separator {
border-top: 4px solid #000;
margin: 0.25rem 0;
opacity: 0.25;
}
Dosya, Düzenle ve Görüntüle arasında hareket etmek için Sağ ok tuşuna basın. Bir menüyü açmak ve alt menü öğeleri arasında Yukarı/Aşağı ok tuşlarıyla gezinmek için Enter veya Aşağı ok tuşuna basın.
Devre dışı menü çubuğu öğeleri
Etkileşimi engellemek için belirli menü öğelerini veya tüm menü çubuğunu devre dışı bırakın. Devre dışı öğelerin klavye navigasyonu sırasında klavye odağı alıp alamayacağını softDisabled girişi ile kontrol edin.
app.ts
import {Component, signal, viewChild} from '@angular/core';
import {MenuBar, Menu, MenuContent, MenuItem} from '@angular/aria/menu';
import {OverlayModule} from '@angular/cdk/overlay';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [MenuBar, Menu, MenuContent, MenuItem, OverlayModule],
})
export class App {
fileMenu = viewChild<Menu<string>>('fileMenu');
shareMenu = viewChild<Menu<string>>('shareMenu');
editMenu = viewChild<Menu<string>>('editMenu');
viewMenu = viewChild<Menu<string>>('viewMenu');
insertMenu = viewChild<Menu<string>>('insertMenu');
imageMenu = viewChild<Menu<string>>('imageMenu');
chartMenu = viewChild<Menu<string>>('chartMenu');
formatMenu = viewChild<Menu<string>>('formatMenu');
textMenu = viewChild<Menu<string>>('textMenu');
sizeMenu = viewChild<Menu<string>>('sizeMenu');
paragraphMenu = viewChild<Menu<string>>('paragraphMenu');
alignMenu = viewChild<Menu<string>>('alignMenu');
rendered = signal(false);
onFocusIn() {
this.rendered.set(true);
}
}
app.html
<div ngMenuBar (focusin)="onFocusIn()">
<div
ngMenuItem
#fileEl
#fileItem="ngMenuItem"
class="menu-bar-item"
value="File"
[submenu]="fileMenu()"
>
File
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: fileEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #fileMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="New">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>article</span
>
<span class="label">New</span>
<span class="shortcut">⌘N</span>
</div>
<div ngMenuItem value="Open">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>folder</span
>
<span class="label">Open</span>
<span class="shortcut">⌘O</span>
</div>
<div ngMenuItem value="Make a copy">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>file_copy</span
>
<span class="label">Make a copy</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem #shareEl value="Share" [submenu]="shareMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>person_add</span
>
<span class="label">Share</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!fileItem.expanded()"
[cdkConnectedOverlay]="{origin: shareEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #shareMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Share with others">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>person_add</span
>
<span class="label">Share with others</span>
</div>
<div ngMenuItem value="Publish to web">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>public</span
>
<span class="label">Publish to web</span>
</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem value="Download">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>download</span
>
<span class="label">Download</span>
</div>
<div ngMenuItem value="Print">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>print</span
>
<span class="label">Print</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Rename">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">edit</span>
<span class="label">Rename</span>
</div>
<div ngMenuItem value="Delete">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>delete</span
>
<span class="label">Move to trash</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#editEl
#editItem="ngMenuItem"
class="menu-bar-item"
value="Edit"
[submenu]="editMenu()"
>
Edit
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: editEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #editMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Undo">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">undo</span>
<span class="label">Undo</span>
<span class="shortcut">⌘Z</span>
</div>
<div ngMenuItem value="Redo">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">redo</span>
<span class="label">Redo</span>
<span class="shortcut">⌘Y</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Cut">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>content_cut</span
>
<span class="label">Cut</span>
<span class="shortcut">⌘X</span>
</div>
<div ngMenuItem value="Copy">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>content_copy</span
>
<span class="label">Copy</span>
<span class="shortcut">⌘C</span>
</div>
<div ngMenuItem value="Paste">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>content_paste</span
>
<span class="label">Paste</span>
<span class="shortcut">⌘V</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Find and replace">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>find_replace</span
>
<span class="label">Find and replace</span>
<span class="shortcut">⇧⌘H</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#viewEl
#viewItem="ngMenuItem"
class="menu-bar-item"
value="View"
[submenu]="viewMenu()"
>
View
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: viewEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #viewMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Show print layout" [disabled]="true">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>check</span
>
<span class="label">Show print layout</span>
</div>
<div ngMenuItem value="Show ruler" [disabled]="true">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>check</span
>
<span class="label">Show ruler</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Zoom in">
<span class="label">Zoom in</span>
<span class="shortcut">⌘+</span>
</div>
<div ngMenuItem value="Zoom out">
<span class="label">Zoom out</span>
<span class="shortcut">⌘-</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Full screen">
<span class="label">Full screen</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#insertEl
#insertItem="ngMenuItem"
class="menu-bar-item"
value="Insert"
[submenu]="insertMenu()"
>
Insert
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: insertEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #insertMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem #imageEl value="Image" [submenu]="imageMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>image</span
>
<span class="label">Image</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!insertItem.expanded()"
[cdkConnectedOverlay]="{origin: imageEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #imageMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Upload from computer">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>upload</span
>
<span class="label">Upload from computer</span>
</div>
<div ngMenuItem value="Search the web">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>search</span
>
<span class="label">Search the web</span>
</div>
<div ngMenuItem value="By URL">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>link</span
>
<span class="label">By URL</span>
</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem value="Table">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>table_chart</span
>
<span class="label">Table</span>
</div>
<div ngMenuItem #chartEl value="Chart" [submenu]="chartMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>insert_chart</span
>
<span class="label">Chart</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!insertItem.expanded()"
[cdkConnectedOverlay]="{origin: chartEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #chartMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Bar">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>bar_chart</span
>
<span class="label">Bar</span>
</div>
<div ngMenuItem value="Column">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>insert_chart</span
>
<span class="label">Column</span>
</div>
<div ngMenuItem value="Line">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>show_chart</span
>
<span class="label">Line</span>
</div>
<div ngMenuItem value="Pie">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>pie_chart</span
>
<span class="label">Pie</span>
</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem value="Horizontal line">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>horizontal_rule</span
>
<span class="label">Horizontal line</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#formatEl
#formatItem="ngMenuItem"
class="menu-bar-item"
value="Format"
[submenu]="formatMenu()"
>
Format
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: formatEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #formatMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem #textEl #textItem="ngMenuItem" value="Text" [submenu]="textMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_bold</span
>
<span class="label">Text</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!formatItem.expanded()"
[cdkConnectedOverlay]="{origin: textEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #textMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Bold">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_bold</span
>
<span class="label">Bold</span>
<span class="shortcut">⌘B</span>
</div>
<div ngMenuItem value="Italic">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_italic</span
>
<span class="label">Italic</span>
<span class="shortcut">⌘I</span>
</div>
<div ngMenuItem value="Underline">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_underlined</span
>
<span class="label">Underline</span>
<span class="shortcut">⌘U</span>
</div>
<div ngMenuItem value="Strikethrough">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>strikethrough_s</span
>
<span class="label">Strikethrough</span>
<span class="shortcut">⇧⌘X</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem #sizeEl value="Size" [submenu]="sizeMenu()">
<span class="label">Size</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!textItem.expanded()"
[cdkConnectedOverlay]="{origin: sizeEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #sizeMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Increase font size">
<span class="label">Increase font size</span>
<span class="shortcut">⇧⌘.</span>
</div>
<div ngMenuItem value="Decrease font size">
<span class="label">Decrease font size</span>
<span class="shortcut">⇧⌘,</span>
</div>
</ng-template>
</div>
</ng-template>
</ng-template>
</div>
</ng-template>
<div ngMenuItem #paragraphEl value="Paragraph styles" [submenu]="paragraphMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_justify</span
>
<span class="label">Paragraph styles</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!formatItem.expanded()"
[cdkConnectedOverlay]="{origin: paragraphEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #paragraphMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Normal text">Normal text</div>
<div ngMenuItem value="Heading 1">Heading 1</div>
<div ngMenuItem value="Heading 2">Heading 2</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem #alignEl [submenu]="alignMenu()" value="Align & indent">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_indent_increase</span
>
<span class="label">Align & indent</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!formatItem.expanded()"
[cdkConnectedOverlay]="{origin: alignEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #alignMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Align left">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_left</span
>
<span class="label">Align left</span>
</div>
<div ngMenuItem value="Align center">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_center</span
>
<span class="label">Align center</span>
</div>
<div ngMenuItem value="Align right">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_right</span
>
<span class="label">Align right</span>
</div>
<div ngMenuItem value="Justify">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_justify</span
>
<span class="label">Justify</span>
</div>
</ng-template>
</div>
</ng-template>
</ng-template>
</div>
</ng-template>
</div>
app.css
@import url('https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined');
:host {
display: flex;
justify-content: center;
font-family: var(--inter-font);
--border-color: color-mix(in srgb, var(--full-contrast) 20%, var(--page-background));
}
[ngMenuBar] {
display: flex;
gap: 0.25rem;
padding: 0.25rem;
border-radius: 0.5rem;
border: 1px solid var(--border-color);
background-color: var(--page-background);
}
[ngMenu] {
margin: 0;
width: 15rem;
padding: 0.25rem;
border-radius: 0.5rem;
border: 1px solid var(--border-color);
background-color: var(--page-background);
}
[ngMenu][data-visible='false'] {
display: none;
}
[ngMenuItem] {
outline: none;
display: flex;
cursor: pointer;
align-items: center;
gap: 0.5rem;
padding: 0.5rem;
font-size: 0.875rem;
border-radius: 0.25rem;
}
[ngMenuItem]:not([aria-disabled='true']):hover,
[ngMenuItem]:not([aria-disabled='true']):focus,
[ngMenuItem][aria-expanded='true'] {
background: color-mix(in srgb, var(--border-color) 10%, transparent);
}
[ngMenuItem]:focus {
outline: 2px solid var(--vivid-pink);
}
[ngMenuItem] .icon {
opacity: 0.875;
font-size: 1.25rem;
}
[ngMenuItem] .label {
flex: 1;
opacity: 0.875;
font-size: 0.875rem;
}
[ngMenuItem]:not([aria-expanded='true']) .arrow {
opacity: 0.5;
}
[ngMenu] .separator {
border-top: 1px solid var(--border-color);
margin: 0.25rem 0;
opacity: 0.25;
}
app.ts
import {Component, signal, viewChild} from '@angular/core';
import {MenuBar, Menu, MenuContent, MenuItem} from '@angular/aria/menu';
import {OverlayModule} from '@angular/cdk/overlay';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [MenuBar, Menu, MenuContent, MenuItem, OverlayModule],
})
export class App {
fileMenu = viewChild<Menu<string>>('fileMenu');
shareMenu = viewChild<Menu<string>>('shareMenu');
editMenu = viewChild<Menu<string>>('editMenu');
viewMenu = viewChild<Menu<string>>('viewMenu');
insertMenu = viewChild<Menu<string>>('insertMenu');
imageMenu = viewChild<Menu<string>>('imageMenu');
chartMenu = viewChild<Menu<string>>('chartMenu');
formatMenu = viewChild<Menu<string>>('formatMenu');
textMenu = viewChild<Menu<string>>('textMenu');
sizeMenu = viewChild<Menu<string>>('sizeMenu');
paragraphMenu = viewChild<Menu<string>>('paragraphMenu');
alignMenu = viewChild<Menu<string>>('alignMenu');
rendered = signal(false);
onFocusIn() {
this.rendered.set(true);
}
}
app.html
<div ngMenuBar class="material-menubar" (focusin)="onFocusIn()">
<div
ngMenuItem
#fileEl
#fileItem="ngMenuItem"
class="menu-bar-item"
value="File"
[submenu]="fileMenu()"
>
File
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: fileEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #fileMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="New">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>article</span
>
<span class="label">New</span>
<span class="shortcut">⌘N</span>
</div>
<div ngMenuItem value="Open">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>folder</span
>
<span class="label">Open</span>
<span class="shortcut">⌘O</span>
</div>
<div ngMenuItem value="Make a copy">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>file_copy</span
>
<span class="label">Make a copy</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem #shareEl value="Share" [submenu]="shareMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>person_add</span
>
<span class="label">Share</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!fileItem.expanded()"
[cdkConnectedOverlay]="{origin: shareEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #shareMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Share with others">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>person_add</span
>
<span class="label">Share with others</span>
</div>
<div ngMenuItem value="Publish to web">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>public</span
>
<span class="label">Publish to web</span>
</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem value="Download">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>download</span
>
<span class="label">Download</span>
</div>
<div ngMenuItem value="Print">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>print</span
>
<span class="label">Print</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Rename">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">edit</span>
<span class="label">Rename</span>
</div>
<div ngMenuItem value="Delete">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>delete</span
>
<span class="label">Move to trash</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#editEl
#editItem="ngMenuItem"
class="menu-bar-item"
value="Edit"
[submenu]="editMenu()"
>
Edit
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: editEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #editMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Undo">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">undo</span>
<span class="label">Undo</span>
<span class="shortcut">⌘Z</span>
</div>
<div ngMenuItem value="Redo">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">redo</span>
<span class="label">Redo</span>
<span class="shortcut">⌘Y</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Cut">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>content_cut</span
>
<span class="label">Cut</span>
<span class="shortcut">⌘X</span>
</div>
<div ngMenuItem value="Copy">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>content_copy</span
>
<span class="label">Copy</span>
<span class="shortcut">⌘C</span>
</div>
<div ngMenuItem value="Paste">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>content_paste</span
>
<span class="label">Paste</span>
<span class="shortcut">⌘V</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Find and replace">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>find_replace</span
>
<span class="label">Find and replace</span>
<span class="shortcut">⇧⌘H</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#viewEl
#viewItem="ngMenuItem"
class="menu-bar-item"
value="View"
[submenu]="viewMenu()"
>
View
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: viewEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #viewMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Show print layout" [disabled]="true">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>check</span
>
<span class="label">Show print layout</span>
</div>
<div ngMenuItem value="Show ruler" [disabled]="true">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>check</span
>
<span class="label">Show ruler</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Zoom in">
<span class="label">Zoom in</span>
<span class="shortcut">⌘+</span>
</div>
<div ngMenuItem value="Zoom out">
<span class="label">Zoom out</span>
<span class="shortcut">⌘-</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Full screen">
<span class="label">Full screen</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#insertEl
#insertItem="ngMenuItem"
class="menu-bar-item"
value="Insert"
[submenu]="insertMenu()"
>
Insert
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: insertEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #insertMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem #imageEl value="Image" [submenu]="imageMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>image</span
>
<span class="label">Image</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!insertItem.expanded()"
[cdkConnectedOverlay]="{origin: imageEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #imageMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Upload from computer">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>upload</span
>
<span class="label">Upload from computer</span>
</div>
<div ngMenuItem value="Search the web">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>search</span
>
<span class="label">Search the web</span>
</div>
<div ngMenuItem value="By URL">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>link</span
>
<span class="label">By URL</span>
</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem value="Table">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>table_chart</span
>
<span class="label">Table</span>
</div>
<div ngMenuItem #chartEl value="Chart" [submenu]="chartMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>insert_chart</span
>
<span class="label">Chart</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!insertItem.expanded()"
[cdkConnectedOverlay]="{origin: chartEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #chartMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Bar">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>bar_chart</span
>
<span class="label">Bar</span>
</div>
<div ngMenuItem value="Column">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>insert_chart</span
>
<span class="label">Column</span>
</div>
<div ngMenuItem value="Line">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>show_chart</span
>
<span class="label">Line</span>
</div>
<div ngMenuItem value="Pie">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>pie_chart</span
>
<span class="label">Pie</span>
</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem value="Horizontal line">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>horizontal_rule</span
>
<span class="label">Horizontal line</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#formatEl
#formatItem="ngMenuItem"
class="menu-bar-item"
value="Format"
[submenu]="formatMenu()"
>
Format
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: formatEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #formatMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem #textEl #textItem="ngMenuItem" value="Text" [submenu]="textMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_bold</span
>
<span class="label">Text</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!formatItem.expanded()"
[cdkConnectedOverlay]="{origin: textEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #textMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Bold">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_bold</span
>
<span class="label">Bold</span>
<span class="shortcut">⌘B</span>
</div>
<div ngMenuItem value="Italic">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_italic</span
>
<span class="label">Italic</span>
<span class="shortcut">⌘I</span>
</div>
<div ngMenuItem value="Underline">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_underlined</span
>
<span class="label">Underline</span>
<span class="shortcut">⌘U</span>
</div>
<div ngMenuItem value="Strikethrough">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>strikethrough_s</span
>
<span class="label">Strikethrough</span>
<span class="shortcut">⇧⌘X</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem #sizeEl value="Size" [submenu]="sizeMenu()">
<span class="label">Size</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!textItem.expanded()"
[cdkConnectedOverlay]="{origin: sizeEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #sizeMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Increase font size">
<span class="label">Increase font size</span>
<span class="shortcut">⇧⌘.</span>
</div>
<div ngMenuItem value="Decrease font size">
<span class="label">Decrease font size</span>
<span class="shortcut">⇧⌘,</span>
</div>
</ng-template>
</div>
</ng-template>
</ng-template>
</div>
</ng-template>
<div ngMenuItem #paragraphEl value="Paragraph styles" [submenu]="paragraphMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_justify</span
>
<span class="label">Paragraph styles</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!formatItem.expanded()"
[cdkConnectedOverlay]="{origin: paragraphEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #paragraphMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Normal text">Normal text</div>
<div ngMenuItem value="Heading 1">Heading 1</div>
<div ngMenuItem value="Heading 2">Heading 2</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem #alignEl [submenu]="alignMenu()" value="Align & indent">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_indent_increase</span
>
<span class="label">Align & indent</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!formatItem.expanded()"
[cdkConnectedOverlay]="{origin: alignEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #alignMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Align left">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_left</span
>
<span class="label">Align left</span>
</div>
<div ngMenuItem value="Align center">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_center</span
>
<span class="label">Align center</span>
</div>
<div ngMenuItem value="Align right">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_right</span
>
<span class="label">Align right</span>
</div>
<div ngMenuItem value="Justify">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_justify</span
>
<span class="label">Justify</span>
</div>
</ng-template>
</div>
</ng-template>
</ng-template>
</div>
</ng-template>
</div>
app.css
@import url('https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined');
:host {
display: flex;
justify-content: center;
font-family: var(--inter-font);
--border-color: color-mix(in srgb, var(--full-contrast) 20%, var(--page-background));
}
[ngMenuBar] {
display: flex;
gap: 0.25rem;
padding: 0.25rem;
border-radius: 0.5rem;
border: 1px solid var(--border-color);
background-color: var(--page-background);
}
[ngMenu] {
gap: 3px;
width: 15rem;
display: flex;
flex-direction: column;
padding: 0.25rem;
border-radius: 1rem;
background-color: var(--page-background);
box-shadow: 0 1px 2px 1px color-mix(in srgb, var(--primary-contrast) 25%, transparent);
}
[ngMenu][data-visible='false'] {
display: none;
}
[ngMenuItem] {
outline: none;
display: flex;
cursor: pointer;
align-items: center;
gap: 0.5rem;
padding: 0.75rem;
font-size: 0.875rem;
border-radius: 0.75rem;
}
[ngMenuItem]:not([aria-disabled='true']):hover,
[ngMenuItem]:not([aria-disabled='true']):focus,
[ngMenuItem][aria-expanded='true'] {
color: color-mix(in srgb, var(--vivid-pink) 70%, var(--primary-contrast));
background: color-mix(in srgb, var(--vivid-pink) 5%, transparent);
}
[ngMenuItem]:focus {
outline: 2px solid var(--vivid-pink);
}
[ngMenuItem] .icon {
opacity: 0.875;
font-size: 1.25rem;
}
[ngMenuItem] .label {
flex: 1;
opacity: 0.875;
font-size: 0.875rem;
}
[ngMenuItem]:not([aria-expanded='true']) .arrow {
opacity: 0.5;
}
[ngMenu] .separator {
border-top: 1px solid var(--quaternary-contrast);
margin: 0.25rem 0;
opacity: 0.25;
}
app.ts
import {Component, signal, viewChild} from '@angular/core';
import {MenuBar, Menu, MenuContent, MenuItem} from '@angular/aria/menu';
import {OverlayModule} from '@angular/cdk/overlay';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [MenuBar, Menu, MenuContent, MenuItem, OverlayModule],
})
export class App {
fileMenu = viewChild<Menu<string>>('fileMenu');
shareMenu = viewChild<Menu<string>>('shareMenu');
editMenu = viewChild<Menu<string>>('editMenu');
viewMenu = viewChild<Menu<string>>('viewMenu');
insertMenu = viewChild<Menu<string>>('insertMenu');
imageMenu = viewChild<Menu<string>>('imageMenu');
chartMenu = viewChild<Menu<string>>('chartMenu');
formatMenu = viewChild<Menu<string>>('formatMenu');
textMenu = viewChild<Menu<string>>('textMenu');
sizeMenu = viewChild<Menu<string>>('sizeMenu');
paragraphMenu = viewChild<Menu<string>>('paragraphMenu');
alignMenu = viewChild<Menu<string>>('alignMenu');
rendered = signal(false);
onFocusIn() {
this.rendered.set(true);
}
}
app.html
<div ngMenuBar class="retro-menubar" (focusin)="onFocusIn()">
<div
ngMenuItem
#fileEl
#fileItem="ngMenuItem"
class="menu-bar-item"
value="File"
[submenu]="fileMenu()"
>
File
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: fileEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #fileMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="New">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>article</span
>
<span class="label">New</span>
<span class="shortcut">⌘N</span>
</div>
<div ngMenuItem value="Open">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>folder</span
>
<span class="label">Open</span>
<span class="shortcut">⌘O</span>
</div>
<div ngMenuItem value="Make a copy">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>file_copy</span
>
<span class="label">Make a copy</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem #shareEl value="Share" [submenu]="shareMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>person_add</span
>
<span class="label">Share</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!fileItem.expanded()"
[cdkConnectedOverlay]="{origin: shareEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #shareMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Share with others">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>person_add</span
>
<span class="label">Share with others</span>
</div>
<div ngMenuItem value="Publish to web">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>public</span
>
<span class="label">Publish to web</span>
</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem value="Download">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>download</span
>
<span class="label">Download</span>
</div>
<div ngMenuItem value="Print">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>print</span
>
<span class="label">Print</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Rename">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">edit</span>
<span class="label">Rename</span>
</div>
<div ngMenuItem value="Delete">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>delete</span
>
<span class="label">Move to trash</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#editEl
#editItem="ngMenuItem"
class="menu-bar-item"
value="Edit"
[submenu]="editMenu()"
>
Edit
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: editEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #editMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Undo">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">undo</span>
<span class="label">Undo</span>
<span class="shortcut">⌘Z</span>
</div>
<div ngMenuItem value="Redo">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">redo</span>
<span class="label">Redo</span>
<span class="shortcut">⌘Y</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Cut">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>content_cut</span
>
<span class="label">Cut</span>
<span class="shortcut">⌘X</span>
</div>
<div ngMenuItem value="Copy">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>content_copy</span
>
<span class="label">Copy</span>
<span class="shortcut">⌘C</span>
</div>
<div ngMenuItem value="Paste">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>content_paste</span
>
<span class="label">Paste</span>
<span class="shortcut">⌘V</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Find and replace">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>find_replace</span
>
<span class="label">Find and replace</span>
<span class="shortcut">⇧⌘H</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#viewEl
#viewItem="ngMenuItem"
class="menu-bar-item"
value="View"
[submenu]="viewMenu()"
>
View
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: viewEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #viewMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Show print layout" [disabled]="true">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>check</span
>
<span class="label">Show print layout</span>
</div>
<div ngMenuItem value="Show ruler" [disabled]="true">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>check</span
>
<span class="label">Show ruler</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Zoom in">
<span class="label">Zoom in</span>
<span class="shortcut">⌘+</span>
</div>
<div ngMenuItem value="Zoom out">
<span class="label">Zoom out</span>
<span class="shortcut">⌘-</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Full screen">
<span class="label">Full screen</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#insertEl
#insertItem="ngMenuItem"
class="menu-bar-item"
value="Insert"
[submenu]="insertMenu()"
>
Insert
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: insertEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #insertMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem #imageEl value="Image" [submenu]="imageMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>image</span
>
<span class="label">Image</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!insertItem.expanded()"
[cdkConnectedOverlay]="{origin: imageEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #imageMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Upload from computer">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>upload</span
>
<span class="label">Upload from computer</span>
</div>
<div ngMenuItem value="Search the web">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>search</span
>
<span class="label">Search the web</span>
</div>
<div ngMenuItem value="By URL">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>link</span
>
<span class="label">By URL</span>
</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem value="Table">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>table_chart</span
>
<span class="label">Table</span>
</div>
<div ngMenuItem #chartEl value="Chart" [submenu]="chartMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>insert_chart</span
>
<span class="label">Chart</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!insertItem.expanded()"
[cdkConnectedOverlay]="{origin: chartEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #chartMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Bar">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>bar_chart</span
>
<span class="label">Bar</span>
</div>
<div ngMenuItem value="Column">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>insert_chart</span
>
<span class="label">Column</span>
</div>
<div ngMenuItem value="Line">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>show_chart</span
>
<span class="label">Line</span>
</div>
<div ngMenuItem value="Pie">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>pie_chart</span
>
<span class="label">Pie</span>
</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem value="Horizontal line">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>horizontal_rule</span
>
<span class="label">Horizontal line</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#formatEl
#formatItem="ngMenuItem"
class="menu-bar-item"
value="Format"
[submenu]="formatMenu()"
>
Format
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: formatEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #formatMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem #textEl #textItem="ngMenuItem" value="Text" [submenu]="textMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_bold</span
>
<span class="label">Text</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!formatItem.expanded()"
[cdkConnectedOverlay]="{origin: textEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #textMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Bold">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_bold</span
>
<span class="label">Bold</span>
<span class="shortcut">⌘B</span>
</div>
<div ngMenuItem value="Italic">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_italic</span
>
<span class="label">Italic</span>
<span class="shortcut">⌘I</span>
</div>
<div ngMenuItem value="Underline">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_underlined</span
>
<span class="label">Underline</span>
<span class="shortcut">⌘U</span>
</div>
<div ngMenuItem value="Strikethrough">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>strikethrough_s</span
>
<span class="label">Strikethrough</span>
<span class="shortcut">⇧⌘X</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem #sizeEl value="Size" [submenu]="sizeMenu()">
<span class="label">Size</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!textItem.expanded()"
[cdkConnectedOverlay]="{origin: sizeEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #sizeMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Increase font size">
<span class="label">Increase font size</span>
<span class="shortcut">⇧⌘.</span>
</div>
<div ngMenuItem value="Decrease font size">
<span class="label">Decrease font size</span>
<span class="shortcut">⇧⌘,</span>
</div>
</ng-template>
</div>
</ng-template>
</ng-template>
</div>
</ng-template>
<div ngMenuItem #paragraphEl value="Paragraph styles" [submenu]="paragraphMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_justify</span
>
<span class="label">Paragraph styles</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!formatItem.expanded()"
[cdkConnectedOverlay]="{origin: paragraphEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #paragraphMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Normal text">Normal text</div>
<div ngMenuItem value="Heading 1">Heading 1</div>
<div ngMenuItem value="Heading 2">Heading 2</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem #alignEl [submenu]="alignMenu()" value="Align & indent">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_indent_increase</span
>
<span class="label">Align & indent</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!formatItem.expanded()"
[cdkConnectedOverlay]="{origin: alignEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #alignMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Align left">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_left</span
>
<span class="label">Align left</span>
</div>
<div ngMenuItem value="Align center">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_center</span
>
<span class="label">Align center</span>
</div>
<div ngMenuItem value="Align right">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_right</span
>
<span class="label">Align right</span>
</div>
<div ngMenuItem value="Justify">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_justify</span
>
<span class="label">Justify</span>
</div>
</ng-template>
</div>
</ng-template>
</ng-template>
</div>
</ng-template>
</div>
app.css
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap');
@import url('https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined');
:host {
display: flex;
justify-content: center;
font-size: 0.8rem;
--border-color: color-mix(in srgb, var(--full-contrast) 20%, var(--page-background));
font-family: 'Press Start 2P';
--retro-button-color: var(--vivid-pink);
--retro-shadow-light: color-mix(in srgb, #fff 20%, transparent);
--retro-shadow-dark: color-mix(in srgb, #000 20%, transparent);
--retro-elevated-shadow:
inset 4px 4px 0px 0px var(--retro-shadow-light),
inset -4px -4px 0px 0px var(--retro-shadow-dark), 4px 0px 0px 0px var(--tertiary-contrast),
0px 4px 0px 0px var(--tertiary-contrast), -4px 0px 0px 0px var(--tertiary-contrast),
0px -4px 0px 0px var(--tertiary-contrast);
--retro-flat-shadow:
4px 0px 0px 0px var(--tertiary-contrast), 0px 4px 0px 0px var(--tertiary-contrast),
-4px 0px 0px 0px var(--tertiary-contrast), 0px -4px 0px 0px var(--tertiary-contrast);
--retro-clickable-shadow:
inset 4px 4px 0px 0px var(--retro-shadow-light),
inset -4px -4px 0px 0px var(--retro-shadow-dark), 4px 0px 0px 0px var(--tertiary-contrast),
0px 4px 0px 0px var(--tertiary-contrast), -4px 0px 0px 0px var(--tertiary-contrast),
0px -4px 0px 0px var(--tertiary-contrast), 8px 8px 0px 0px var(--tertiary-contrast);
--retro-pressed-shadow:
inset 4px 4px 0px 0px var(--retro-shadow-dark),
inset -4px -4px 0px 0px var(--retro-shadow-light), 4px 0px 0px 0px var(--tertiary-contrast),
0px 4px 0px 0px var(--tertiary-contrast), -4px 0px 0px 0px var(--tertiary-contrast),
0px -4px 0px 0px var(--tertiary-contrast), 0px 0px 0px 0px var(--tertiary-contrast);
}
[ngMenuBar] {
display: flex;
gap: 0.25rem;
padding: 0.25rem;
background-color: var(--page-background);
box-shadow: var(--retro-flat-shadow);
}
[ngMenu] {
margin-top: 8px;
width: 15rem;
padding: 0.25rem;
background-color: var(--page-background);
box-shadow: var(--retro-flat-shadow);
}
[ngMenu][data-visible='false'] {
display: none;
}
[ngMenuItem] {
outline: none;
display: flex;
cursor: pointer;
align-items: center;
gap: 0.5rem;
padding: 0.5rem;
font-size: 0.875rem;
}
[ngMenuItem]:not([aria-disabled='true']):hover,
[ngMenuItem]:not([aria-disabled='true']):focus,
[ngMenuItem][aria-expanded='true'] {
background: color-mix(in srgb, var(--border-color) 10%, transparent);
}
[ngMenuItem]:focus {
outline: 4px dashed var(--vivid-pink);
outline-offset: -4px;
}
[ngMenuItem] .icon {
opacity: 0.875;
font-size: 1.25rem;
}
[ngMenuItem] .label {
flex: 1;
opacity: 0.875;
font-size: 0.875rem;
}
[ngMenuItem]:not([aria-expanded='true']) .arrow {
opacity: 0.5;
}
[ngMenu] .separator {
border-top: 4px solid #000;
margin: 0.25rem 0;
opacity: 0.25;
}
Menü çubuğunda [softDisabled]="true" olduğunda, devre dışı öğeler odak alabilir ancak etkinleştirilemez. [softDisabled]="false" olduğunda, devre dışı öğeler klavye navigasyonu sırasında atlanır.
RTL desteği
Menü çubukları sağdan sola (RTL) dillere otomatik olarak uyum sağlar. Ok tuşu navigasyonu yönünü tersine çevirir ve alt menüler sol tarafa konumlanır.
app.ts
import {Component, signal, viewChild} from '@angular/core';
import {MenuBar, Menu, MenuContent, MenuItem} from '@angular/aria/menu';
import {OverlayModule} from '@angular/cdk/overlay';
import {Dir} from '@angular/cdk/bidi';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [Dir, MenuBar, Menu, MenuContent, MenuItem, OverlayModule],
})
export class App {
fileMenu = viewChild<Menu<string>>('fileMenu');
shareMenu = viewChild<Menu<string>>('shareMenu');
editMenu = viewChild<Menu<string>>('editMenu');
viewMenu = viewChild<Menu<string>>('viewMenu');
insertMenu = viewChild<Menu<string>>('insertMenu');
imageMenu = viewChild<Menu<string>>('imageMenu');
chartMenu = viewChild<Menu<string>>('chartMenu');
formatMenu = viewChild<Menu<string>>('formatMenu');
textMenu = viewChild<Menu<string>>('textMenu');
sizeMenu = viewChild<Menu<string>>('sizeMenu');
paragraphMenu = viewChild<Menu<string>>('paragraphMenu');
alignMenu = viewChild<Menu<string>>('alignMenu');
rendered = signal(false);
onFocusIn() {
this.rendered.set(true);
}
}
app.html
<div ngMenuBar dir="rtl" (focusin)="onFocusIn()">
<div
ngMenuItem
#fileEl
#fileItem="ngMenuItem"
class="menu-bar-item"
value="File"
[submenu]="fileMenu()"
>
File
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: fileEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #fileMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="New">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>article</span
>
<span class="label">New</span>
<span class="shortcut">⌘N</span>
</div>
<div ngMenuItem value="Open">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>folder</span
>
<span class="label">Open</span>
<span class="shortcut">⌘O</span>
</div>
<div ngMenuItem value="Make a copy">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>file_copy</span
>
<span class="label">Make a copy</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem #shareEl value="Share" [submenu]="shareMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>person_add</span
>
<span class="label">Share</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_left</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!fileItem.expanded()"
[cdkConnectedOverlay]="{origin: shareEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: -6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #shareMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Share with others">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>person_add</span
>
<span class="label">Share with others</span>
</div>
<div ngMenuItem value="Publish to web">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>public</span
>
<span class="label">Publish to web</span>
</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem value="Download">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>download</span
>
<span class="label">Download</span>
</div>
<div ngMenuItem value="Print">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>print</span
>
<span class="label">Print</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Rename">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">edit</span>
<span class="label">Rename</span>
</div>
<div ngMenuItem value="Delete">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>delete</span
>
<span class="label">Move to trash</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#editEl
#editItem="ngMenuItem"
class="menu-bar-item"
value="Edit"
[submenu]="editMenu()"
>
Edit
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: editEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #editMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Undo">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">undo</span>
<span class="label">Undo</span>
<span class="shortcut">⌘Z</span>
</div>
<div ngMenuItem value="Redo">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">redo</span>
<span class="label">Redo</span>
<span class="shortcut">⌘Y</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Cut">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>content_cut</span
>
<span class="label">Cut</span>
<span class="shortcut">⌘X</span>
</div>
<div ngMenuItem value="Copy">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>content_copy</span
>
<span class="label">Copy</span>
<span class="shortcut">⌘C</span>
</div>
<div ngMenuItem value="Paste">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>content_paste</span
>
<span class="label">Paste</span>
<span class="shortcut">⌘V</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Find and replace">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>find_replace</span
>
<span class="label">Find and replace</span>
<span class="shortcut">⇧⌘H</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#viewEl
#viewItem="ngMenuItem"
class="menu-bar-item"
value="View"
[submenu]="viewMenu()"
>
View
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: viewEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #viewMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Show print layout" [disabled]="true">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>check</span
>
<span class="label">Show print layout</span>
</div>
<div ngMenuItem value="Show ruler" [disabled]="true">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>check</span
>
<span class="label">Show ruler</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Zoom in">
<span class="label">Zoom in</span>
<span class="shortcut">⌘+</span>
</div>
<div ngMenuItem value="Zoom out">
<span class="label">Zoom out</span>
<span class="shortcut">⌘-</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Full screen">
<span class="label">Full screen</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#insertEl
#insertItem="ngMenuItem"
class="menu-bar-item"
value="Insert"
[submenu]="insertMenu()"
>
Insert
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: insertEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #insertMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem #imageEl value="Image" [submenu]="imageMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>image</span
>
<span class="label">Image</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_left</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!insertItem.expanded()"
[cdkConnectedOverlay]="{origin: imageEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: -6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #imageMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Upload from computer">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>upload</span
>
<span class="label">Upload from computer</span>
</div>
<div ngMenuItem value="Search the web">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>search</span
>
<span class="label">Search the web</span>
</div>
<div ngMenuItem value="By URL">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>link</span
>
<span class="label">By URL</span>
</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem value="Table">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>table_chart</span
>
<span class="label">Table</span>
</div>
<div ngMenuItem #chartEl value="Chart" [submenu]="chartMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>insert_chart</span
>
<span class="label">Chart</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_left</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!insertItem.expanded()"
[cdkConnectedOverlay]="{origin: chartEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: -6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #chartMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Bar">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>bar_chart</span
>
<span class="label">Bar</span>
</div>
<div ngMenuItem value="Column">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>insert_chart</span
>
<span class="label">Column</span>
</div>
<div ngMenuItem value="Line">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>show_chart</span
>
<span class="label">Line</span>
</div>
<div ngMenuItem value="Pie">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>pie_chart</span
>
<span class="label">Pie</span>
</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem value="Horizontal line">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>horizontal_rule</span
>
<span class="label">Horizontal line</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#formatEl
#formatItem="ngMenuItem"
class="menu-bar-item"
value="Format"
[submenu]="formatMenu()"
>
Format
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: formatEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #formatMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem #textEl #textItem="ngMenuItem" value="Text" [submenu]="textMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_bold</span
>
<span class="label">Text</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_left</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!formatItem.expanded()"
[cdkConnectedOverlay]="{origin: textEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: -6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #textMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Bold">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_bold</span
>
<span class="label">Bold</span>
<span class="shortcut">⌘B</span>
</div>
<div ngMenuItem value="Italic">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_italic</span
>
<span class="label">Italic</span>
<span class="shortcut">⌘I</span>
</div>
<div ngMenuItem value="Underline">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_underlined</span
>
<span class="label">Underline</span>
<span class="shortcut">⌘U</span>
</div>
<div ngMenuItem value="Strikethrough">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>strikethrough_s</span
>
<span class="label">Strikethrough</span>
<span class="shortcut">⇧⌘X</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem #sizeEl value="Size" [submenu]="sizeMenu()">
<span class="label">Size</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_left</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!textItem.expanded()"
[cdkConnectedOverlay]="{origin: sizeEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: -6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #sizeMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Increase font size">
<span class="label">Increase font size</span>
<span class="shortcut">⇧⌘.</span>
</div>
<div ngMenuItem value="Decrease font size">
<span class="label">Decrease font size</span>
<span class="shortcut">⇧⌘,</span>
</div>
</ng-template>
</div>
</ng-template>
</ng-template>
</div>
</ng-template>
<div ngMenuItem #paragraphEl value="Paragraph styles" [submenu]="paragraphMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_justify</span
>
<span class="label">Paragraph styles</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_left</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!formatItem.expanded()"
[cdkConnectedOverlay]="{origin: paragraphEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: -6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #paragraphMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Normal text">Normal text</div>
<div ngMenuItem value="Heading 1">Heading 1</div>
<div ngMenuItem value="Heading 2">Heading 2</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem #alignEl [submenu]="alignMenu()" value="Align & indent">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_indent_increase</span
>
<span class="label">Align & indent</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_left</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!formatItem.expanded()"
[cdkConnectedOverlay]="{origin: alignEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: -6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #alignMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Align left">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_left</span
>
<span class="label">Align left</span>
</div>
<div ngMenuItem value="Align center">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_center</span
>
<span class="label">Align center</span>
</div>
<div ngMenuItem value="Align right">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_right</span
>
<span class="label">Align right</span>
</div>
<div ngMenuItem value="Justify">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_justify</span
>
<span class="label">Justify</span>
</div>
</ng-template>
</div>
</ng-template>
</ng-template>
</div>
</ng-template>
</div>
app.css
@import url('https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined');
:host {
display: flex;
justify-content: center;
font-family: var(--inter-font);
--border-color: color-mix(in srgb, var(--full-contrast) 20%, var(--page-background));
}
[ngMenuBar] {
display: flex;
gap: 0.25rem;
padding: 0.25rem;
border-radius: 0.5rem;
border: 1px solid var(--border-color);
background-color: var(--page-background);
}
[ngMenu] {
margin: 0;
width: 15rem;
padding: 0.25rem;
border-radius: 0.5rem;
border: 1px solid var(--border-color);
background-color: var(--page-background);
}
[ngMenu][data-visible='false'] {
display: none;
}
[ngMenuItem] {
outline: none;
display: flex;
cursor: pointer;
align-items: center;
gap: 0.5rem;
padding: 0.5rem;
font-size: 0.875rem;
border-radius: 0.25rem;
}
[ngMenuItem]:not([aria-disabled='true']):hover,
[ngMenuItem]:not([aria-disabled='true']):focus,
[ngMenuItem][aria-expanded='true'] {
background: color-mix(in srgb, var(--border-color) 10%, transparent);
}
[ngMenuItem]:focus {
outline: 2px solid var(--vivid-pink);
}
[ngMenuItem] .icon {
opacity: 0.875;
font-size: 1.25rem;
}
[ngMenuItem] .label {
flex: 1;
opacity: 0.875;
font-size: 0.875rem;
}
[ngMenuItem]:not([aria-expanded='true']) .arrow {
opacity: 0.5;
}
[ngMenu] .separator {
border-top: 1px solid var(--border-color);
margin: 0.25rem 0;
opacity: 0.25;
}
app.ts
import {Component, signal, viewChild} from '@angular/core';
import {MenuBar, Menu, MenuContent, MenuItem} from '@angular/aria/menu';
import {OverlayModule} from '@angular/cdk/overlay';
import {Dir} from '@angular/cdk/bidi';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [Dir, MenuBar, Menu, MenuContent, MenuItem, OverlayModule],
})
export class App {
fileMenu = viewChild<Menu<string>>('fileMenu');
shareMenu = viewChild<Menu<string>>('shareMenu');
editMenu = viewChild<Menu<string>>('editMenu');
viewMenu = viewChild<Menu<string>>('viewMenu');
insertMenu = viewChild<Menu<string>>('insertMenu');
imageMenu = viewChild<Menu<string>>('imageMenu');
chartMenu = viewChild<Menu<string>>('chartMenu');
formatMenu = viewChild<Menu<string>>('formatMenu');
textMenu = viewChild<Menu<string>>('textMenu');
sizeMenu = viewChild<Menu<string>>('sizeMenu');
paragraphMenu = viewChild<Menu<string>>('paragraphMenu');
alignMenu = viewChild<Menu<string>>('alignMenu');
rendered = signal(false);
onFocusIn() {
this.rendered.set(true);
}
}
app.html
<div ngMenuBar class="material-menubar" dir="rtl" (focusin)="onFocusIn()">
<div
ngMenuItem
#fileEl
#fileItem="ngMenuItem"
class="menu-bar-item"
value="File"
[submenu]="fileMenu()"
>
File
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: fileEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #fileMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="New">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>article</span
>
<span class="label">New</span>
<span class="shortcut">⌘N</span>
</div>
<div ngMenuItem value="Open">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>folder</span
>
<span class="label">Open</span>
<span class="shortcut">⌘O</span>
</div>
<div ngMenuItem value="Make a copy">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>file_copy</span
>
<span class="label">Make a copy</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem #shareEl value="Share" [submenu]="shareMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>person_add</span
>
<span class="label">Share</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_left</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!fileItem.expanded()"
[cdkConnectedOverlay]="{origin: shareEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: -6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #shareMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Share with others">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>person_add</span
>
<span class="label">Share with others</span>
</div>
<div ngMenuItem value="Publish to web">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>public</span
>
<span class="label">Publish to web</span>
</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem value="Download">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>download</span
>
<span class="label">Download</span>
</div>
<div ngMenuItem value="Print">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>print</span
>
<span class="label">Print</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Rename">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">edit</span>
<span class="label">Rename</span>
</div>
<div ngMenuItem value="Delete">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>delete</span
>
<span class="label">Move to trash</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#editEl
#editItem="ngMenuItem"
class="menu-bar-item"
value="Edit"
[submenu]="editMenu()"
>
Edit
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: editEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #editMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Undo">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">undo</span>
<span class="label">Undo</span>
<span class="shortcut">⌘Z</span>
</div>
<div ngMenuItem value="Redo">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">redo</span>
<span class="label">Redo</span>
<span class="shortcut">⌘Y</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Cut">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>content_cut</span
>
<span class="label">Cut</span>
<span class="shortcut">⌘X</span>
</div>
<div ngMenuItem value="Copy">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>content_copy</span
>
<span class="label">Copy</span>
<span class="shortcut">⌘C</span>
</div>
<div ngMenuItem value="Paste">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>content_paste</span
>
<span class="label">Paste</span>
<span class="shortcut">⌘V</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Find and replace">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>find_replace</span
>
<span class="label">Find and replace</span>
<span class="shortcut">⇧⌘H</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#viewEl
#viewItem="ngMenuItem"
class="menu-bar-item"
value="View"
[submenu]="viewMenu()"
>
View
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: viewEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #viewMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Show print layout" [disabled]="true">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>check</span
>
<span class="label">Show print layout</span>
</div>
<div ngMenuItem value="Show ruler" [disabled]="true">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>check</span
>
<span class="label">Show ruler</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Zoom in">
<span class="label">Zoom in</span>
<span class="shortcut">⌘+</span>
</div>
<div ngMenuItem value="Zoom out">
<span class="label">Zoom out</span>
<span class="shortcut">⌘-</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Full screen">
<span class="label">Full screen</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#insertEl
#insertItem="ngMenuItem"
class="menu-bar-item"
value="Insert"
[submenu]="insertMenu()"
>
Insert
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: insertEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #insertMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem #imageEl value="Image" [submenu]="imageMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>image</span
>
<span class="label">Image</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_left</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!insertItem.expanded()"
[cdkConnectedOverlay]="{origin: imageEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: -6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #imageMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Upload from computer">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>upload</span
>
<span class="label">Upload from computer</span>
</div>
<div ngMenuItem value="Search the web">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>search</span
>
<span class="label">Search the web</span>
</div>
<div ngMenuItem value="By URL">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>link</span
>
<span class="label">By URL</span>
</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem value="Table">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>table_chart</span
>
<span class="label">Table</span>
</div>
<div ngMenuItem #chartEl value="Chart" [submenu]="chartMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>insert_chart</span
>
<span class="label">Chart</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_left</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!insertItem.expanded()"
[cdkConnectedOverlay]="{origin: chartEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: -6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #chartMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Bar">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>bar_chart</span
>
<span class="label">Bar</span>
</div>
<div ngMenuItem value="Column">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>insert_chart</span
>
<span class="label">Column</span>
</div>
<div ngMenuItem value="Line">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>show_chart</span
>
<span class="label">Line</span>
</div>
<div ngMenuItem value="Pie">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>pie_chart</span
>
<span class="label">Pie</span>
</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem value="Horizontal line">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>horizontal_rule</span
>
<span class="label">Horizontal line</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#formatEl
#formatItem="ngMenuItem"
class="menu-bar-item"
value="Format"
[submenu]="formatMenu()"
>
Format
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: formatEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #formatMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem #textEl #textItem="ngMenuItem" value="Text" [submenu]="textMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_bold</span
>
<span class="label">Text</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_left</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!formatItem.expanded()"
[cdkConnectedOverlay]="{origin: textEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: -6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #textMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Bold">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_bold</span
>
<span class="label">Bold</span>
<span class="shortcut">⌘B</span>
</div>
<div ngMenuItem value="Italic">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_italic</span
>
<span class="label">Italic</span>
<span class="shortcut">⌘I</span>
</div>
<div ngMenuItem value="Underline">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_underlined</span
>
<span class="label">Underline</span>
<span class="shortcut">⌘U</span>
</div>
<div ngMenuItem value="Strikethrough">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>strikethrough_s</span
>
<span class="label">Strikethrough</span>
<span class="shortcut">⇧⌘X</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem #sizeEl value="Size" [submenu]="sizeMenu()">
<span class="label">Size</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_left</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!textItem.expanded()"
[cdkConnectedOverlay]="{origin: sizeEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: -6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #sizeMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Increase font size">
<span class="label">Increase font size</span>
<span class="shortcut">⇧⌘.</span>
</div>
<div ngMenuItem value="Decrease font size">
<span class="label">Decrease font size</span>
<span class="shortcut">⇧⌘,</span>
</div>
</ng-template>
</div>
</ng-template>
</ng-template>
</div>
</ng-template>
<div ngMenuItem #paragraphEl value="Paragraph styles" [submenu]="paragraphMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_justify</span
>
<span class="label">Paragraph styles</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_left</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!formatItem.expanded()"
[cdkConnectedOverlay]="{origin: paragraphEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: -6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #paragraphMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Normal text">Normal text</div>
<div ngMenuItem value="Heading 1">Heading 1</div>
<div ngMenuItem value="Heading 2">Heading 2</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem #alignEl [submenu]="alignMenu()" value="Align & indent">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_indent_increase</span
>
<span class="label">Align & indent</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_left</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!formatItem.expanded()"
[cdkConnectedOverlay]="{origin: alignEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: -6},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #alignMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Align left">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_left</span
>
<span class="label">Align left</span>
</div>
<div ngMenuItem value="Align center">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_center</span
>
<span class="label">Align center</span>
</div>
<div ngMenuItem value="Align right">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_right</span
>
<span class="label">Align right</span>
</div>
<div ngMenuItem value="Justify">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_justify</span
>
<span class="label">Justify</span>
</div>
</ng-template>
</div>
</ng-template>
</ng-template>
</div>
</ng-template>
</div>
app.css
@import url('https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined');
:host {
display: flex;
justify-content: center;
font-family: var(--inter-font);
--border-color: color-mix(in srgb, var(--full-contrast) 20%, var(--page-background));
}
[ngMenuBar] {
display: flex;
gap: 0.25rem;
padding: 0.25rem;
border-radius: 0.5rem;
border: 1px solid var(--border-color);
background-color: var(--page-background);
}
[ngMenu] {
gap: 3px;
width: 15rem;
display: flex;
flex-direction: column;
padding: 0.25rem;
border-radius: 1rem;
background-color: var(--page-background);
box-shadow: 0 1px 2px 1px color-mix(in srgb, var(--primary-contrast) 25%, transparent);
}
[ngMenu][data-visible='false'] {
display: none;
}
[ngMenuItem] {
outline: none;
display: flex;
cursor: pointer;
align-items: center;
gap: 0.5rem;
padding: 0.75rem;
font-size: 0.875rem;
border-radius: 0.75rem;
}
[ngMenuItem]:not([aria-disabled='true']):hover,
[ngMenuItem]:not([aria-disabled='true']):focus,
[ngMenuItem][aria-expanded='true'] {
color: color-mix(in srgb, var(--vivid-pink) 70%, var(--primary-contrast));
background: color-mix(in srgb, var(--vivid-pink) 5%, transparent);
}
[ngMenuItem]:focus {
outline: 2px solid var(--vivid-pink);
}
[ngMenuItem] .icon {
opacity: 0.875;
font-size: 1.25rem;
}
[ngMenuItem] .label {
flex: 1;
opacity: 0.875;
font-size: 0.875rem;
}
[ngMenuItem]:not([aria-expanded='true']) .arrow {
opacity: 0.5;
}
[ngMenu] .separator {
border-top: 1px solid var(--quaternary-contrast);
margin: 0.25rem 0;
opacity: 0.25;
}
app.ts
import {Component, signal, viewChild} from '@angular/core';
import {MenuBar, Menu, MenuContent, MenuItem} from '@angular/aria/menu';
import {OverlayModule} from '@angular/cdk/overlay';
import {Dir} from '@angular/cdk/bidi';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [Dir, MenuBar, Menu, MenuContent, MenuItem, OverlayModule],
})
export class App {
fileMenu = viewChild<Menu<string>>('fileMenu');
shareMenu = viewChild<Menu<string>>('shareMenu');
editMenu = viewChild<Menu<string>>('editMenu');
viewMenu = viewChild<Menu<string>>('viewMenu');
insertMenu = viewChild<Menu<string>>('insertMenu');
imageMenu = viewChild<Menu<string>>('imageMenu');
chartMenu = viewChild<Menu<string>>('chartMenu');
formatMenu = viewChild<Menu<string>>('formatMenu');
textMenu = viewChild<Menu<string>>('textMenu');
sizeMenu = viewChild<Menu<string>>('sizeMenu');
paragraphMenu = viewChild<Menu<string>>('paragraphMenu');
alignMenu = viewChild<Menu<string>>('alignMenu');
rendered = signal(false);
onFocusIn() {
this.rendered.set(true);
}
}
app.html
<div ngMenuBar class="retro-menubar" dir="rtl" (focusin)="onFocusIn()">
<div
ngMenuItem
#fileEl
#fileItem="ngMenuItem"
class="menu-bar-item"
value="File"
[submenu]="fileMenu()"
>
File
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: fileEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #fileMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="New">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>article</span
>
<span class="label">New</span>
<span class="shortcut">⌘N</span>
</div>
<div ngMenuItem value="Open">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>folder</span
>
<span class="label">Open</span>
<span class="shortcut">⌘O</span>
</div>
<div ngMenuItem value="Make a copy">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>file_copy</span
>
<span class="label">Make a copy</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem #shareEl value="Share" [submenu]="shareMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>person_add</span
>
<span class="label">Share</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_left</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!fileItem.expanded()"
[cdkConnectedOverlay]="{origin: shareEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: -12},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #shareMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Share with others">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>person_add</span
>
<span class="label">Share with others</span>
</div>
<div ngMenuItem value="Publish to web">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>public</span
>
<span class="label">Publish to web</span>
</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem value="Download">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>download</span
>
<span class="label">Download</span>
</div>
<div ngMenuItem value="Print">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>print</span
>
<span class="label">Print</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Rename">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">edit</span>
<span class="label">Rename</span>
</div>
<div ngMenuItem value="Delete">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>delete</span
>
<span class="label">Move to trash</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#editEl
#editItem="ngMenuItem"
class="menu-bar-item"
value="Edit"
[submenu]="editMenu()"
>
Edit
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: editEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #editMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Undo">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">undo</span>
<span class="label">Undo</span>
<span class="shortcut">⌘Z</span>
</div>
<div ngMenuItem value="Redo">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">redo</span>
<span class="label">Redo</span>
<span class="shortcut">⌘Y</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Cut">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>content_cut</span
>
<span class="label">Cut</span>
<span class="shortcut">⌘X</span>
</div>
<div ngMenuItem value="Copy">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>content_copy</span
>
<span class="label">Copy</span>
<span class="shortcut">⌘C</span>
</div>
<div ngMenuItem value="Paste">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>content_paste</span
>
<span class="label">Paste</span>
<span class="shortcut">⌘V</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Find and replace">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>find_replace</span
>
<span class="label">Find and replace</span>
<span class="shortcut">⇧⌘H</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#viewEl
#viewItem="ngMenuItem"
class="menu-bar-item"
value="View"
[submenu]="viewMenu()"
>
View
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: viewEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #viewMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Show print layout" [disabled]="true">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>check</span
>
<span class="label">Show print layout</span>
</div>
<div ngMenuItem value="Show ruler" [disabled]="true">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>check</span
>
<span class="label">Show ruler</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Zoom in">
<span class="label">Zoom in</span>
<span class="shortcut">⌘+</span>
</div>
<div ngMenuItem value="Zoom out">
<span class="label">Zoom out</span>
<span class="shortcut">⌘-</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem value="Full screen">
<span class="label">Full screen</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#insertEl
#insertItem="ngMenuItem"
class="menu-bar-item"
value="Insert"
[submenu]="insertMenu()"
>
Insert
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: insertEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #insertMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem #imageEl value="Image" [submenu]="imageMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>image</span
>
<span class="label">Image</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_left</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!insertItem.expanded()"
[cdkConnectedOverlay]="{origin: imageEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: -12},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #imageMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Upload from computer">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>upload</span
>
<span class="label">Upload from computer</span>
</div>
<div ngMenuItem value="Search the web">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>search</span
>
<span class="label">Search the web</span>
</div>
<div ngMenuItem value="By URL">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>link</span
>
<span class="label">By URL</span>
</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem value="Table">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>table_chart</span
>
<span class="label">Table</span>
</div>
<div ngMenuItem #chartEl value="Chart" [submenu]="chartMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>insert_chart</span
>
<span class="label">Chart</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_left</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!insertItem.expanded()"
[cdkConnectedOverlay]="{origin: chartEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: -12},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #chartMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Bar">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>bar_chart</span
>
<span class="label">Bar</span>
</div>
<div ngMenuItem value="Column">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>insert_chart</span
>
<span class="label">Column</span>
</div>
<div ngMenuItem value="Line">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>show_chart</span
>
<span class="label">Line</span>
</div>
<div ngMenuItem value="Pie">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>pie_chart</span
>
<span class="label">Pie</span>
</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem value="Horizontal line">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>horizontal_rule</span
>
<span class="label">Horizontal line</span>
</div>
</ng-template>
</div>
</ng-template>
<div
ngMenuItem
#formatEl
#formatItem="ngMenuItem"
class="menu-bar-item"
value="Format"
[submenu]="formatMenu()"
>
Format
</div>
<ng-template
[cdkConnectedOverlayOpen]="rendered()"
[cdkConnectedOverlay]="{origin: formatEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #formatMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem #textEl #textItem="ngMenuItem" value="Text" [submenu]="textMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_bold</span
>
<span class="label">Text</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_left</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!formatItem.expanded()"
[cdkConnectedOverlay]="{origin: textEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: -12},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #textMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Bold">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_bold</span
>
<span class="label">Bold</span>
<span class="shortcut">⌘B</span>
</div>
<div ngMenuItem value="Italic">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_italic</span
>
<span class="label">Italic</span>
<span class="shortcut">⌘I</span>
</div>
<div ngMenuItem value="Underline">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_underlined</span
>
<span class="label">Underline</span>
<span class="shortcut">⌘U</span>
</div>
<div ngMenuItem value="Strikethrough">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>strikethrough_s</span
>
<span class="label">Strikethrough</span>
<span class="shortcut">⇧⌘X</span>
</div>
<div role="separator" class="example-menu-item-separator"></div>
<div ngMenuItem #sizeEl value="Size" [submenu]="sizeMenu()">
<span class="label">Size</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_left</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!textItem.expanded()"
[cdkConnectedOverlay]="{origin: sizeEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{
originX: 'end',
originY: 'top',
overlayY: 'top',
overlayX: 'start',
offsetX: -12,
},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #sizeMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Increase font size">
<span class="label">Increase font size</span>
<span class="shortcut">⇧⌘.</span>
</div>
<div ngMenuItem value="Decrease font size">
<span class="label">Decrease font size</span>
<span class="shortcut">⇧⌘,</span>
</div>
</ng-template>
</div>
</ng-template>
</ng-template>
</div>
</ng-template>
<div ngMenuItem #paragraphEl value="Paragraph styles" [submenu]="paragraphMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_justify</span
>
<span class="label">Paragraph styles</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_left</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!formatItem.expanded()"
[cdkConnectedOverlay]="{origin: paragraphEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: -12},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #paragraphMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Normal text">Normal text</div>
<div ngMenuItem value="Heading 1">Heading 1</div>
<div ngMenuItem value="Heading 2">Heading 2</div>
</ng-template>
</div>
</ng-template>
<div ngMenuItem #alignEl [submenu]="alignMenu()" value="Align & indent">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_indent_increase</span
>
<span class="label">Align & indent</span>
<span class="icon arrow material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_left</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="!!formatItem.expanded()"
[cdkConnectedOverlay]="{origin: alignEl, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: -12},
]"
[cdkConnectedOverlayDisableClose]="true"
cdkAttachPopoverAsChild
>
<div ngMenu #alignMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Align left">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_left</span
>
<span class="label">Align left</span>
</div>
<div ngMenuItem value="Align center">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_center</span
>
<span class="label">Align center</span>
</div>
<div ngMenuItem value="Align right">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_right</span
>
<span class="label">Align right</span>
</div>
<div ngMenuItem value="Justify">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>format_align_justify</span
>
<span class="label">Justify</span>
</div>
</ng-template>
</div>
</ng-template>
</ng-template>
</div>
</ng-template>
</div>
app.css
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap');
@import url('https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined');
:host {
display: flex;
justify-content: center;
font-size: 0.8rem;
--border-color: color-mix(in srgb, var(--full-contrast) 20%, var(--page-background));
font-family: 'Press Start 2P';
--retro-button-color: var(--vivid-pink);
--retro-shadow-light: color-mix(in srgb, #fff 20%, transparent);
--retro-shadow-dark: color-mix(in srgb, #000 20%, transparent);
--retro-elevated-shadow:
inset 4px 4px 0px 0px var(--retro-shadow-light),
inset -4px -4px 0px 0px var(--retro-shadow-dark), 4px 0px 0px 0px var(--tertiary-contrast),
0px 4px 0px 0px var(--tertiary-contrast), -4px 0px 0px 0px var(--tertiary-contrast),
0px -4px 0px 0px var(--tertiary-contrast);
--retro-flat-shadow:
4px 0px 0px 0px var(--tertiary-contrast), 0px 4px 0px 0px var(--tertiary-contrast),
-4px 0px 0px 0px var(--tertiary-contrast), 0px -4px 0px 0px var(--tertiary-contrast);
--retro-clickable-shadow:
inset 4px 4px 0px 0px var(--retro-shadow-light),
inset -4px -4px 0px 0px var(--retro-shadow-dark), 4px 0px 0px 0px var(--tertiary-contrast),
0px 4px 0px 0px var(--tertiary-contrast), -4px 0px 0px 0px var(--tertiary-contrast),
0px -4px 0px 0px var(--tertiary-contrast), 8px 8px 0px 0px var(--tertiary-contrast);
--retro-pressed-shadow:
inset 4px 4px 0px 0px var(--retro-shadow-dark),
inset -4px -4px 0px 0px var(--retro-shadow-light), 4px 0px 0px 0px var(--tertiary-contrast),
0px 4px 0px 0px var(--tertiary-contrast), -4px 0px 0px 0px var(--tertiary-contrast),
0px -4px 0px 0px var(--tertiary-contrast), 0px 0px 0px 0px var(--tertiary-contrast);
}
[ngMenuBar] {
display: flex;
gap: 0.25rem;
padding: 0.25rem;
background-color: var(--page-background);
box-shadow: var(--retro-flat-shadow);
}
[ngMenu] {
margin-top: 8px;
width: 15rem;
padding: 0.25rem;
background-color: var(--page-background);
box-shadow: var(--retro-flat-shadow);
}
[ngMenu][data-visible='false'] {
display: none;
}
[ngMenuItem] {
outline: none;
display: flex;
cursor: pointer;
align-items: center;
gap: 0.5rem;
padding: 0.5rem;
font-size: 0.875rem;
}
[ngMenuItem]:not([aria-disabled='true']):hover,
[ngMenuItem]:not([aria-disabled='true']):focus,
[ngMenuItem][aria-expanded='true'] {
background: color-mix(in srgb, var(--border-color) 10%, transparent);
}
[ngMenuItem]:focus {
outline: 4px dashed var(--vivid-pink);
outline-offset: -4px;
}
[ngMenuItem] .icon {
opacity: 0.875;
font-size: 1.25rem;
}
[ngMenuItem] .label {
flex: 1;
opacity: 0.875;
font-size: 0.875rem;
}
[ngMenuItem]:not([aria-expanded='true']) .arrow {
opacity: 0.5;
}
[ngMenu] .separator {
border-top: 4px solid #000;
margin: 0.25rem 0;
opacity: 0.25;
}
dir="rtl" niteliği RTL modunu etkinleştirir. Sol ok sağa, sağ ok sola hareket eder ve RTL dil kullanıcıları için doğal navigasyonu korur.
Test etme
Angular Aria, menü çubuğu bileşenlerini test etmek için bileşen harness'leri sağlar. İşte harness'lerin bir bileşen testinde nasıl kullanılacağına dair bir örnek:
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {HarnessLoader} from '@angular/cdk/testing';
import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed';
import {MenuHarness} from '@angular/aria/menu/testing';
import {MyMenubarComponent} from './my-menubar'; // Sizin bileşeniniz
describe('MyMenubarComponent', () => {
let fixture: ComponentFixture<MyMenubarComponent>;
let loader: HarnessLoader;
beforeEach(async () => {
TestBed.configureTestingModule({
imports: [MyMenubarComponent],
});
fixture = TestBed.createComponent(MyMenubarComponent);
await fixture.whenStable();
loader = TestbedHarnessEnvironment.loader(fixture);
});
it('should interact with menubar items', async () => {
// Menü çubuğu harness'ini yükle ('[ngMenuBar]' seçicisine sahip bir MenuHarness'tir)
const menubar = await loader.getHarness(MenuHarness.with({selector: '[ngMenuBar]'}));
// Menü çubukları kalıcıdır ve her zaman "açık" durumdadır
expect(await menubar.isOpen()).toBe(true);
expect(await menubar.isMenuBar()).toBe(true);
// Üst düzey öğeleri al
const items = await menubar.getItems();
expect(items.length).toBe(2);
expect(await items[0].getText()).toBe('File');
expect(await items[1].getText()).toBe('Edit');
// Açılır menüsünü açmak için bir öğeye tıkla
await items[0].click();
const fileMenu = await items[0].getSubmenu();
expect(fileMenu).toBeTruthy();
expect(await fileMenu!.isOpen()).toBe(true);
});
});
API referansı
Ayrıntılı API belgeleri için aşağıdaki API referanslarını inceleyin: