src/context-menu/context-menu-item.component.ts
OnInit
AfterContentInit
OnDestroy
| selector | cds-menu-item, cds-context-menu-item, ibm-context-menu-item |
| styles |
:host {
grid-template-columns: 1rem 1fr max-content;
}
|
| template | |
Properties |
Methods |
Inputs |
Outputs |
HostBindings |
HostListeners |
Accessors |
constructor(elementRef: ElementRef, contextMenuSelectionService: ContextMenuSelectionService)
|
|||||||||
|
Parameters :
|
| checked | |
Type : boolean
|
|
Default value : false
|
|
| danger | |
Type : boolean
|
|
Default value : false
|
|
| disabled | |
Type : boolean
|
|
Default value : false
|
|
| icon | |
Type : string
|
|
Default value : ""
|
|
| info | |
Type : string
|
|
Default value : ""
|
|
| label | |
Type : string
|
|
Default value : ""
|
|
| type | |
Type : null | "checkbox" | "radio"
|
|
Default value : null
|
|
| value | |
Type : string
|
|
Default value : ""
|
|
| checkedChange | |
Type : EventEmitter
|
|
| itemClick | |
Type : EventEmitter
|
|
| attr.aria-checked |
Type : boolean
|
| attr.aria-disabled |
Type : boolean
|
| attr.aria-expanded |
Type : any
|
Default value : null
|
| attr.aria-haspopup |
Type : any
|
Default value : null
|
| attr.role |
Type : string
|
Default value : "menuitem"
|
| attr.tabindex |
Type : number
|
Default value : -1
|
| class.cds--menu-item |
Type : boolean
|
Default value : true
|
| blur |
blur()
|
| click |
Arguments : '$event'
|
click(event)
|
| focus |
focus()
|
| keydown.enter |
Arguments : '$event'
|
keydown.enter(event)
|
| keydown.space |
Arguments : '$event'
|
keydown.space(event)
|
| mouseout |
mouseout()
|
| mouseover |
mouseover()
|
| closeSubMenu |
closeSubMenu()
|
|
Returns :
void
|
| focusItem |
focusItem()
|
|
Returns :
void
|
| handleBlur |
handleBlur()
|
Decorators :
@HostListener('blur')
|
|
Returns :
void
|
| handleClick | ||||
handleClick(event)
|
||||
Decorators :
@HostListener('keydown.enter', ['$event'])
|
||||
|
Parameters :
Returns :
void
|
| handleFocus |
handleFocus()
|
Decorators :
@HostListener('focus')
|
|
Returns :
void
|
| handleMouseOut |
handleMouseOut()
|
Decorators :
@HostListener('mouseout')
|
|
Returns :
void
|
| handleMouseOver |
handleMouseOver()
|
Decorators :
@HostListener('mouseover')
|
|
Returns :
void
|
| handleSelection | ||||||
handleSelection(selected: boolean)
|
||||||
|
Parameters :
Returns :
void
|
| ngAfterContentInit |
ngAfterContentInit()
|
|
Returns :
void
|
| ngOnDestroy |
ngOnDestroy()
|
|
Returns :
void
|
| ngOnInit |
ngOnInit()
|
|
Returns :
void
|
| openSubMenu |
openSubMenu()
|
|
Returns :
void
|
| ariaExpanded |
Type : null
|
Default value : null
|
Decorators :
@HostBinding('attr.aria-expanded')
|
| ariaHasPopup |
Type : null
|
Default value : null
|
Decorators :
@HostBinding('attr.aria-haspopup')
|
| childContextMenu |
Type : ContextMenuComponent
|
Decorators :
@ContentChild(ContextMenuComponent, {static: true})
|
| hasChildren |
Default value : false
|
| optionClass |
Default value : true
|
Decorators :
@HostBinding('class.cds--menu-item')
|
| role |
Type : string
|
Default value : "menuitem"
|
Decorators :
@HostBinding('attr.role')
|
| selectable |
Default value : false
|
| Private subscriptions |
Default value : new Subscription()
|
| tabindex |
Default value : -1
|
Decorators :
@HostBinding('attr.tabindex')
|
| ariaChecked |
getariaChecked()
|
| ariaDisabled |
getariaDisabled()
|
import {
Component,
HostBinding,
Input,
Output,
EventEmitter,
ElementRef,
HostListener,
ContentChild,
Optional,
OnInit,
AfterContentInit,
OnDestroy
} from "@angular/core";
import { Subscription } from "rxjs";
import { ContextMenuSelectionService } from "./context-menu-selection.service";
import { ContextMenuComponent } from "./context-menu.component";
import { ItemClickEvent } from "./context-menu.types";
@Component({
selector: "cds-menu-item, cds-context-menu-item, ibm-context-menu-item",
template: `
<div class="cds--menu-item__icon">
<svg *ngIf="selectable && checked" cdsIcon="checkmark" size="16"></svg>
<svg *ngIf="!selectable && icon" [cdsIcon]="icon" size="16"></svg>
</div>
<div class="cds--menu-item__label" [title]="label">{{label}}</div>
<div class="cds--menu-item__shortcut">
<ng-container *ngIf="info">{{info}}</ng-container>
<svg *ngIf="hasChildren" cdsIcon="caret--right" size="16"></svg>
</div>
<ng-content></ng-content>
`,
styles: [`
:host {
grid-template-columns: 1rem 1fr max-content;
}
`]
})
export class ContextMenuItemComponent implements OnInit, AfterContentInit, OnDestroy {
@HostBinding("class.cds--menu-item") optionClass = true;
@HostBinding("attr.role") role = "menuitem";
@HostBinding("attr.tabindex") tabindex = -1;
@HostBinding("attr.aria-haspopup") ariaHasPopup = null;
@HostBinding("attr.aria-expanded") ariaExpanded = null;
@HostBinding("attr.aria-checked") get ariaChecked() {
return this.type === "checkbox" ?
(this.checked ? true : false) : null;
}
@HostBinding("attr.aria-disabled") get ariaDisabled() {
return this.disabled;
}
@Input() @HostBinding("class.cds--menu-item--disabled") disabled = false;
@Input() @HostBinding("class.cds--menu-item--danger") danger = false;
@Input() label = "";
@Input() info = "";
@Input() type: null | "checkbox" | "radio" = null;
@Input() checked = false;
@Input() icon = "";
@Input() value = "";
@Output() checkedChange = new EventEmitter<boolean>();
@Output() itemClick = new EventEmitter<ItemClickEvent>();
hasChildren = false;
selectable = false;
@ContentChild(ContextMenuComponent, { static: true }) childContextMenu: ContextMenuComponent;
private subscriptions = new Subscription();
constructor(
protected elementRef: ElementRef,
@Optional() protected contextMenuSelectionService: ContextMenuSelectionService
) {}
ngOnInit() {
switch (this.type) {
case "checkbox": {
this.role = "menuitemcheckbox";
this.selectable = true;
break;
}
case "radio": {
this.role = "menuitemradio";
this.selectable = true;
break;
}
default: {
this.role = "menuitem";
}
}
if (this.type && this.contextMenuSelectionService && this.value) {
const { selectionObservable } = this.contextMenuSelectionService;
const subscription = selectionObservable.subscribe((value) => {
if (this.type === "radio") {
this.handleSelection(value === this.value);
}
if (this.type === "checkbox") {
this.handleSelection(value.includes(this.value));
}
});
this.subscriptions.add(subscription);
}
}
ngAfterContentInit() {
if (this.childContextMenu) {
this.hasChildren = true;
this.ariaHasPopup = true;
this.ariaExpanded = false;
}
}
@HostListener("keydown.enter", ["$event"])
@HostListener("keydown.space", ["$event"])
@HostListener("click", ["$event"])
handleClick(event: MouseEvent & KeyboardEvent) {
event.stopPropagation();
if (this.disabled) {
return;
}
if (this.hasChildren) {
this.openSubMenu();
this.childContextMenu.focusMenu();
}
if (this.type) {
this.handleSelection(!this.checked);
}
if (this.contextMenuSelectionService) {
if (this.type === "radio") {
this.contextMenuSelectionService.selectRadio(this.value);
}
if (this.type === "checkbox") {
this.contextMenuSelectionService.selectCheckbox(this.value);
}
}
if (!this.disabled) {
this.itemClick.emit({
event,
label: this.label,
info: this.info,
value: this.value,
type: this.type
});
}
}
handleSelection(selected: boolean) {
this.checked = selected;
this.checkedChange.emit(this.checked);
}
openSubMenu() {
if (this.childContextMenu) {
this.childContextMenu.open = true;
this.ariaExpanded = true;
const dimensions = this.elementRef.nativeElement.getBoundingClientRect();
this.childContextMenu.position.left = dimensions.left + dimensions.width;
// subtract 4px to account for margins
this.childContextMenu.position.top = dimensions.top - 4;
}
}
closeSubMenu() {
if (this.childContextMenu) {
this.childContextMenu.open = false;
this.ariaExpanded = false;
}
}
@HostListener("mouseover")
handleMouseOver() {
this.openSubMenu();
}
@HostListener("mouseout")
handleMouseOut() {
this.closeSubMenu();
}
@HostListener("focus")
handleFocus() {
this.tabindex = 0;
if (this.hasChildren && this.ariaExpanded) {
this.closeSubMenu();
}
}
@HostListener("blur")
handleBlur() {
this.tabindex = -1;
}
focusItem() {
this.elementRef.nativeElement.focus();
}
ngOnDestroy() {
this.subscriptions.unsubscribe();
}
}
:host {
grid-template-columns: 1rem 1fr max-content;
}