diff --git a/src/cdk-experimental/menu/menu.md b/src/cdk-experimental/menu/menu.md index e28953a9bd86..38d19d2b76e5 100644 --- a/src/cdk-experimental/menu/menu.md +++ b/src/cdk-experimental/menu/menu.md @@ -29,7 +29,10 @@ menu directives to build your custom menu. A typical menu consists of the follow - `cdkMenu` - the actual menu you want to open - `cdkMenuItem` - added to each button - + Most menu interactions consist of two parts: a trigger and a menu panel. @@ -70,7 +73,10 @@ layed out horizontally or vertically (defaulting to horizontal). If the layout c the `orientation` attribute to match in order for the keyboard navigation to work properly and for menus to open up in the correct location. - + ### Context Menus @@ -79,7 +85,10 @@ container element with the `cdkContextMenuTriggerFor`, which behaves like `cdkMe that it responds to the browser's native `contextmenu` event. Custom context menus appear next to the cursor, similarly to native context menus. - + You can nest context menu container elements. Upon right-click, the menu associated with the closest container element will open. @@ -101,7 +110,10 @@ trigger. You can use an inline menu when you want a persistent menu interaction items within an inline menus are logically grouped together and you can navigate through them using your keyboard. - + ### Menu Items @@ -177,7 +189,10 @@ Note however that when the menu is closed and reopened any state is lost. You mu groups `change` output, or to `cdkMenuItemToggled` on each radio item and track changes your self. Finally, you can provide state for each item using the `checked` attribute. - + ### Smart Menu Aim diff --git a/src/components-examples/BUILD.bazel b/src/components-examples/BUILD.bazel index d980eb93b8ea..9ad3bb8ad421 100644 --- a/src/components-examples/BUILD.bazel +++ b/src/components-examples/BUILD.bazel @@ -59,6 +59,7 @@ ALL_EXAMPLES = [ "//src/components-examples/cdk/clipboard", "//src/components-examples/cdk/a11y", "//src/components-examples/cdk/overlay", + "//src/components-examples/cdk-experimental/menu", "//src/components-examples/cdk-experimental/popover-edit", "//src/components-examples/cdk-experimental/selection", ] diff --git a/src/components-examples/cdk-experimental/menu/BUILD.bazel b/src/components-examples/cdk-experimental/menu/BUILD.bazel new file mode 100644 index 000000000000..b07df5a2c9a5 --- /dev/null +++ b/src/components-examples/cdk-experimental/menu/BUILD.bazel @@ -0,0 +1,26 @@ +load("//tools:defaults.bzl", "ng_module") + +package(default_visibility = ["//visibility:public"]) + +ng_module( + name = "menu", + srcs = glob(["**/*.ts"]), + assets = glob([ + "**/*.html", + "**/*.css", + ]), + module_name = "@angular/components-examples/cdk-experimental/menu", + deps = [ + "//src/cdk-experimental/menu", + "@npm//@angular/forms", + ], +) + +filegroup( + name = "source-files", + srcs = glob([ + "**/*.html", + "**/*.css", + "**/*.ts", + ]), +) diff --git a/src/components-examples/cdk-experimental/menu/cdk-menu-context/cdk-menu-context-example.css b/src/components-examples/cdk-experimental/menu/cdk-menu-context/cdk-menu-context-example.css new file mode 100644 index 000000000000..0fed1a5894cc --- /dev/null +++ b/src/components-examples/cdk-experimental/menu/cdk-menu-context/cdk-menu-context-example.css @@ -0,0 +1,33 @@ +.example-menu { + display: inline-flex; + flex-direction: column; + min-width: 180px; + max-width: 280px; + background-color: rgb(255, 255, 255); + padding: 6px 0; +} + +.example-menu-item { + background-color: transparent; + cursor: pointer; + outline: none; + border: none; + + user-select: none; + min-width: 64px; + line-height: 36px; + padding: 0 16px; + + display: flex; + align-items: center; + flex-direction: row; + flex: 1; +} + +.example-menu-item:hover { + background-color: rgb(208, 208, 208); +} + +.example-menu-item:active { + background-color: rgb(170, 170, 170); +} diff --git a/src/components-examples/cdk-experimental/menu/cdk-menu-context/cdk-menu-context-example.html b/src/components-examples/cdk-experimental/menu/cdk-menu-context/cdk-menu-context-example.html new file mode 100644 index 000000000000..99338e29d68d --- /dev/null +++ b/src/components-examples/cdk-experimental/menu/cdk-menu-context/cdk-menu-context-example.html @@ -0,0 +1,18 @@ +
+ Did you ever hear the tragedy of Darth Plagueis The Wise? I thought not. It's not a story the Jedi + would tell you. It's a Sith legend. Darth Plagueis was a Dark Lord of the Sith, so powerful and so + wise he could use the Force to influence the midichlorians to create life… He had such a knowledge + of the dark side that he could even keep the ones he cared about from dying. The dark side of the + Force is a pathway to many abilities some consider to be unnatural. He became so powerful… the + only thing he was afraid of was losing his power, which eventually, of course, he did. + Unfortunately, he taught his apprentice everything he knew, then his apprentice killed him in his + sleep. Ironic. He could save others from death, but not himself. +
+ + +
+ + + +
+
diff --git a/src/components-examples/cdk-experimental/menu/cdk-menu-context/cdk-menu-context-example.ts b/src/components-examples/cdk-experimental/menu/cdk-menu-context/cdk-menu-context-example.ts new file mode 100644 index 000000000000..7f4c26af6165 --- /dev/null +++ b/src/components-examples/cdk-experimental/menu/cdk-menu-context/cdk-menu-context-example.ts @@ -0,0 +1,10 @@ +import {Component} from '@angular/core'; + +/** @title Context menu. */ +@Component({ + selector: 'cdk-menu-context-example', + exportAs: 'cdkMenuContextExample', + styleUrls: ['cdk-menu-context-example.css'], + templateUrl: 'cdk-menu-context-example.html', +}) +export class CdkMenuContextExample {} diff --git a/src/components-examples/cdk-experimental/menu/cdk-menu-inline/cdk-menu-inline-example.css b/src/components-examples/cdk-experimental/menu/cdk-menu-inline/cdk-menu-inline-example.css new file mode 100644 index 000000000000..0fed1a5894cc --- /dev/null +++ b/src/components-examples/cdk-experimental/menu/cdk-menu-inline/cdk-menu-inline-example.css @@ -0,0 +1,33 @@ +.example-menu { + display: inline-flex; + flex-direction: column; + min-width: 180px; + max-width: 280px; + background-color: rgb(255, 255, 255); + padding: 6px 0; +} + +.example-menu-item { + background-color: transparent; + cursor: pointer; + outline: none; + border: none; + + user-select: none; + min-width: 64px; + line-height: 36px; + padding: 0 16px; + + display: flex; + align-items: center; + flex-direction: row; + flex: 1; +} + +.example-menu-item:hover { + background-color: rgb(208, 208, 208); +} + +.example-menu-item:active { + background-color: rgb(170, 170, 170); +} diff --git a/src/components-examples/cdk-experimental/menu/cdk-menu-inline/cdk-menu-inline-example.html b/src/components-examples/cdk-experimental/menu/cdk-menu-inline/cdk-menu-inline-example.html new file mode 100644 index 000000000000..6b85d198fbe0 --- /dev/null +++ b/src/components-examples/cdk-experimental/menu/cdk-menu-inline/cdk-menu-inline-example.html @@ -0,0 +1,8 @@ +
+ + + + + + +
diff --git a/src/components-examples/cdk-experimental/menu/cdk-menu-inline/cdk-menu-inline-example.ts b/src/components-examples/cdk-experimental/menu/cdk-menu-inline/cdk-menu-inline-example.ts new file mode 100644 index 000000000000..e64489e64c34 --- /dev/null +++ b/src/components-examples/cdk-experimental/menu/cdk-menu-inline/cdk-menu-inline-example.ts @@ -0,0 +1,10 @@ +import {Component} from '@angular/core'; + +/** @title Gmail inline menu. */ +@Component({ + selector: 'cdk-menu-inline-example', + exportAs: 'cdkMenuInlineExample', + styleUrls: ['cdk-menu-inline-example.css'], + templateUrl: 'cdk-menu-inline-example.html', +}) +export class CdkMenuInlineExample {} diff --git a/src/components-examples/cdk-experimental/menu/cdk-menu-menubar/cdk-menu-menubar-example.css b/src/components-examples/cdk-experimental/menu/cdk-menu-menubar/cdk-menu-menubar-example.css new file mode 100644 index 000000000000..ed33366edc61 --- /dev/null +++ b/src/components-examples/cdk-experimental/menu/cdk-menu-menubar/cdk-menu-menubar-example.css @@ -0,0 +1,76 @@ +.example-menu-bar-item { + cursor: pointer; + outline: none; + border: none; + + user-select: none; + min-width: 34px; + line-height: 26px; + padding: 0 16px; +} + +.example-menu-bar-item:hover { + background-color: rgb(208, 208, 208); +} + +.example-menu { + display: inline-flex; + flex-direction: column; + min-width: 180px; + max-width: 280px; + background-color: rgb(255, 255, 255); + padding: 6px 0; +} + +.example-menu hr { + width: 100%; + color: rgba(0, 0, 0, 0.12); +} + +.example-menu .example-menu-group { + display: inline-flex; + flex-direction: column; +} + +.example-menu .example-menu-item { + background-color: transparent; + cursor: pointer; + outline: none; + border: none; + + user-select: none; + min-width: 64px; + line-height: 36px; + padding: 0 16px; + + display: flex; + align-items: center; + flex-direction: row; + flex: 1; +} + +.example-menu-item > span { + display: flex; + flex-direction: row; + flex: 1; + justify-content: flex-end; +} + +.example-menu .example-menu-item:hover { + background-color: rgb(208, 208, 208); +} + +.example-menu .example-menu-item[role='menuitemradio'][aria-checked='true'] { + background-color: rgb(225, 225, 225); +} +.example-menu .example-menu-item[role='menuitemcheckbox'][aria-checked='true'] { + background-color: rgb(225, 225, 225); +} +.example-menu .example-menu-item:active { + background-color: rgb(170, 170, 170); +} + +.example-menu-bar-item[aria-expanded='true'], +.example-menu-item[aria-expanded='true'] { + background-color: rgb(208, 208, 208) !important; +} diff --git a/src/components-examples/cdk-experimental/menu/cdk-menu-menubar/cdk-menu-menubar-example.html b/src/components-examples/cdk-experimental/menu/cdk-menu-menubar/cdk-menu-menubar-example.html new file mode 100644 index 000000000000..655c5eb894af --- /dev/null +++ b/src/components-examples/cdk-experimental/menu/cdk-menu-menubar/cdk-menu-menubar-example.html @@ -0,0 +1,66 @@ +
+ + + +
+ + +
+ +
+ + + +
+ +
+
+ + +
+ + +
+ + + +
+
+ + +
+
+ + +
+
+
+ + + +
+
+
+ + +
+ + +
+ + + +
+
+ + +
+ + + +
+
diff --git a/src/components-examples/cdk-experimental/menu/cdk-menu-menubar/cdk-menu-menubar-example.ts b/src/components-examples/cdk-experimental/menu/cdk-menu-menubar/cdk-menu-menubar-example.ts new file mode 100644 index 000000000000..dc80a88db662 --- /dev/null +++ b/src/components-examples/cdk-experimental/menu/cdk-menu-menubar/cdk-menu-menubar-example.ts @@ -0,0 +1,10 @@ +import {Component} from '@angular/core'; + +/** @title Google Docs Menu Bar. */ +@Component({ + selector: 'cdk-menu-menubar-example', + exportAs: 'cdkMenuMenubarExample', + styleUrls: ['cdk-menu-menubar-example.css'], + templateUrl: 'cdk-menu-menubar-example.html', +}) +export class CdkMenuMenubarExample {} diff --git a/src/components-examples/cdk-experimental/menu/cdk-menu-standalone-menu/cdk-menu-standalone-menu-example.css b/src/components-examples/cdk-experimental/menu/cdk-menu-standalone-menu/cdk-menu-standalone-menu-example.css new file mode 100644 index 000000000000..9aec6cde6a0b --- /dev/null +++ b/src/components-examples/cdk-experimental/menu/cdk-menu-standalone-menu/cdk-menu-standalone-menu-example.css @@ -0,0 +1,44 @@ +.example-menu { + display: inline-flex; + flex-direction: column; + min-width: 180px; + max-width: 280px; + background-color: rgba(255, 255, 255); + padding: 6px 0; +} + +.example-menu-item, +.example-standalone-item { + background-color: transparent; + cursor: pointer; + outline: none; + border: none; + + user-select: none; + min-width: 64px; + line-height: 36px; + padding: 0 16px; + + display: flex; + align-items: center; + flex-direction: row; + flex: 1; +} + +.example-menu-item:hover { + background-color: rgb(208, 208, 208); +} + +.example-menu-item:active { + background-color: rgb(170, 170, 170); +} + +.example-standalone-item { + background-color: rgb(239, 239, 239); +} +.example-standalone-item:hover { + background-color: rgb(208, 208, 208); +} +.example-standalone-item[aria-expanded='true'] { + background-color: rgb(208, 208, 208); +} diff --git a/src/components-examples/cdk-experimental/menu/cdk-menu-standalone-menu/cdk-menu-standalone-menu-example.html b/src/components-examples/cdk-experimental/menu/cdk-menu-standalone-menu/cdk-menu-standalone-menu-example.html new file mode 100644 index 000000000000..5aca493001a9 --- /dev/null +++ b/src/components-examples/cdk-experimental/menu/cdk-menu-standalone-menu/cdk-menu-standalone-menu-example.html @@ -0,0 +1,10 @@ + + + +
+ + + + +
+
diff --git a/src/components-examples/cdk-experimental/menu/cdk-menu-standalone-menu/cdk-menu-standalone-menu-example.ts b/src/components-examples/cdk-experimental/menu/cdk-menu-standalone-menu/cdk-menu-standalone-menu-example.ts new file mode 100644 index 000000000000..2525c5671252 --- /dev/null +++ b/src/components-examples/cdk-experimental/menu/cdk-menu-standalone-menu/cdk-menu-standalone-menu-example.ts @@ -0,0 +1,9 @@ +import {Component} from '@angular/core'; + +/** @title Menu with Standalone Trigger. */ +@Component({ + selector: 'cdk-menu-standalone-menu-example', + styleUrls: ['cdk-menu-standalone-menu-example.css'], + templateUrl: 'cdk-menu-standalone-menu-example.html', +}) +export class CdkMenuStandaloneMenuExample {} diff --git a/src/components-examples/cdk-experimental/menu/cdk-menu-standalone-stateful-menu/cdk-menu-standalone-stateful-menu-example.css b/src/components-examples/cdk-experimental/menu/cdk-menu-standalone-stateful-menu/cdk-menu-standalone-stateful-menu-example.css new file mode 100644 index 000000000000..df43544a02fc --- /dev/null +++ b/src/components-examples/cdk-experimental/menu/cdk-menu-standalone-stateful-menu/cdk-menu-standalone-stateful-menu-example.css @@ -0,0 +1,60 @@ +.example-menu { + display: inline-flex; + flex-direction: column; + min-width: 180px; + max-width: 280px; + background-color: rgb(255, 255, 255); + padding: 6px 0; +} + +.example-menu .example-menu-item { + width: 100%; +} + +hr { + width: 100%; + color: rgba(0, 0, 0, 0.12); +} + +.example-menu-item, +.example-standalone-item { + background-color: transparent; + cursor: pointer; + outline: none; + border: none; + + user-select: none; + min-width: 64px; + line-height: 36px; + padding: 0 16px; + + display: flex; + align-items: center; + flex-direction: row; + flex: 1; +} + +.example-menu-item:hover { + background-color: rgb(208, 208, 208); +} + +.example-menu-item:active { + background-color: rgb(170, 170, 170); +} + +.example-standalone-item { + background-color: rgb(239, 239, 239); +} +.example-standalone-item:hover { + background-color: rgb(208, 208, 208); +} +.example-standalone-item[aria-expanded='true'] { + background-color: rgb(208, 208, 208); +} + +.example-menu-item[role='menuitemradio'][aria-checked='true'] { + background-color: rgb(225, 225, 225); +} +.example-menu-item[role='menuitemcheckbox'][aria-checked='true'] { + background-color: rgb(225, 225, 225); +} diff --git a/src/components-examples/cdk-experimental/menu/cdk-menu-standalone-stateful-menu/cdk-menu-standalone-stateful-menu-example.html b/src/components-examples/cdk-experimental/menu/cdk-menu-standalone-stateful-menu/cdk-menu-standalone-stateful-menu-example.html new file mode 100644 index 000000000000..73f014fc7204 --- /dev/null +++ b/src/components-examples/cdk-experimental/menu/cdk-menu-standalone-stateful-menu/cdk-menu-standalone-stateful-menu-example.html @@ -0,0 +1,30 @@ + + + +
+ + +
+
+ + + +
+
+
diff --git a/src/components-examples/cdk-experimental/menu/cdk-menu-standalone-stateful-menu/cdk-menu-standalone-stateful-menu-example.ts b/src/components-examples/cdk-experimental/menu/cdk-menu-standalone-stateful-menu/cdk-menu-standalone-stateful-menu-example.ts new file mode 100644 index 000000000000..06e41c0dafc3 --- /dev/null +++ b/src/components-examples/cdk-experimental/menu/cdk-menu-standalone-stateful-menu/cdk-menu-standalone-stateful-menu-example.ts @@ -0,0 +1,19 @@ +import {Component} from '@angular/core'; +import {CdkMenuItem} from '@angular/cdk-experimental/menu'; + +/** @title Stateful Menu with Standalone Trigger. */ +@Component({ + selector: 'cdk-menu-standalone-stateful-menu-example', + styleUrls: ['cdk-menu-standalone-stateful-menu-example.css'], + templateUrl: 'cdk-menu-standalone-stateful-menu-example.html', +}) +export class CdkMenuStandaloneStatefulMenuExample { + bold = true; + italic = false; + + size: string | undefined = 'Normal'; + + onSizeChange(item: CdkMenuItem) { + this.size = item._elementRef.nativeElement.textContent?.trim(); + } +} diff --git a/src/components-examples/cdk-experimental/menu/index.ts b/src/components-examples/cdk-experimental/menu/index.ts new file mode 100644 index 000000000000..3ed9926ee0da --- /dev/null +++ b/src/components-examples/cdk-experimental/menu/index.ts @@ -0,0 +1,34 @@ +import {NgModule} from '@angular/core'; +import {CdkMenuModule} from '@angular/cdk-experimental/menu'; +import { + CdkMenuStandaloneMenuExample +} from './cdk-menu-standalone-menu/cdk-menu-standalone-menu-example'; +import { + CdkMenuStandaloneStatefulMenuExample +} from './cdk-menu-standalone-stateful-menu/cdk-menu-standalone-stateful-menu-example'; +import {CdkMenuMenubarExample} from './cdk-menu-menubar/cdk-menu-menubar-example'; +import {CdkMenuInlineExample} from './cdk-menu-inline/cdk-menu-inline-example'; +import {CdkMenuContextExample} from './cdk-menu-context/cdk-menu-context-example'; + +export { + CdkMenuStandaloneMenuExample, + CdkMenuMenubarExample, + CdkMenuInlineExample, + CdkMenuContextExample, + CdkMenuStandaloneStatefulMenuExample, +}; + +const EXAMPLES = [ + CdkMenuStandaloneMenuExample, + CdkMenuMenubarExample, + CdkMenuInlineExample, + CdkMenuContextExample, + CdkMenuStandaloneStatefulMenuExample, +]; + +@NgModule({ + imports: [CdkMenuModule], + declarations: EXAMPLES, + exports: EXAMPLES, +}) +export class CdkMenuExamplesModule {} diff --git a/src/dev-app/cdk-experimental-menu/BUILD.bazel b/src/dev-app/cdk-experimental-menu/BUILD.bazel index 13b644897db3..b7034d9f5765 100644 --- a/src/dev-app/cdk-experimental-menu/BUILD.bazel +++ b/src/dev-app/cdk-experimental-menu/BUILD.bazel @@ -11,6 +11,7 @@ ng_module( ], deps = [ "//src/cdk-experimental/menu", + "//src/components-examples/cdk-experimental/menu", "@npm//@angular/router", ], ) diff --git a/src/dev-app/cdk-experimental-menu/cdk-menu-demo-module.ts b/src/dev-app/cdk-experimental-menu/cdk-menu-demo-module.ts index 6e1fdc06edd6..93ca63ba31e9 100644 --- a/src/dev-app/cdk-experimental-menu/cdk-menu-demo-module.ts +++ b/src/dev-app/cdk-experimental-menu/cdk-menu-demo-module.ts @@ -10,6 +10,7 @@ import {NgModule} from '@angular/core'; import {CommonModule} from '@angular/common'; import {RouterModule} from '@angular/router'; import {CdkMenuModule} from '@angular/cdk-experimental/menu'; +import {CdkMenuExamplesModule} from '@angular/components-examples/cdk-experimental/menu'; import {CdkMenuDemo} from './cdk-menu-demo'; @@ -17,6 +18,7 @@ import {CdkMenuDemo} from './cdk-menu-demo'; imports: [ CdkMenuModule, CommonModule, + CdkMenuExamplesModule, RouterModule.forChild([{path: '', component: CdkMenuDemo}]), ], declarations: [CdkMenuDemo], diff --git a/src/dev-app/cdk-experimental-menu/cdk-menu-demo.html b/src/dev-app/cdk-experimental-menu/cdk-menu-demo.html index e26c2bc2aee9..92b7de7854b3 100644 --- a/src/dev-app/cdk-experimental-menu/cdk-menu-demo.html +++ b/src/dev-app/cdk-experimental-menu/cdk-menu-demo.html @@ -1,3 +1,32 @@ +
+

Examples

+ +
+

Inline Menu

+ +
+ +
+

Standalone Menu

+ +
+ +
+

Standalone Stateful Menu

+ +
+ +
+

Menu Bar

+ +
+ +
+

Context Menu

+ +
+
+

MenuBar Example