Menubar
Overview
Menubar یک navigation bar افقی است که دسترسی persistent به menuهای برنامه را فراهم میکند. Menubarها commandها را در categoryهای منطقی مثل File، Edit و View سازماندهی میکنند و به کاربران کمک میکنند featureهای برنامه را با keyboard یا mouse interaction پیدا و اجرا کنند.
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);
}
}<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>@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;
}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);
}
}<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>@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;
}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);
}
}<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>@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;
}Usage
Menubarها برای سازماندهی commandهای برنامه در navigationای persistent و قابل discover مناسباند.
از menubar استفاده کنید وقتی:
- application command bar میسازید \(مثل File، Edit، View، Insert، Format\).
- navigation persistent میسازید که در سراسر interface visible میماند.
- commandها را در categoryهای top-level منطقی سازماندهی میکنید.
- به horizontal menu navigation همراه با keyboard support نیاز دارید.
- interfaceهایی به سبک برنامههای desktop میسازید.
از menubar پرهیز کنید وقتی:
- برای actionهای جداگانه dropdown menu میسازید \(به جای آن از Menu with trigger استفاده کنید\).
- context menu میسازید \(از pattern موجود در راهنمای Menu استفاده کنید\).
- action listهای ساده standalone دارید \(به جای آن از Menu استفاده کنید\).
- interfaceهای mobile دارید که فضای افقی محدود است.
- navigation باید در sidebar یا header navigation pattern قرار بگیرد.
قابلیتها
- Horizontal navigation - arrow keyهای چپ/راست بین categoryهای top-level حرکت میکنند.
- Persistent visibility - همیشه visible است و modal یا dismissable نیست.
- Hover-to-open - پس از اولین interaction با keyboard یا click، submenuها روی hover باز میشوند.
- Nested submenuها - پشتیبانی از چند level عمق menu.
- Keyboard navigation - arrow keyها، Enter/Space، Escape و typeahead search.
- Disabled stateها - کل menubar یا itemهای جداگانه را disabled کنید.
- RTL support - navigation خودکار برای زبانهای راستبهچپ.
مثالها
Basic menubar
Menubar دسترسی persistent به commandهای برنامه را فراهم میکند که در categoryهای top-level سازماندهی شدهاند. کاربران با arrowهای چپ/راست بین categoryها navigate میکنند و با Enter یا arrow پایین menuها را باز میکنند.
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);
}
}<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>@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;
}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);
}
}<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>@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;
}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);
}
}<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>@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;
}arrow راست را فشار دهید تا بین File، Edit و View حرکت کنید. Enter یا arrow پایین را فشار دهید تا menu باز شود و با arrowهای بالا/پایین بین submenu itemها navigate کنید.
Menubar itemهای disabled
menu itemهای مشخص یا کل menubar را disabled کنید تا از interaction جلوگیری شود. با input مربوط به softDisabled کنترل کنید itemهای disabled بتوانند keyboard focus دریافت کنند یا نه.
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);
}
}<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>@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;
}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);
}
}<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>@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;
}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);
}
}<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>@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;
}وقتی [softDisabled]="true" روی menubar باشد، itemهای disabled میتوانند focus دریافت کنند اما activate نمیشوند. وقتی [softDisabled]="false" باشد، itemهای disabled هنگام keyboard navigation skip میشوند.
RTL support
Menubarها به صورت خودکار با زبانهای راستبهچپ \(RTL\) سازگار میشوند. navigation با arrow keyها جهت را معکوس میکند و submenuها در سمت چپ position میشوند.
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);
}
}<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>@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;
}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);
}
}<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>@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;
}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);
}
}<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>@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;
}attribute مربوط به dir="rtl"، RTL mode را فعال میکند. arrow چپ به راست حرکت میکند و arrow راست به چپ، تا navigation طبیعی برای کاربران زبانهای RTL حفظ شود.
Testing
Angular Aria برای testing کامپوننتهای menubar، component harness ارائه میکند. این نمونه نحوه استفاده از harnessها را در یک component test نشان میدهد:
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'; // Your component
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 () => {
// Load the menubar harness (which is a MenuHarness with selector '[ngMenuBar]')
const menubar = await loader.getHarness(MenuHarness.with({selector: '[ngMenuBar]'}));
// Menubars are persistent and always "open"
expect(await menubar.isOpen()).toBe(true);
expect(await menubar.isMenuBar()).toBe(true);
// Get top-level items
const items = await menubar.getItems();
expect(items.length).toBe(2);
expect(await items[0].getText()).toBe('File');
expect(await items[1].getText()).toBe('Edit');
// Click an item to open its dropdown menu
await items[0].click();
const fileMenu = await items[0].getSubmenu();
expect(fileMenu).toBeTruthy();
expect(await fileMenu!.isOpen()).toBe(true);
});
});API reference
برای مستندات دقیق API، referenceهای API زیر را بررسی کنید: