UNPKG

carbon-components-angular

Version:
551 lines (542 loc) 24.1 kB
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