-
Notifications
You must be signed in to change notification settings - Fork 6.8k
feat(cdk-experimental/menu): Add menu skeleton and build scripts #19583
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
b8e838b
187bc6e
7b010a9
5fbb865
9118b14
c039f66
235da65
645f0d7
2ac2cf3
e4e8c7e
0fed70f
ba75cf5
20ea413
dce973c
933edf9
a07fe11
a6dde62
36b4b5e
5d22b6b
740217d
c2c7146
633b573
559adf6
2c9b997
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
CDK_EXPERIMENTAL_ENTRYPOINTS = [ | ||
"column-resize", | ||
"dialog", | ||
"menu", | ||
"popover-edit", | ||
"scrolling", | ||
] | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
load("//tools:defaults.bzl", "ng_module") | ||
|
||
package(default_visibility = ["//visibility:public"]) | ||
|
||
ng_module( | ||
name = "menu", | ||
srcs = glob( | ||
["**/*.ts"], | ||
exclude = ["**/*.spec.ts"], | ||
), | ||
module_name = "@angular/cdk-experimental/menu", | ||
deps = [ | ||
"//src/cdk/coercion", | ||
"@npm//@angular/core", | ||
"@npm//rxjs", | ||
], | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
|
||
export * from './public-api'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
|
||
import {Directive, Input} from '@angular/core'; | ||
|
||
/** | ||
* Directive applied to an element which configures it as a MenuBar by setting the appropriate | ||
* role, aria attributes, and accessable keyboard and mouse handling logic. The component that | ||
* this directive is applied to should contain components marked with CdkMenuItem. | ||
* | ||
*/ | ||
@Directive({ | ||
selector: '[cdkMenuBar]', | ||
exportAs: 'cdkMenuBar', | ||
mmalerba marked this conversation as resolved.
Show resolved
Hide resolved
|
||
host: { | ||
'role': 'menubar', | ||
'[attr.aria-orientation]': 'orientation', | ||
}, | ||
}) | ||
export class CdkMenuBar { | ||
/** | ||
* Sets the aria-orientation attribute and determines where sub-menus will be opened. | ||
* Does not affect styling/layout. | ||
*/ | ||
@Input('cdkMenuBarOrientation') orientation: 'horizontal' | 'vertical' = 'horizontal'; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
|
||
import {Directive, Output, EventEmitter} from '@angular/core'; | ||
import {CdkMenuItem} from './menu-item'; | ||
|
||
/** | ||
* Directive which acts as a grouping container for `CdkMenuItem` instances with | ||
* `role="menuitemradio"`, similar to a `role="radiogroup"` element. | ||
*/ | ||
@Directive({ | ||
selector: '[cdkMenuGroup]', | ||
exportAs: 'cdkMenuGroup', | ||
host: { | ||
'role': 'group', | ||
}, | ||
}) | ||
export class CdkMenuGroup { | ||
/** Emits the element when checkbox or radiobutton state changed */ | ||
@Output() change: EventEmitter<CdkMenuItem> = new EventEmitter(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
|
||
import {Directive, Output, Input, EventEmitter} from '@angular/core'; | ||
import {CdkMenuPanel} from './menu-panel'; | ||
import {coerceBooleanProperty, BooleanInput} from '@angular/cdk/coercion'; | ||
|
||
/** | ||
* Directive which provides behavior for an element which when clicked: | ||
* If located in a CdkMenuBar: | ||
* - opens up an attached submenu | ||
* | ||
* If located in a CdkMenu/CdkMenuGroup, one of: | ||
* - executes the user defined click handler | ||
* - toggles its checkbox state | ||
* - toggles its radio button state (in relation to siblings) | ||
* | ||
* If it's in a CdkMenu and it triggers a sub-menu, hovering over the | ||
* CdkMenuItem will open the submenu. | ||
* | ||
*/ | ||
@Directive({ | ||
selector: '[cdkMenuItem], [cdkMenuTriggerFor]', | ||
exportAs: 'cdkMenuItem', | ||
host: { | ||
'type': 'button', | ||
'[attr.role]': 'role', | ||
'[attr.aria-checked]': '_getAriaChecked()', | ||
}, | ||
}) | ||
export class CdkMenuItem { | ||
/** Template reference variable to the menu this trigger opens */ | ||
@Input('cdkMenuTriggerFor') _menuPanel: CdkMenuPanel; | ||
|
||
/** ARIA role for the menu item. */ | ||
@Input() role: 'menuitem' | 'menuitemradio' | 'menuitemcheckbox' = 'menuitem'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looking at this in code, I would probably lean towards having separate classes for
|
||
|
||
/** Whether the checkbox or radiobutton is checked */ | ||
@Input() | ||
get checked() { | ||
return this._checked; | ||
} | ||
set checked(value: boolean) { | ||
this._checked = coerceBooleanProperty(value); | ||
} | ||
private _checked = false; | ||
|
||
/** Emits when the attached submenu is opened */ | ||
@Output() opened: EventEmitter<void> = new EventEmitter(); | ||
|
||
/** get the aria-checked value only if element is `menuitemradio` or `menuitemcheckbox` */ | ||
_getAriaChecked(): boolean | null { | ||
if (this.role === 'menuitem') { | ||
return null; | ||
} | ||
return this.checked; | ||
} | ||
|
||
/** Whether the menu item opens a menu */ | ||
hasSubmenu() { | ||
return !!this._menuPanel; | ||
} | ||
|
||
static ngAcceptInputType_checked: BooleanInput; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
|
||
import {NgModule} from '@angular/core'; | ||
import {CdkMenu} from './menu'; | ||
import {CdkMenuBar} from './menu-bar'; | ||
import {CdkMenuPanel} from './menu-panel'; | ||
import {CdkMenuItem} from './menu-item'; | ||
import {CdkMenuGroup} from './menu-group'; | ||
|
||
const EXPORTED_DECLARATIONS = [CdkMenuBar, CdkMenu, CdkMenuPanel, CdkMenuItem, CdkMenuGroup]; | ||
@NgModule({ | ||
exports: EXPORTED_DECLARATIONS, | ||
declarations: EXPORTED_DECLARATIONS, | ||
}) | ||
export class CdkMenuModule {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
|
||
import {Directive} from '@angular/core'; | ||
import {CdkMenu} from './menu'; | ||
|
||
/** | ||
andy9775 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* Directive applied to an ng-template which wraps a CdkMenu and provides a reference to the | ||
* child element it wraps which allows for opening of the CdkMenu in an overlay. | ||
*/ | ||
@Directive({selector: 'ng-template[cdkMenuPanel]', exportAs: 'cdkMenuPanel'}) | ||
export class CdkMenuPanel { | ||
/** Reference to the child menu component */ | ||
_menu: CdkMenu; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
|
||
import {Directive, Input, Output, EventEmitter} from '@angular/core'; | ||
import {CdkMenuItem} from './menu-item'; | ||
|
||
/** | ||
* Directive which configures the element as a Menu which should contain child elements marked as | ||
* CdkMenuItem or CdkMenuGroup. Sets the appropriate role and aria-attributes for a menu and | ||
* contains accessable keyboard and mouse handling logic. | ||
* | ||
* It also acts as a RadioGroup for elements marked with role `menuitemradio`. | ||
*/ | ||
@Directive({ | ||
selector: '[cdkMenu]', | ||
exportAs: 'cdkMenu', | ||
host: { | ||
'role': 'menubar', | ||
'[attr.aria-orientation]': 'orientation', | ||
}, | ||
}) | ||
export class CdkMenu { | ||
/** | ||
* Sets the aria-orientation attribute and determines where sub-menus will be opened. | ||
* Does not affect styling/layout. | ||
*/ | ||
@Input('cdkMenuOrientation') orientation: 'horizontal' | 'vertical' = 'vertical'; | ||
|
||
/** Event emitted when the menu is closed. */ | ||
@Output() readonly closed: EventEmitter<void | 'click' | 'tab' | 'escape'> = new EventEmitter(); | ||
|
||
/** Emits the activated element when checkbox or radiobutton state changed */ | ||
@Output() change: EventEmitter<CdkMenuItem>; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
|
||
export * from './menu-module'; | ||
export * from './menu-bar'; | ||
export * from './menu'; | ||
export * from './menu-item'; | ||
export * from './menu-panel'; | ||
export * from './menu-group'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
load("//tools:defaults.bzl", "ng_module") | ||
|
||
package(default_visibility = ["//visibility:public"]) | ||
|
||
ng_module( | ||
name = "cdk-experimental-menu", | ||
srcs = glob(["**/*.ts"]), | ||
assets = [ | ||
"cdk-menu-demo.html", | ||
"cdk-menu-demo.css", | ||
], | ||
deps = [ | ||
"//src/cdk-experimental/menu", | ||
"@npm//@angular/router", | ||
], | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
|
||
import {NgModule} from '@angular/core'; | ||
import {CommonModule} from '@angular/common'; | ||
import {RouterModule} from '@angular/router'; | ||
import {CdkMenuModule} from '@angular/cdk-experimental/menu'; | ||
|
||
import {CdkMenuDemo} from './cdk-menu-demo'; | ||
|
||
@NgModule({ | ||
imports: [ | ||
CdkMenuModule, | ||
CommonModule, | ||
RouterModule.forChild([{path: '', component: CdkMenuDemo}]), | ||
], | ||
declarations: [CdkMenuDemo], | ||
}) | ||
export class CdkMenuDemoModule {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
.example-menu-bar { | ||
display: flex; | ||
flex-direction: row; | ||
list-style: none; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
<ul cdkMenuBar class="example-menu-bar"> | ||
<li role="none"><button id="file_button" [cdkMenuTriggerFor]="file">File</button></li> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not in this PR, but at some point I want to sit down and think about the way we're using lists with the menu and see if we could do something simpler |
||
<li role="none"><button id="edit_button" [cdkMenuTriggerFor]="edit">Edit</button></li> | ||
</ul> | ||
|
||
<ng-template cdkMenuPanel #file="cdkMenuPanel"> | ||
<ul cdkMenu id="file_menu"> | ||
<li role="none"><button id="share_button" cdkMenuItem>Share</button></li> | ||
<li role="none"><button id="open_button" cdkMenuItem>Open</button></li> | ||
<li role="none"><button id="rename_button" cdkMenuItem>Rename</button></li> | ||
<li role="none"><button id="print_button" cdkMenuItem>Print</button></li> | ||
</ul> | ||
</ng-template> | ||
|
||
<ng-template cdkMenuPanel #edit="cdkMenuPanel"> | ||
<ul cdkMenu id="edit_menu"> | ||
<li role="none"><button id="undo_button" cdkMenuItem>Undo</button></li> | ||
<li role="none"><button id="redo_button" cdkMenuItem>Redo</button></li> | ||
<li role="none"><button id="cut_button" cdkMenuItem>Cut</button></li> | ||
<li role="none"><button id="copy_button" cdkMenuItem>Copy</button></li> | ||
<li role="none"><button id="paste_button" cdkMenuItem>Paste</button></li> | ||
</ul> | ||
</ng-template> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
|
||
import {Component} from '@angular/core'; | ||
|
||
@Component({ | ||
templateUrl: 'cdk-menu-demo.html', | ||
styleUrls: ['cdk-menu-demo.css'], | ||
}) | ||
export class CdkMenuDemo {} |
Uh oh!
There was an error while loading. Please reload this page.