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.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;
}