UNPKG

clarity-angular

Version:

Angular components for Clarity

1,603 lines (1,571 loc) 471 kB
import { ChangeDetectorRef, Component, ComponentFactoryResolver, ContentChild, ContentChildren, Directive, ElementRef, EventEmitter, HostBinding, HostListener, Inject, Injectable, InjectionToken, Injector, Input, IterableDiffers, NgModule, NgZone, Optional, Output, QueryList, Renderer2, SkipSelf, TemplateRef, ViewChild, ViewContainerRef, forwardRef } from '@angular/core'; import { CommonModule, DOCUMENT } from '@angular/common'; import { Subject as Subject$1 } from 'rxjs/Subject'; import { FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms'; import { BehaviorSubject as BehaviorSubject$1 } from 'rxjs/BehaviorSubject'; import 'rxjs/add/operator/map'; import { animate, state, style, transition, trigger } from '@angular/animations'; import { DOCUMENT as DOCUMENT$1 } from '@angular/platform-browser'; /* * Copyright (c) 2016 VMware, Inc. All Rights Reserved. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ class IconCustomTag { } // No behavior // The only purpose is to "declare" the tag in Angular IconCustomTag.decorators = [ { type: Directive, args: [{ selector: "clr-icon" },] }, ]; /** * @nocollapse */ IconCustomTag.ctorParameters = () => []; /* * Copyright (c) 2016 VMware, Inc. All Rights Reserved. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ const ICON_DIRECTIVES = [IconCustomTag]; /* * Copyright (c) 2016 VMware, Inc. All Rights Reserved. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ class ClrIconModule { } ClrIconModule.decorators = [ { type: NgModule, args: [{ imports: [CommonModule], declarations: [ICON_DIRECTIVES], exports: [ICON_DIRECTIVES] },] }, ]; /** * @nocollapse */ ClrIconModule.ctorParameters = () => []; /* * Copyright (c) 2017 VMware, Inc. All Rights Reserved. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ let Point = {}; Point.RIGHT_CENTER = 0; Point.RIGHT_TOP = 1; Point.RIGHT_BOTTOM = 2; Point.TOP_CENTER = 3; Point.TOP_RIGHT = 4; Point.TOP_LEFT = 5; Point.BOTTOM_CENTER = 6; Point.BOTTOM_RIGHT = 7; Point.BOTTOM_LEFT = 8; Point.LEFT_CENTER = 9; Point.LEFT_TOP = 10; Point.LEFT_BOTTOM = 11; Point[Point.RIGHT_CENTER] = "RIGHT_CENTER"; Point[Point.RIGHT_TOP] = "RIGHT_TOP"; Point[Point.RIGHT_BOTTOM] = "RIGHT_BOTTOM"; Point[Point.TOP_CENTER] = "TOP_CENTER"; Point[Point.TOP_RIGHT] = "TOP_RIGHT"; Point[Point.TOP_LEFT] = "TOP_LEFT"; Point[Point.BOTTOM_CENTER] = "BOTTOM_CENTER"; Point[Point.BOTTOM_RIGHT] = "BOTTOM_RIGHT"; Point[Point.BOTTOM_LEFT] = "BOTTOM_LEFT"; Point[Point.LEFT_CENTER] = "LEFT_CENTER"; Point[Point.LEFT_TOP] = "LEFT_TOP"; Point[Point.LEFT_BOTTOM] = "LEFT_BOTTOM"; const POSITION_RELATIVE = "relative"; const POSITION_ABSOLUTE = "absolute"; const POSITION_FIXED = "fixed"; const OVERFLOW_SCROLL = "scroll"; const OVERFLOW_AUTO = "auto"; class Popover { /** * @param {?} element */ constructor(element) { this.element = element; this.scrollableElements = []; this.boundOnScrollListener = this.emitScrollEvent.bind(this); // Browsers don't agree with what to do if some of these are not specified, so we set them all to be safe. element.style.position = POSITION_ABSOLUTE; element.style.top = 0; element.style.bottom = "auto"; element.style.left = 0; element.style.right = "auto"; } /** * @param {?} anchor * @param {?} anchorAlign * @param {?} popoverAlign * @param {?=} __3 * @return {?} */ anchor(anchor, anchorAlign, popoverAlign, { offsetX = 0, offsetY = 0, useAnchorParent = false } = {}) { // TODO: we are assuming here that the popover is inside or next to the anchor. // We'd need to go up the popover tree too otherwise this.addScrollEventListeners(anchor); if (useAnchorParent) { anchor = anchor.parentNode; } // explicitly override anchor's style to static anchor.style.position = "static"; const /** @type {?} */ anchorRect = anchor.getBoundingClientRect(); const /** @type {?} */ popoverRect = this.element.getBoundingClientRect(); // position of left top corner of anchor + the offset let /** @type {?} */ leftDiff = anchorRect.left - popoverRect.left + offsetX; let /** @type {?} */ topDiff = anchorRect.top - popoverRect.top + offsetY; // first, adjust positioning based on anchor's align point switch (anchorAlign) { case Point.LEFT_TOP: case Point.TOP_LEFT: break; case Point.TOP_CENTER: leftDiff += anchorRect.width / 2; break; case Point.TOP_RIGHT: leftDiff += anchorRect.width; break; case Point.RIGHT_TOP: leftDiff += anchorRect.width; break; case Point.LEFT_BOTTOM: topDiff += anchorRect.height; break; case Point.BOTTOM_LEFT: topDiff += anchorRect.height; break; case Point.BOTTOM_CENTER: topDiff += anchorRect.height; leftDiff += anchorRect.width / 2; break; case Point.BOTTOM_RIGHT: topDiff += anchorRect.height; leftDiff += anchorRect.width; break; case Point.RIGHT_BOTTOM: topDiff += anchorRect.height; leftDiff += anchorRect.width; break; case Point.LEFT_CENTER: topDiff += anchorRect.height / 2; break; case Point.RIGHT_CENTER: topDiff += anchorRect.height / 2; leftDiff += anchorRect.width; break; default: } // second, adjust positioning based on popover's align point switch (popoverAlign) { case Point.LEFT_TOP: case Point.TOP_LEFT: break; case Point.TOP_CENTER: leftDiff -= popoverRect.width / 2; break; case Point.TOP_RIGHT: leftDiff -= popoverRect.width; break; case Point.RIGHT_TOP: leftDiff -= popoverRect.width; break; case Point.LEFT_BOTTOM: topDiff -= popoverRect.height; break; case Point.BOTTOM_LEFT: topDiff -= popoverRect.height; break; case Point.BOTTOM_CENTER: topDiff -= popoverRect.height; leftDiff -= popoverRect.width / 2; break; case Point.BOTTOM_RIGHT: topDiff -= popoverRect.height; leftDiff -= popoverRect.width; break; case Point.RIGHT_BOTTOM: topDiff -= popoverRect.height; leftDiff -= popoverRect.width; break; case Point.LEFT_CENTER: topDiff -= popoverRect.height / 2; break; case Point.RIGHT_CENTER: topDiff -= popoverRect.height / 2; leftDiff -= popoverRect.width; break; default: } // Third, adjust with popover's margins based on the two align points. // Here, we make an assumption that popover is primarily positioned outside the // anchor with minor offset. Without this assumption, it's impossible to apply // the popover's margins in a predictable way. For example, assume that a popover // and its anchor are exactly the same size. if a popover is positioned inside the // anchor (which is technically possible), then it becomes impossible to know what to do // if the popover has a non-zero margin value all around (because applying the margin in // all four directions will result in no margin visually, which isn't what we want). // Therefore, our logic makes assumptions about margins of interest given the points, // and only covers the cases where popover is outside the anchor. const /** @type {?} */ popoverComputedStyle = getComputedStyle(this.element); const /** @type {?} */ marginLeft = parseInt(popoverComputedStyle.marginLeft, 10); const /** @type {?} */ marginRight = parseInt(popoverComputedStyle.marginRight, 10); const /** @type {?} */ marginTop = parseInt(popoverComputedStyle.marginTop, 10); const /** @type {?} */ marginBottom = parseInt(popoverComputedStyle.marginBottom, 10); switch (anchorAlign) { case Point.LEFT_TOP: case Point.TOP_LEFT: case Point.TOP_RIGHT: case Point.RIGHT_TOP: if (popoverAlign === Point.BOTTOM_RIGHT || popoverAlign === Point.RIGHT_BOTTOM) { topDiff -= marginBottom; leftDiff -= marginRight; } if (popoverAlign === Point.BOTTOM_LEFT || popoverAlign === Point.LEFT_BOTTOM) { topDiff -= marginTop; leftDiff += marginLeft; } if (popoverAlign === Point.TOP_LEFT || popoverAlign === Point.LEFT_TOP) { topDiff += marginTop; leftDiff += marginLeft; } if (popoverAlign === Point.TOP_RIGHT || popoverAlign === Point.RIGHT_TOP) { topDiff += marginTop; leftDiff -= marginRight; } break; case Point.LEFT_BOTTOM: case Point.BOTTOM_LEFT: case Point.BOTTOM_RIGHT: case Point.RIGHT_BOTTOM: if (popoverAlign === Point.BOTTOM_LEFT || popoverAlign === Point.LEFT_BOTTOM) { topDiff -= marginBottom; leftDiff += marginLeft; } if (popoverAlign === Point.BOTTOM_RIGHT || popoverAlign === Point.RIGHT_BOTTOM) { topDiff -= marginBottom; leftDiff -= marginRight; } if (popoverAlign === Point.TOP_LEFT || popoverAlign === Point.LEFT_TOP) { topDiff += marginTop; leftDiff += marginLeft; } if (popoverAlign === Point.TOP_RIGHT || popoverAlign === Point.RIGHT_TOP) { topDiff += marginTop; leftDiff -= marginRight; } break; case Point.TOP_CENTER: topDiff -= marginBottom; leftDiff += marginLeft; leftDiff -= marginRight; break; case Point.BOTTOM_CENTER: topDiff += marginTop; leftDiff += marginLeft; leftDiff -= marginRight; break; case Point.LEFT_CENTER: topDiff += marginTop; topDiff -= marginBottom; leftDiff -= marginRight; break; case Point.RIGHT_CENTER: topDiff += marginTop; topDiff -= marginBottom; leftDiff += marginLeft; break; default: } this.element.style.transform = `translateX(${leftDiff}px) translateY(${topDiff}px)`; return this._scroll.asObservable(); } /** * @return {?} */ release() { this.element.style.transform = ""; this.removeScrollEventListeners(); } /** * @param {?} container * @return {?} */ isPositioned(container) { const /** @type {?} */ position = getComputedStyle(container).position; return position === POSITION_RELATIVE || position === POSITION_ABSOLUTE || position === POSITION_FIXED; } /** * @return {?} */ emitScrollEvent() { this._scroll.next(); } /** * @param {?} e * @return {?} */ addScrollEventListeners(e) { this._scroll = new Subject$1(); const /** @type {?} */ anchor = e; let /** @type {?} */ current = e; while (current && current !== document) { if (this.scrolls(current)) { current.addEventListener("scroll", this.boundOnScrollListener); this.scrollableElements.push(current); } if (current !== anchor && this.isPositioned(current)) { break; } current = current.parentNode; } } /** * @return {?} */ removeScrollEventListeners() { for (const /** @type {?} */ elem of this.scrollableElements) { elem.removeEventListener("scroll", this.boundOnScrollListener); } this.scrollableElements.length = 0; if (this._scroll) { this._scroll.complete(); delete this._scroll; } } /** * @param {?} container * @return {?} */ scrolls(container) { const /** @type {?} */ computedStyles = getComputedStyle(container); return computedStyles.overflowX === OVERFLOW_SCROLL || computedStyles.overflowX === OVERFLOW_AUTO || computedStyles.overflowY === OVERFLOW_SCROLL || computedStyles.overflowY === OVERFLOW_AUTO; } } /* * Copyright (c) 2016-2017 VMware, Inc. All Rights Reserved. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ let openCount = 0; const waiting = []; class PopoverDirectiveOld { /** * @param {?} templateRef * @param {?} viewContainer */ constructor(templateRef, viewContainer) { this.templateRef = templateRef; this.viewContainer = viewContainer; this.popoverOptions = {}; this.clrPopoverOldChange = new EventEmitter(false); } /** * @param {?} open * @return {?} */ set clrPopoverOld(open) { if (open) { if (this.popoverOptions.allowMultipleOpen) { this.createPopover(); } else { if (openCount === 0) { this.createPopover(); } else { waiting.push(() => { this.createPopover(); }); } } } else { this.viewContainer.clear(); this.destroyPopover(); if (!this.popoverOptions.allowMultipleOpen) { if (waiting.length > 0) { const /** @type {?} */ createPopoverFn = waiting.shift(); createPopoverFn(); } } } } /** * @return {?} */ createPopover() { const /** @type {?} */ embeddedViewRef = (this.viewContainer.createEmbeddedView(this.templateRef)); // TODO: Not sure of the risks associated with using this. Find an alternative. // Needed for find the correct height and width of dynamically created views // inside of the popover. For Eg: Button Groups embeddedViewRef.detectChanges(); // filter out other nodes in the view ref so we are only left with element nodes const /** @type {?} */ elementNodes = embeddedViewRef.rootNodes.filter((node) => { return node.nodeType === 1; }); // we take the first element node in the embedded view; usually there should only be one anyways this._popoverInstance = new Popover(elementNodes[0]); this._subscription = this._popoverInstance.anchor(this.anchorElem, this.anchorPoint, this.popoverPoint, this.popoverOptions) .subscribe(() => { this.clrPopoverOldChange.emit(false); }); openCount++; } /** * @return {?} */ destroyPopover() { if (this._popoverInstance) { this._subscription.unsubscribe(); this._popoverInstance.release(); delete this._popoverInstance; openCount--; } } /** * @return {?} */ ngOnDestroy() { this.destroyPopover(); } } PopoverDirectiveOld.decorators = [ { type: Directive, args: [{ selector: "[clrPopoverOld]" },] }, ]; /** * @nocollapse */ PopoverDirectiveOld.ctorParameters = () => [ { type: TemplateRef, }, { type: ViewContainerRef, }, ]; PopoverDirectiveOld.propDecorators = { 'anchorElem': [{ type: Input, args: ["clrPopoverOldAnchor",] },], 'anchorPoint': [{ type: Input, args: ["clrPopoverOldAnchorPoint",] },], 'popoverPoint': [{ type: Input, args: ["clrPopoverOldPopoverPoint",] },], 'popoverOptions': [{ type: Input, args: ["clrPopoverOldOptions",] },], 'clrPopoverOldChange': [{ type: Output, args: ["clrPopoverOldChange",] },], 'clrPopoverOld': [{ type: Input },], }; /* * Copyright (c) 2017 VMware, Inc. All Rights Reserved. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ const POPOVER_DIRECTIVES = [PopoverDirectiveOld]; /** * Copyright (c) 2016-2017 VMware, Inc. All Rights Reserved. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ class ClrCommonPopoverModule { } ClrCommonPopoverModule.decorators = [ { type: NgModule, args: [{ imports: [CommonModule], declarations: [POPOVER_DIRECTIVES], exports: [POPOVER_DIRECTIVES] },] }, ]; /** * @nocollapse */ ClrCommonPopoverModule.ctorParameters = () => []; /* * Copyright (c) 2016 VMware, Inc. All Rights Reserved. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ class ButtonInGroupService { constructor() { this._changes = new Subject$1(); } /** * @return {?} */ get changes() { return this._changes.asObservable(); } /** * @param {?} button * @return {?} */ updateButtonGroup(button) { this._changes.next(button); } } ButtonInGroupService.decorators = [ { type: Injectable }, ]; /** * @nocollapse */ ButtonInGroupService.ctorParameters = () => []; /* * Copyright (c) 2016 VMware, Inc. All Rights Reserved. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ class Button { /** * @param {?} buttonInGroupService */ constructor(buttonInGroupService) { this.buttonInGroupService = buttonInGroupService; this._enableService = false; this._inMenu = false; this._classNames = "btn"; this._name = null; this._type = null; this._disabled = null; this._click = new EventEmitter(false); } /** * @return {?} */ get inMenu() { return this._inMenu; } /** * @param {?} value * @return {?} */ set inMenu(value) { value = !!value; if (this._inMenu !== value) { this._inMenu = value; // We check if the service flag is enabled // and if the service exists because the service is optional if (this._enableService && this.buttonInGroupService) { this.buttonInGroupService.updateButtonGroup(this); } } } /** * @return {?} */ get classNames() { return this._classNames; } /** * @param {?} value * @return {?} */ set classNames(value) { if (typeof value === "string") { const /** @type {?} */ classNames = value.split(" "); if (classNames.indexOf("btn") === -1) { classNames.push("btn"); } this._classNames = classNames.join(" "); } } /** * @return {?} */ get name() { return this._name; } /** * @param {?} value * @return {?} */ set name(value) { if (typeof value === "string") { this._name = value; } } /** * @return {?} */ get type() { return this._type; } /** * @param {?} value * @return {?} */ set type(value) { if (typeof value === "string") { this._type = value; } } /** * @return {?} */ get disabled() { return this._disabled; } /** * @param {?} value * @return {?} */ set disabled(value) { if (value !== null && value !== false) { this._disabled = ""; } else { this._disabled = null; } } /** * @return {?} */ emitClick() { this._click.emit(true); } /** * @return {?} */ ngAfterViewInit() { this._enableService = true; } } Button.decorators = [ { type: Component, args: [{ selector: "clr-button", template: ` <ng-template #buttonProjectedRef> <button [class]="classNames" (click)="emitClick()" [attr.type]="type" [attr.name]="name" [attr.disabled]="disabled"> <ng-content></ng-content> </button> </ng-template> ` },] }, ]; /** * @nocollapse */ Button.ctorParameters = () => [ { type: ButtonInGroupService, decorators: [{ type: SkipSelf }, { type: Optional },] }, ]; Button.propDecorators = { 'templateRef': [{ type: ViewChild, args: ["buttonProjectedRef",] },], 'inMenu': [{ type: Input, args: ["clrInMenu",] },], 'classNames': [{ type: Input, args: ["class",] },], 'name': [{ type: Input, args: ["name",] },], 'type': [{ type: Input, args: ["type",] },], 'disabled': [{ type: Input, args: ["disabled",] },], '_click': [{ type: Output, args: ["click",] },], }; /* * Copyright (c) 2016-2017 VMware, Inc. All Rights Reserved. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ /* * Copyright (c) 2016-2017 VMware, Inc. All Rights Reserved. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ const menuPositions = ["bottom-left", "bottom-right", "top-left", "top-right", "left-bottom", "left-top", "right-bottom", "right-top"]; /* * Copyright (c) 2016 VMware, Inc. All Rights Reserved. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ class ButtonGroup { /** * @param {?} buttonGroupNewService * @param {?} elementRef */ constructor(buttonGroupNewService, elementRef) { this.buttonGroupNewService = buttonGroupNewService; this.elementRef = elementRef; this.inlineButtons = []; this.menuButtons = []; this._openMenu = false; this.anchorPoint = Point.BOTTOM_LEFT; this.popoverPoint = Point.LEFT_TOP; /** * Flag with indicates if the overflow menu toggle was clicked. * If true, this can save us traversing the DOM to find * whether the click was withing the button group toggle * or menu in the onMouseClick method */ this._overflowMenuToggleClicked = false; } /** * 1. Initializes the initial Button Group View * 2. Subscribes to changes on the ContentChildren * in case the user content projection changes * @return {?} */ ngAfterContentInit() { this.initializeButtons(); this.buttonGroupNewService.changes.subscribe(button => this.rearrangeButton(button)); this.buttons.changes.subscribe(() => { this.initializeButtons(); }); } /** * Moves the button into the other ViewContainer * when an update is received. * * @param {?} button * @return {?} */ rearrangeButton(button) { let /** @type {?} */ fromView; let /** @type {?} */ toView; if (button.inMenu) { fromView = this.inlineButtons; toView = this.menuButtons; } else { fromView = this.menuButtons; toView = this.inlineButtons; } const /** @type {?} */ index = fromView.indexOf(button); if (index > -1) { fromView.splice(index, 1); const /** @type {?} */ moveIndex = this.getMoveIndex(button); if (moveIndex <= toView.length) { toView.splice(moveIndex, 0, button); } } } /** * Author: Eudes * * Finds the order of a button w.r.t other buttons * * @param {?} buttonToMove * @return {?} */ getMoveIndex(buttonToMove) { const /** @type {?} */ tempArr = this.buttons.filter(button => (button.inMenu === buttonToMove.inMenu)); return tempArr.indexOf(buttonToMove); } /** * Finds where each button belongs based on * the ContentChildren * @return {?} */ initializeButtons() { const /** @type {?} */ tempInlineButtons = []; const /** @type {?} */ tempInMenuButtons = []; this.buttons.forEach((button) => { if (button.inMenu) { tempInMenuButtons.push(button); } else { tempInlineButtons.push(button); } }); this.inlineButtons = tempInlineButtons; this.menuButtons = tempInMenuButtons; } /** * @return {?} */ get menuPosition() { return this._menuPosition; } /** * @param {?} pos * @return {?} */ set menuPosition(pos) { if (pos && (menuPositions.indexOf(pos) > -1)) { this._menuPosition = pos; } else { this._menuPosition = "bottom-left"; } // set the popover values based on menu position switch (this._menuPosition) { case ("top-right"): this.anchorPoint = Point.TOP_RIGHT; this.popoverPoint = Point.RIGHT_BOTTOM; break; case ("top-left"): this.anchorPoint = Point.TOP_LEFT; this.popoverPoint = Point.LEFT_BOTTOM; break; case ("bottom-right"): this.anchorPoint = Point.BOTTOM_RIGHT; this.popoverPoint = Point.RIGHT_TOP; break; case ("bottom-left"): this.anchorPoint = Point.BOTTOM_LEFT; this.popoverPoint = Point.LEFT_TOP; break; case ("right-top"): this.anchorPoint = Point.RIGHT_TOP; this.popoverPoint = Point.LEFT_TOP; break; case ("right-bottom"): this.anchorPoint = Point.RIGHT_BOTTOM; this.popoverPoint = Point.LEFT_BOTTOM; break; case ("left-top"): this.anchorPoint = Point.LEFT_TOP; this.popoverPoint = Point.RIGHT_TOP; break; case ("left-bottom"): this.anchorPoint = Point.LEFT_BOTTOM; this.popoverPoint = Point.RIGHT_BOTTOM; break; default: this.anchorPoint = Point.BOTTOM_LEFT; this.popoverPoint = Point.LEFT_TOP; break; } } /** * @return {?} */ get openMenu() { return this._openMenu; } /** * @param {?} value * @return {?} */ set openMenu(value) { this._openMenu = value; } /** * Toggle the Dropdown Menu when the Dropdown Toggle is * clicked. Also set a flag that indicates that the toggle * was clicked so that we don't traverse the DOM to find the * location of the click. * @return {?} */ toggleMenu() { this.openMenu = !this.openMenu; this._overflowMenuToggleClicked = true; } /** * Called on mouse clicks anywhere in the DOM. * Checks to see if the mouseclick happened on the host or outside * @param {?} target * @return {?} */ onMouseClick(target) { if (this.openMenu && !this._overflowMenuToggleClicked) { // Reset the overflow menu toggle clicked flag this._overflowMenuToggleClicked = false; let /** @type {?} */ current = target; // Get the element in the DOM on which the mouse was clicked const /** @type {?} */ host = this.elementRef.nativeElement; // Current Button Group if (current.classList.contains("dropdown-menu")) { current = current.parentNode; while (current) { if (current === document) { this.openMenu = false; return; } // If clicked on dropdown menu and menu is in host // do nothing if (current === host) { return; } current = current.parentNode; } } this.openMenu = false; } this._overflowMenuToggleClicked = false; // Reset the overflow menu toggle clicked flag } } ButtonGroup.decorators = [ { type: Component, args: [{ selector: "clr-button-group", template: ` <ng-container *ngFor="let inlineButton of inlineButtons"> <ng-template [ngTemplateOutlet]="inlineButton.templateRef"></ng-template> </ng-container> <ng-container *ngIf="menuButtons.length > 0"> <div class="btn-group-overflow open" [ngClass]="menuPosition" #anchor> <button class="btn dropdown-toggle" (click)="toggleMenu()"> <clr-icon shape="ellipsis-horizontal"></clr-icon> </button> <div class="dropdown-menu" *clrPopoverOld="openMenu; anchor: anchor; anchorPoint: anchorPoint; popoverPoint: popoverPoint;"> <ng-template [ngTemplateOutlet]="ref"></ng-template> </div> </div> </ng-container> <ng-template #ref> <ng-container *ngFor="let menuButton of menuButtons"> <ng-template [ngTemplateOutlet]="menuButton.templateRef"></ng-template> </ng-container> </ng-template> `, providers: [ButtonInGroupService], host: { "[class.btn-group]": "true" } },] }, ]; /** * @nocollapse */ ButtonGroup.ctorParameters = () => [ { type: ButtonInGroupService, }, { type: ElementRef, }, ]; ButtonGroup.propDecorators = { 'buttons': [{ type: ContentChildren, args: [Button,] },], 'menuPosition': [{ type: Input, args: ["clrMenuPosition",] },], 'onMouseClick': [{ type: HostListener, args: ["document:click", ["$event.target"],] },], }; /** * Copyright (c) 2016-2017 VMware, Inc. All Rights Reserved. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ const BUTTON_GROUP_DIRECTIVES = [Button, ButtonGroup]; /** * Copyright (c) 2016-2017 VMware, Inc. All Rights Reserved. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ class ClrButtonGroupModule { } ClrButtonGroupModule.decorators = [ { type: NgModule, args: [{ imports: [CommonModule, ClrIconModule, ClrCommonPopoverModule], declarations: [BUTTON_GROUP_DIRECTIVES], exports: [BUTTON_GROUP_DIRECTIVES] },] }, ]; /** * @nocollapse */ ClrButtonGroupModule.ctorParameters = () => []; /** * This is an abstract class because we need it to still be a valid token for dependency injection after transpiling. * This does not mean you should extend it, simply implementing it is fine. * @abstract */ class LoadingListener { /** * @abstract * @return {?} */ startLoading() { } /** * @abstract * @return {?} */ doneLoading() { } } /* * Copyright (c) 2016 VMware, Inc. All Rights Reserved. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ class Loading { /** * @param {?} listener */ constructor(listener) { this.listener = listener; this._loading = false; } /** * @return {?} */ get loading() { return this._loading; } /** * @param {?} value * @return {?} */ set loading(value) { value = !!value; if (value === this._loading) { return; } this._loading = value; if (this.listener) { if (value) { this.listener.startLoading(); } else { this.listener.doneLoading(); } } } /** * @return {?} */ ngOnDestroy() { this.loading = false; } } Loading.decorators = [ { type: Directive, args: [{ selector: "[clrLoading]" },] }, ]; /** * @nocollapse */ Loading.ctorParameters = () => [ { type: LoadingListener, decorators: [{ type: Optional },] }, ]; Loading.propDecorators = { 'loading': [{ type: Input, args: ["clrLoading",] },], }; /* * Copyright (c) 2016 VMware, Inc. All Rights Reserved. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ const LOADING_DIRECTIVES = [Loading]; /** * Copyright (c) 2016-2017 VMware, Inc. All Rights Reserved. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ class ClrLoadingModule { } ClrLoadingModule.decorators = [ { type: NgModule, args: [{ imports: [CommonModule], declarations: [LOADING_DIRECTIVES], exports: [LOADING_DIRECTIVES] },] }, ]; /** * @nocollapse */ ClrLoadingModule.ctorParameters = () => []; /* * Copyright (c) 2017 VMware, Inc. All Rights Reserved. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ class LoadingButton { /** * @return {?} */ startLoading() { this.loading = true; } /** * @return {?} */ doneLoading() { this.loading = false; } } LoadingButton.decorators = [ { type: Component, args: [{ selector: "button[clrLoading]", template: ` <span class="spinner spinner-inline" *ngIf="loading"></span> <ng-content></ng-content> `, providers: [{ provide: LoadingListener, useExisting: LoadingButton }] },] }, ]; /** * @nocollapse */ LoadingButton.ctorParameters = () => []; /* * Copyright (c) 2017 VMware, Inc. All Rights Reserved. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ const LOADING_BUTTON_DIRECTIVES = [LoadingButton]; /** * Copyright (c) 2016-2017 VMware, Inc. All Rights Reserved. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ class ClrLoadingButtonModule { } ClrLoadingButtonModule.decorators = [ { type: NgModule, args: [{ imports: [CommonModule, ClrLoadingModule], declarations: [LOADING_BUTTON_DIRECTIVES], exports: [LOADING_BUTTON_DIRECTIVES, ClrLoadingModule] },] }, ]; /** * @nocollapse */ ClrLoadingButtonModule.ctorParameters = () => []; /** * Copyright (c) 2016-2017 VMware, Inc. All Rights Reserved. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ class ClrButtonModule { } ClrButtonModule.decorators = [ { type: NgModule, args: [{ exports: [ ClrLoadingButtonModule, ClrButtonGroupModule, ] },] }, ]; /** * @nocollapse */ ClrButtonModule.ctorParameters = () => []; /* * Copyright (c) 2016 VMware, Inc. All Rights Reserved. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ class CodeHighlight { /** * @param {?} _el * @param {?} renderer */ constructor(_el, renderer) { this._el = _el; this.renderer = renderer; this._highlight = ""; } /** * @return {?} */ ngAfterContentInit() { this.redraw(); } /** * @return {?} */ redraw() { if (this._el && this._el.nativeElement) { Prism.highlightElement(this._el.nativeElement); } } /** * @param {?} val * @return {?} */ set highlight(val) { if (val && val.trim() !== "") { this._highlight = val; this.renderer.addClass(this._el.nativeElement, this._highlight); } } /** * @return {?} */ get highlight() { return this._highlight; } } CodeHighlight.decorators = [ { type: Directive, args: [{ selector: "code[clr-code-highlight]" },] }, ]; /** * @nocollapse */ CodeHighlight.ctorParameters = () => [ { type: ElementRef, }, { type: Renderer2, }, ]; CodeHighlight.propDecorators = { 'highlight': [{ type: Input, args: ["clr-code-highlight",] },], }; /* * Copyright (c) 2016 VMware, Inc. All Rights Reserved. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ const CODE_HIGHLIGHT_DIRECTIVES = [CodeHighlight]; /** * Copyright (c) 2016-2017 VMware, Inc. All Rights Reserved. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ class ClrSyntaxHighlightModule { } ClrSyntaxHighlightModule.decorators = [ { type: NgModule, args: [{ imports: [CommonModule], declarations: [CODE_HIGHLIGHT_DIRECTIVES], exports: [CODE_HIGHLIGHT_DIRECTIVES] },] }, ]; /** * @nocollapse */ ClrSyntaxHighlightModule.ctorParameters = () => []; /** * Copyright (c) 2016-2017 VMware, Inc. All Rights Reserved. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ class ClrCodeModule { } ClrCodeModule.decorators = [ { type: NgModule, args: [{ exports: [ClrSyntaxHighlightModule] },] }, ]; /** * @nocollapse */ ClrCodeModule.ctorParameters = () => []; /* * Copyright (c) 2016 VMware, Inc. All Rights Reserved. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ /** * Private counter to generate unique IDs for the checkboxes, to bind the labels to them. */ let latestId = 0; class Checkbox { constructor() { // If our host has an ID attribute, we use this instead of our index. this._id = (latestId++).toString(); this.name = null; this.disabled = false; this.inline = false; this._checked = false; this._indeterminate = false; this.indeterminateChange = new EventEmitter(false); this.change = new EventEmitter(false); this.onChangeCallback = (_) => { }; this.onTouchedCallback = () => { }; } /** * @return {?} */ get id() { return `clr-checkbox-${this._id}`; } /** * @return {?} */ get checked() { return this._checked; } /** * @param {?} value * @return {?} */ set checked(value) { if (value !== this._checked) { if (this._indeterminate) { this.setIndeterminate(false); } this.setChecked(value); } } /** * @return {?} */ get indeterminate() { return this._indeterminate; } /** * @param {?} value * @return {?} */ set indeterminate(value) { if (this._indeterminate !== value) { if (this._checked) { this.setChecked(false); } this.setIndeterminate(value); } } /** * @param {?} value * @return {?} */ setIndeterminate(value) { this._indeterminate = value; this.indeterminateChange.emit(this._indeterminate); } /** * @param {?} value * @return {?} */ setChecked(value) { this._checked = value; this.change.emit(this._checked); } /** * @return {?} */ toggle() { this.checked = !this.checked; this.onChangeCallback(this.checked); } /** * @param {?} value * @return {?} */ writeValue(value) { if (value === null) { value = false; } if (value !== this.checked) { this.checked = value; } } /** * @param {?} onChange * @return {?} */ registerOnChange(onChange) { this.onChangeCallback = onChange; } /** * @param {?} onTouched * @return {?} */ registerOnTouched(onTouched) { this.onTouchedCallback = onTouched; } /** * @return {?} */ touch() { this.onTouchedCallback(); } /** * @return {?} */ checkIndeterminateState() { if (!this.disabled) { this.toggle(); } } } Checkbox.decorators = [ { type: Component, args: [{ selector: "clr-checkbox", template: ` <!-- FIXME: We are not subscribed to the change event but the click event here. The reason for that is because checkboxes behave differently on IE & Edge. https://stackoverflow.com/a/19447939 To fix that, we listen to every click event and then toggle the checkbox manually to make it behave the same way across the browsers we support. This works for cases when users toggle the checkbox using the keyboard too: https://stackoverflow.com/questions/27878940/spacebar-triggering-click-event-on-checkbox --> <input type="checkbox" [id]="id" [name]="name" [checked]="checked" [indeterminate]="indeterminate" [disabled]="disabled" (blur)="touch()" (click)="checkIndeterminateState()"> <label [attr.for]="id"> <ng-content></ng-content> </label> `, host: { "[class.checkbox]": "!inline", "[class.checkbox-inline]": "inline", "[class.disabled]": "disabled" }, /* * This provider lets us declare our checkbox as a ControlValueAccessor, * which allows us to use [(ngModel)] directly on our component, * with all the automatic features wiring that come with it. */ providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => Checkbox), multi: true }] },] }, ]; /** * @nocollapse */ Checkbox.ctorParameters = () => []; Checkbox.propDecorators = { '_id': [{ type: Input, args: ["id",] },], 'name': [{ type: Input, args: ["name",] },], 'disabled': [{ type: Input, args: ["clrDisabled",] },], 'inline': [{ type: Input, args: ["clrInline",] },], 'checked': [{ type: Input, args: ["clrChecked",] },], 'indeterminate': [{ type: Input, args: ["clrIndeterminate",] },], 'indeterminateChange': [{ type: Output, args: ["clrIndeterminateChange",] },], 'change': [{ type: Output, args: ["clrCheckedChange",] },], }; /* * Copyright (c) 2016 VMware, Inc. All Rights Reserved. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ const CHECKBOX_DIRECTIVES = [Checkbox]; /** * Copyright (c) 2016-2017 VMware, Inc. All Rights Reserved. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ class ClrFormsModule { } ClrFormsModule.decorators = [ { type: NgModule, args: [{ imports: [CommonModule], declarations: [CHECKBOX_DIRECTIVES], exports: [CHECKBOX_DIRECTIVES] },] }, ]; /** * @nocollapse */ ClrFormsModule.ctorParameters = () => []; /* * Copyright (c) 2016-2017 VMware, Inc. All Rights Reserved. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ class Expand { constructor() { this.expandable = 0; this.replace = false; this._loading = false; this._expanded = false; this._animate = new Subject$1(); this._expandChange = new Subject$1(); } /** * @return {?} */ get loading() { return this._loading; } /** * @param {?} value * @return {?} */ set loading(value) { value = !!value; if (value !== this._loading) { this._loading = value; } } /** * @return {?} */ get expanded() { return this._expanded; } /** * @param {?} value * @return {?} */ set expanded(value) { value = !!value; if (value !== this._expanded) { this._expanded = value; this._animate.next(); this._expandChange.next(value); } } /** * @return {?} */ get animate() { return this._animate.asObservable(); } /** * @return {?} */ get expandChange() { return this._expandChange.asObservable(); } /** * @return {?} */ startLoading() { this.loading = true; } /** * @return {?} */ doneLoading() { this.loading = false; this._animate.next(); } } Expand.decorators = [ { type: Injectable }, ]; /** * @nocollapse */ Expand.ctorParameters = () => []; /* * Copyright (c) 2016-2017 VMware, Inc. All Rights Reserved. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ /** * TODO: make this a reusable directive outside of Datagrid, like [clrLoading]. */ class IfExpanded { /** * @param {?} template * @param {?} container * @param {?} expand */ constructor(template, container, expand) { this.template = template; this.container = container; this.expand = expand; this._expanded = false; this.expandedChange = new EventEmitter(true); /** * Subscriptions to all the services and queries changes */ this._subscriptions = []; expand.expandable++; this._subscriptions.push(expand.expandChange.subscribe(() => { this.updateView(); this.expandedChange.emit(this.expand.expanded); })); } /** * @return {?} */ get expanded() { return this._expanded; } /** * @param {?} value * @return {?} */ set expanded(value) { if (typeof value === "boolean") { this.expand.expanded = value; this._expanded = value; } } /** * @return {?} */ updateView() { if (this.expand.expanded && this.container.length !== 0) { return; } if (this.expand.expanded) { // Should we pass a context? I don't see anything useful to pass right now, // but we can come back to it in the future as a solution for additional features. this.container.createEmbeddedView(this.template); } else { // TODO: Move when we move the animation logic to Datagrid Row Expand // We clear before the animation is over. Not ideal, but doing better would involve a much heavier // process for very little gain. Once Angular animations are dynamic enough, we should be able to // get the optimal behavior. this.container.clear(); } } /** * @return {?} */ ngOnInit() { this.updateView(); } /** * @return {?} */ ngOnDestroy() { this.expand.expandable--; this._subscriptions.forEach((sub) => sub.unsubscribe()); } } IfExpanded.decorators = [ { type: Directive, args: [{ selector: "[clrIfExpanded]" },] }, ]; /** * @nocollapse */ IfExpanded.ctorParameters = () => [ { type: TemplateRef, }, { type: ViewContainerRef, }, { type: Expand, }, ]; IfExpanded.propDecorators = { 'expan