carbon-components-angular
Version:
Next generation components
551 lines (542 loc) • 24.1 kB
JavaScript
import * as i2 from '@angular/common';
import { CommonModule } from '@angular/common';
import * as i0 from '@angular/core';
import { Component, HostBinding, Injectable, EventEmitter, Input, Output, HostListener, Optional, ContentChild, NgModule } from '@angular/core';
import * as i3 from 'carbon-components-angular/icon';
import { IconModule } from 'carbon-components-angular/icon';
import { ReplaySubject, Subscription } from 'rxjs';
class ContextMenuDividerComponent {
constructor() {
this.dividerClass = true;
this.role = "separator";
}
}
ContextMenuDividerComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ContextMenuDividerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
ContextMenuDividerComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: ContextMenuDividerComponent, selector: "cds-menu-divider, cds-context-menu-divider, ibm-context-menu-divider", host: { properties: { "class.cds--menu-item-divider": "this.dividerClass", "attr.role": "this.role" } }, ngImport: i0, template: "", isInline: true });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ContextMenuDividerComponent, decorators: [{
type: Component,
args: [{
selector: "cds-menu-divider, cds-context-menu-divider, ibm-context-menu-divider",
template: ""
}]
}], propDecorators: { dividerClass: [{
type: HostBinding,
args: ["class.cds--menu-item-divider"]
}], role: [{
type: HostBinding,
args: ["attr.role"]
}] } });
class ContextMenuSelectionService {
constructor() {
this.selectionSubject = new ReplaySubject(1);
this.value = [];
this.selectionObservable = this.selectionSubject.asObservable();
}
selectRadio(value) {
if (!value) {
return;
}
this.selectionSubject.next(value);
this.value = [value];
}
selectCheckbox(value) {
if (!value) {
return;
}
if (this.value.includes(value)) {
this.value = this.value.filter(v => v !== value);
}
else {
this.value.push(value);
}
this.selectionSubject.next(this.value);
}
selectCheckboxes(value) {
if (!value) {
return;
}
this.value = value;
this.selectionSubject.next(value);
}
}
ContextMenuSelectionService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ContextMenuSelectionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
ContextMenuSelectionService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ContextMenuSelectionService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ContextMenuSelectionService, decorators: [{
type: Injectable
}], ctorParameters: function () { return []; } });
class ContextMenuGroupComponent {
constructor(contextMenuSelectionService) {
this.contextMenuSelectionService = contextMenuSelectionService;
this.role = "group";
this.label = null;
this.value = [];
this.type = null;
this.valueChange = new EventEmitter();
this.subscription = new Subscription();
}
get radioGroup() { return this.type === "radio"; }
get group() { return this.type === "checkbox"; }
ngOnInit() {
const { selectionObservable } = this.contextMenuSelectionService;
const subscription = selectionObservable.subscribe(value => {
this.valueChange.emit(value);
});
this.subscription.add(subscription);
}
ngOnChanges(changes) {
if (changes.value) {
if (this.type === "radio") {
this.contextMenuSelectionService.selectRadio(changes.value.currentValue);
}
if (this.type === "checkbox") {
this.contextMenuSelectionService.selectCheckboxes(changes.value.currentValue);
}
}
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
ContextMenuGroupComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ContextMenuGroupComponent, deps: [{ token: ContextMenuSelectionService }], target: i0.ɵɵFactoryTarget.Component });
ContextMenuGroupComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: ContextMenuGroupComponent, selector: "cds-menu-group, cds-context-menu-group, ibm-context-menu-group", inputs: { label: "label", value: "value", type: "type" }, outputs: { valueChange: "valueChange" }, host: { properties: { "attr.role": "this.role", "class.cds--menu-item-radio-group": "this.radioGroup", "class.cds--menu-item-group": "this.group", "attr.aria-label": "this.label" } }, providers: [ContextMenuSelectionService], usesOnChanges: true, ngImport: i0, template: `
<ng-content></ng-content>
`, isInline: true });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ContextMenuGroupComponent, decorators: [{
type: Component,
args: [{
selector: "cds-menu-group, cds-context-menu-group, ibm-context-menu-group",
template: `
<ng-content></ng-content>
`,
providers: [ContextMenuSelectionService]
}]
}], ctorParameters: function () { return [{ type: ContextMenuSelectionService }]; }, propDecorators: { role: [{
type: HostBinding,
args: ["attr.role"]
}], radioGroup: [{
type: HostBinding,
args: ["class.cds--menu-item-radio-group"]
}], group: [{
type: HostBinding,
args: ["class.cds--menu-item-group"]
}], label: [{
type: HostBinding,
args: ["attr.aria-label"]
}, {
type: Input
}], value: [{
type: Input
}], type: [{
type: Input
}], valueChange: [{
type: Output
}] } });
/**
* Get started with importing the module:
*
* ```typescript
* import { ContextMenuModule } from 'carbon-components-angular';
* ```
*
* [See demo](../../?path=/story/components-context-menu--basic)
*/
class ContextMenuComponent {
constructor(elementRef) {
this.elementRef = elementRef;
this.open = false;
this.position = {
left: 0,
top: 0
};
this.size = "lg";
this.role = "menu";
this.tabindex = "-1";
/**
* @todo - convert to getter in v6, should resolve expression has changed
* after switching to on OnPush Change Detection Strategy
*/
this.iconClass = false;
}
get hostClass() {
const open = this.open ? "cds--menu--open cds--menu--shown" : "";
return `cds--menu cds--autoalign cds--menu--${this.size} ${open}`;
}
get leftPosition() { return this.position.left; }
get topPosition() { return this.position.top; }
ngOnChanges(changes) {
if (changes.open && changes.open.currentValue) {
this.focusMenu();
}
}
ngAfterViewInit() {
setTimeout(() => {
const nativeElement = this.elementRef.nativeElement;
if (nativeElement) {
this.iconClass = !!nativeElement
.querySelector(".cds--menu-item .cds--menu-item__icon svg");
}
});
}
focusMenu() {
// wait until the next tick to let the DOM settle before changing the focus
setTimeout(() => {
const list = this.elementRef.nativeElement;
const firstOption = list.querySelector(".cds--menu-item");
firstOption.focus();
});
}
handleNavigation(event) {
const list = this.elementRef.nativeElement;
const subMenus = Array.from(list.querySelectorAll("cds-context-menu[role=menu]"));
const menuItems = Array.from(list.querySelectorAll(".cds--menu-item"))
.filter(menuItem => !subMenus.some(subMenu => subMenu.contains(menuItem)));
const currentIndex = menuItems.findIndex(menuItem => parseInt(menuItem.getAttribute("tabindex"), 10) === 0);
const currentMenuItem = menuItems[currentIndex];
switch (event.key) {
case "ArrowDown": {
if (document.activeElement === list) {
menuItems[0].focus();
}
else {
if (currentIndex !== -1 && currentIndex < menuItems.length - 1) {
menuItems[currentIndex + 1].focus();
}
}
break;
}
case "ArrowUp": {
if (document.activeElement === list) {
menuItems[menuItems.length - 1].focus();
}
else {
if (currentIndex !== -1 && currentIndex > 0) {
menuItems[currentIndex - 1].focus();
}
}
break;
}
case "ArrowRight": {
if (currentIndex !== -1 && subMenus.some(subMenu => currentMenuItem.contains(subMenu))) {
currentMenuItem.click();
}
break;
}
case "ArrowLeft": {
const parent = currentMenuItem.parentElement.closest(".cds--menu-item, .cds--menu-item");
if (parent) {
parent.focus();
}
break;
}
}
}
}
ContextMenuComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ContextMenuComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
ContextMenuComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: ContextMenuComponent, selector: "cds-menu, cds-context-menu, ibm-context-menu", inputs: { open: "open", position: "position", size: "size" }, host: { listeners: { "keydown": "handleNavigation($event)" }, properties: { "class": "this.hostClass", "attr.role": "this.role", "attr.tabindex": "this.tabindex", "style.left.px": "this.leftPosition", "style.top.px": "this.topPosition", "class.cds--menu--with-icons": "this.iconClass" } }, usesOnChanges: true, ngImport: i0, template: `
<ng-content></ng-content>
`, isInline: true, styles: [":host{display:block}\n"] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ContextMenuComponent, decorators: [{
type: Component,
args: [{ selector: "cds-menu, cds-context-menu, ibm-context-menu", template: `
<ng-content></ng-content>
`, styles: [":host{display:block}\n"] }]
}], ctorParameters: function () { return [{ type: i0.ElementRef }]; }, propDecorators: { open: [{
type: Input
}], position: [{
type: Input
}], size: [{
type: Input
}], hostClass: [{
type: HostBinding,
args: ["class"]
}], role: [{
type: HostBinding,
args: ["attr.role"]
}], tabindex: [{
type: HostBinding,
args: ["attr.tabindex"]
}], leftPosition: [{
type: HostBinding,
args: ["style.left.px"]
}], topPosition: [{
type: HostBinding,
args: ["style.top.px"]
}], iconClass: [{
type: HostBinding,
args: ["class.cds--menu--with-icons"]
}], handleNavigation: [{
type: HostListener,
args: ["keydown", ["$event"]]
}] } });
class ContextMenuItemComponent {
constructor(elementRef, contextMenuSelectionService) {
this.elementRef = elementRef;
this.contextMenuSelectionService = contextMenuSelectionService;
this.optionClass = true;
this.role = "menuitem";
this.tabindex = -1;
this.ariaHasPopup = null;
this.ariaExpanded = null;
this.disabled = false;
this.danger = false;
this.label = "";
this.info = "";
this.type = null;
this.checked = false;
this.icon = "";
this.value = "";
this.checkedChange = new EventEmitter();
this.itemClick = new EventEmitter();
this.hasChildren = false;
this.selectable = false;
this.subscriptions = new Subscription();
}
get ariaChecked() {
return this.type === "checkbox" ?
(this.checked ? true : false) : null;
}
get ariaDisabled() {
return this.disabled;
}
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;
}
}
handleClick(event) {
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) {
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;
}
}
handleMouseOver() {
this.openSubMenu();
}
handleMouseOut() {
this.closeSubMenu();
}
handleFocus() {
this.tabindex = 0;
if (this.hasChildren && this.ariaExpanded) {
this.closeSubMenu();
}
}
handleBlur() {
this.tabindex = -1;
}
focusItem() {
this.elementRef.nativeElement.focus();
}
ngOnDestroy() {
this.subscriptions.unsubscribe();
}
}
ContextMenuItemComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ContextMenuItemComponent, deps: [{ token: i0.ElementRef }, { token: ContextMenuSelectionService, optional: true }], target: i0.ɵɵFactoryTarget.Component });
ContextMenuItemComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: ContextMenuItemComponent, selector: "cds-menu-item, cds-context-menu-item, ibm-context-menu-item", inputs: { disabled: "disabled", danger: "danger", label: "label", info: "info", type: "type", checked: "checked", icon: "icon", value: "value" }, outputs: { checkedChange: "checkedChange", itemClick: "itemClick" }, host: { listeners: { "keydown.enter": "handleClick($event)", "keydown.space": "handleClick($event)", "click": "handleClick($event)", "mouseover": "handleMouseOver()", "mouseout": "handleMouseOut()", "focus": "handleFocus()", "blur": "handleBlur()" }, properties: { "class.cds--menu-item": "this.optionClass", "attr.role": "this.role", "attr.tabindex": "this.tabindex", "attr.aria-haspopup": "this.ariaHasPopup", "attr.aria-expanded": "this.ariaExpanded", "attr.aria-checked": "this.ariaChecked", "attr.aria-disabled": "this.ariaDisabled", "class.cds--menu-item--disabled": "this.disabled", "class.cds--menu-item--danger": "this.danger" } }, queries: [{ propertyName: "childContextMenu", first: true, predicate: ContextMenuComponent, descendants: true, static: true }], ngImport: i0, 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>
`, isInline: true, styles: [":host{grid-template-columns:1rem 1fr max-content}\n"], dependencies: [{ kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.IconDirective, selector: "[cdsIcon], [ibmIcon]", inputs: ["ibmIcon", "cdsIcon", "size", "title", "ariaLabel", "ariaLabelledBy", "ariaHidden", "isFocusable"] }] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ContextMenuItemComponent, decorators: [{
type: Component,
args: [{ 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}\n"] }]
}], ctorParameters: function () {
return [{ type: i0.ElementRef }, { type: ContextMenuSelectionService, decorators: [{
type: Optional
}] }];
}, propDecorators: { optionClass: [{
type: HostBinding,
args: ["class.cds--menu-item"]
}], role: [{
type: HostBinding,
args: ["attr.role"]
}], tabindex: [{
type: HostBinding,
args: ["attr.tabindex"]
}], ariaHasPopup: [{
type: HostBinding,
args: ["attr.aria-haspopup"]
}], ariaExpanded: [{
type: HostBinding,
args: ["attr.aria-expanded"]
}], ariaChecked: [{
type: HostBinding,
args: ["attr.aria-checked"]
}], ariaDisabled: [{
type: HostBinding,
args: ["attr.aria-disabled"]
}], disabled: [{
type: Input
}, {
type: HostBinding,
args: ["class.cds--menu-item--disabled"]
}], danger: [{
type: Input
}, {
type: HostBinding,
args: ["class.cds--menu-item--danger"]
}], label: [{
type: Input
}], info: [{
type: Input
}], type: [{
type: Input
}], checked: [{
type: Input
}], icon: [{
type: Input
}], value: [{
type: Input
}], checkedChange: [{
type: Output
}], itemClick: [{
type: Output
}], childContextMenu: [{
type: ContentChild,
args: [ContextMenuComponent, { static: true }]
}], handleClick: [{
type: HostListener,
args: ["keydown.enter", ["$event"]]
}, {
type: HostListener,
args: ["keydown.space", ["$event"]]
}, {
type: HostListener,
args: ["click", ["$event"]]
}], handleMouseOver: [{
type: HostListener,
args: ["mouseover"]
}], handleMouseOut: [{
type: HostListener,
args: ["mouseout"]
}], handleFocus: [{
type: HostListener,
args: ["focus"]
}], handleBlur: [{
type: HostListener,
args: ["blur"]
}] } });
class ContextMenuModule {
}
ContextMenuModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ContextMenuModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
ContextMenuModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.3.0", ngImport: i0, type: ContextMenuModule, declarations: [ContextMenuDividerComponent,
ContextMenuGroupComponent,
ContextMenuItemComponent,
ContextMenuComponent], imports: [CommonModule, IconModule], exports: [ContextMenuDividerComponent,
ContextMenuGroupComponent,
ContextMenuItemComponent,
ContextMenuComponent] });
ContextMenuModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ContextMenuModule, imports: [CommonModule, IconModule] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ContextMenuModule, decorators: [{
type: NgModule,
args: [{
declarations: [
ContextMenuDividerComponent,
ContextMenuGroupComponent,
ContextMenuItemComponent,
ContextMenuComponent
],
exports: [
ContextMenuDividerComponent,
ContextMenuGroupComponent,
ContextMenuItemComponent,
ContextMenuComponent
],
imports: [CommonModule, IconModule]
}]
}] });
/**
* Generated bundle index. Do not edit.
*/
export { ContextMenuComponent, ContextMenuDividerComponent, ContextMenuGroupComponent, ContextMenuItemComponent, ContextMenuModule };
//# sourceMappingURL=carbon-components-angular-context-menu.mjs.map