UNPKG

gentics-ui-core

Version:

This is the common core framework for the Gentics CMS and Mesh UI, and other Angular applications.

945 lines (928 loc) 363 kB
import 'hammerjs'; import * as i1 from '@angular/common'; import { CommonModule } from '@angular/common'; import * as i0 from '@angular/core'; import { InjectionToken, Injectable, Directive, EventEmitter, Component, ChangeDetectionStrategy, Input, Output, ViewChildren, ViewChild, HostListener, forwardRef, HostBinding, ElementRef, ContentChildren, Inject, Optional, SkipSelf, TemplateRef, ContentChild, ViewContainerRef, Pipe, Self, Attribute, NgModule } from '@angular/core'; import * as i5 from '@angular/forms'; import { NG_VALUE_ACCESSOR, FormsModule, ReactiveFormsModule } from '@angular/forms'; import * as i3 from '@angular/router'; import { RouterLinkWithHref, RouterModule } from '@angular/router'; import * as i1$3 from 'ngx-autosize'; import { AutosizeModule } from 'ngx-autosize'; import { Subscription, BehaviorSubject, timer, NEVER, of, Subject, Observable, merge, combineLatest, ObjectUnsubscribedError } from 'rxjs'; import { debounceTime, take, concat, filter, mapTo, map, switchMap, startWith } from 'rxjs/operators'; import * as moment_ from 'moment'; import * as rome_ from '@bevacqua/rome'; import * as momentum from '@bevacqua/rome/src/momentum'; import * as i1$1 from '@angular/platform-browser'; import { HammerModule } from '@angular/platform-browser'; import * as i1$2 from '@angular/animations'; import { animate, keyframes, style, trigger, state, transition, query, animateChild } from '@angular/animations'; import * as Sortable from 'sortablejs'; /* Default values */ const defaultConfig = { dropDownPageMargin: 50, dropDownMaxHeight: 650 }; const ConfigService = new InjectionToken('ConfigService'); const CustomConfig = new InjectionToken('CustomConfig'); const PredefinedConfig = new InjectionToken('PredefinedConfig'); function configFactory(initConfig, configValue) { return configValue instanceof Function ? Object.assign(Object.assign({}, initConfig), configValue()) : Object.assign(Object.assign({}, initConfig), configValue); } class UserAgentRef { constructor() { const window = UserAgentRef._window; this.isIE11 = !!(window.MSInputMethodContext && window.document.documentMode); this.isEdge = !!(window.navigator.userAgent.indexOf('Edge') > -1); } } UserAgentRef._window = window; /** @nocollapse */ UserAgentRef.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: UserAgentRef, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); /** @nocollapse */ UserAgentRef.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: UserAgentRef }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: UserAgentRef, decorators: [{ type: Injectable }], ctorParameters: function () { return []; } }); class Icon { } /** @nocollapse */ Icon.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: Icon, deps: [], target: i0.ɵɵFactoryTarget.Directive }); /** @nocollapse */ Icon.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "13.3.8", type: Icon, selector: "icon", ngImport: i0 }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: Icon, decorators: [{ type: Directive, args: [{ selector: 'icon' }] }] }); /** The width configured in the .ellipsis CSS class. */ const ELLIPSIS_WIDTH = 13; /** * A Breadcrumbs navigation component. * * ```html * <gtx-breadcrumbs></gtx-breadcrumbs> * ``` */ class Breadcrumbs { constructor(changeDetector, elementRef, userAgent) { this.changeDetector = changeDetector; this.elementRef = elementRef; this.userAgent = userAgent; /** * Fires when a link is clicked */ this.linkClick = new EventEmitter(); /** * Fires when the expand button is clicked */ this.multilineExpandedChange = new EventEmitter(); this.isMultiline = false; this.isMultilineExpanded = false; this.isDisabled = false; this.isOverflowing = false; this.showArrow = false; this.subscriptions = new Subscription(); this.resizeEvents = new BehaviorSubject(null); this.preventClicksWhenDisabled = (ev) => { if (this.isDisabled) { let target = ev.target; if (target.tagName.toLowerCase() === 'a' && target.classList.contains('breadcrumb')) { ev.preventDefault(); ev.stopImmediatePropagation(); } } }; } /** * If true the first folder and all the folder names from the end of the breadcrumbs, which fit into one line are shown * and an ellipsis in between. */ get multiline() { return this.isMultiline; } set multiline(val) { this.isMultiline = val != undefined && val !== false; } /** * If true the breadcrumbs are always expanded */ get multilineExpanded() { return this.isMultilineExpanded; } set multilineExpanded(val) { this.isMultilineExpanded = val != undefined && val !== false; } /** * Controls whether the navigation is disabled. */ get disabled() { return this.isDisabled; } set disabled(val) { this.isDisabled = val != undefined && val !== false; } ngAfterViewInit() { let element = this.elementRef.nativeElement; if (element) { // Listen in the "capture" phase to prevent routerLinks when disabled element.firstElementChild.addEventListener('click', this.preventClicksWhenDisabled, true); element.style.setProperty('--collapsedColor', this.collapsedColor); } const timerSub = timer(500, 500) .subscribe(() => this.resizeEvents.next()); this.subscriptions.add(timerSub); this.setUpResizeSub(); this.preventDisabledRouterLinks(); this.routerLinkChildren.changes.subscribe(() => this.preventDisabledRouterLinks()); this.resizeEvents.next(null); } ngOnChanges(changes) { if (changes['links'] || changes['routerLinks']) { let allLinks = (this.links || []).concat(this.routerLinks || []); this.backLink = allLinks[allLinks.length - 2]; this.resizeEvents.next(null); } if (changes['multiline'] || changes['multilineExpanded']) { this.resizeEvents.next(null); } } ngOnDestroy() { let element = this.elementRef.nativeElement; element.firstElementChild.removeEventListener('click', this.preventClicksWhenDisabled, true); this.subscriptions.unsubscribe(); } onLinkClicked(link, event) { if (this.isDisabled) { event.preventDefault(); event.stopImmediatePropagation(); } else { this.linkClick.emit(link); } } toggleMultilineExpanded() { this.multilineExpanded = !this.multilineExpanded; this.multilineExpandedChange.emit(this.multilineExpanded); this.resizeEvents.next(null); this.changeDetector.markForCheck(); } setUpResizeSub() { let prevLinks; let prevRouterLinks; let prevIsExpanded; let prevNavWidth = -1; const resizeSub = this.resizeEvents .pipe(debounceTime(5)) .subscribe(() => { if (!this.lastPart || !this.navWrapper) { return; } // If neither the links, nor isMultilineExpanded, nor the navWrapper element's clientWidth has changed, we don't need to do anything. const currNavWidth = this.navWrapper.nativeElement.clientWidth; if (prevLinks === this.links && prevRouterLinks === this.routerLinks && prevIsExpanded === this.isMultilineExpanded && prevNavWidth === currNavWidth) { return; } prevLinks = this.links; prevRouterLinks = this.routerLinks; prevIsExpanded = this.isMultilineExpanded; prevNavWidth = currNavWidth; const elements = this.lastPart.nativeElement.querySelectorAll('a.breadcrumb'); if (elements.length > 0) { const firstOffsetBottom = elements[0].offsetTop + elements[0].offsetHeight; const lastOffsetBottom = elements[elements.length - 1].offsetTop + elements[elements.length - 1].offsetHeight; this.showArrow = firstOffsetBottom !== lastOffsetBottom; } else { this.showArrow = false; } this.shortenTexts(); this.changeDetector.markForCheck(); }); this.subscriptions.add(resizeSub); } shortenTexts() { const navWrapper = this.navWrapper.nativeElement; const lastPart = this.lastPart.nativeElement; const innerElements = lastPart.querySelectorAll('a.breadcrumb'); const defaultElements = this.getCuttableBreadcrumbsTexts(); this.isOverflowing = false; // Reset all elements to their default states. const offset = this.multilineExpanded ? 0 : 1; for (let i = 0; i < innerElements.length; i++) { const innerElement = innerElements[i]; innerElement.classList.remove('without'); innerElement.classList.remove('hidden'); innerElement.textContent = defaultElements[i + offset]; } if (this.multilineExpanded) { return; } for (let i = 0; i < innerElements.length; ++i) { const innerElement = innerElements[i]; while (lastPart.offsetLeft + lastPart.scrollWidth + ELLIPSIS_WIDTH > navWrapper.clientWidth) { this.isOverflowing = true; if (innerElement.textContent.length === 0) { innerElement.classList.add('hidden'); const nextInnerElement = innerElements[i + 1]; if (nextInnerElement) { nextInnerElement.classList.add('without'); } break; } else { innerElement.textContent = innerElement.textContent.substring(1); } } } } getCuttableBreadcrumbsTexts() { let defaultBreadcrumbs = []; if (this.links) { for (let i = 0; i < this.links.length; i++) { defaultBreadcrumbs.push(this.links[i].text); } } if (this.routerLinks) { for (let i = 0; i < this.routerLinks.length; i++) { defaultBreadcrumbs.push(this.routerLinks[i].text); } } return defaultBreadcrumbs; } onResize(event) { this.resizeEvents.next(null); } /** * Workaround/Hack for the native angular "RouterLink" having no way to disable navigation on click. */ preventDisabledRouterLinks() { const thisComponent = this; const createsCompileErrorIfRouterLinkAPIChanges = 'onClick'; for (const link of this.routerLinkChildren.filter(link => !link.hasOwnProperty('onClick'))) { const originalOnClick = link.onClick; link.onClick = function interceptedOnClick(...args) { if (thisComponent.isDisabled) { return true; } else { return originalOnClick.apply(this, args); } }; } } } /** @nocollapse */ Breadcrumbs.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: Breadcrumbs, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.ElementRef }, { token: UserAgentRef }], target: i0.ɵɵFactoryTarget.Component }); /** @nocollapse */ Breadcrumbs.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.8", type: Breadcrumbs, selector: "gtx-breadcrumbs", inputs: { links: "links", routerLinks: "routerLinks", collapsedColor: "collapsedColor", multiline: "multiline", multilineExpanded: "multilineExpanded", disabled: "disabled" }, outputs: { linkClick: "linkClick", multilineExpandedChange: "multilineExpandedChange" }, viewQueries: [{ propertyName: "navWrapper", first: true, predicate: ["navWrapper"], descendants: true }, { propertyName: "lastPart", first: true, predicate: ["lastPart"], descendants: true }, { propertyName: "routerLinkChildren", predicate: RouterLinkWithHref, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<nav [class.disabled]=\"isDisabled\" [class.multiline]=\"multiline\">\n <div #navWrapper class=\"nav-wrapper\" [class.is-overflowing]=\"isOverflowing\" [class.multilineExpanded]=\"multilineExpanded\" [class.multiline]=\"multiline\" (window:resize)=\"onResize($event)\">\n <div class=\"inner-wrapper\">\n <a class=\"back-button\" *ngIf=\"backLink && backLink.route\"\n (click)=\"onLinkClicked(backLink, $event)\"\n [routerLink]=\"backLink.route\"\n [title]=\"backLink.text\"></a>\n <a class=\"back-button\" *ngIf=\"backLink && !backLink.route\"\n (click)=\"onLinkClicked(backLink, $event)\"\n [attr.href]=\"isDisabled ? null : backLink?.href\"\n [title]=\"backLink.text\"></a>\n\n <div class=\"other-content\">\n <ng-content></ng-content>\n </div>\n\n <ng-template [ngIf]=\"links\">\n <a *ngIf=\"!multilineExpanded && links[0]\" class=\"breadcrumb\"\n [attr.href]=\"isDisabled ? null : links[0].href\"\n (click)=\"onLinkClicked(links[0], $event)\"\n [title]=\"links[0].tooltip || links[0].text\"\n >{{ links[0].text }}</a>\n <div class=\"ellipsis\" *ngIf=\"isOverflowing && !multilineExpanded\" [class.multilineExpanded]=\"multilineExpanded\" (click)=\"toggleMultilineExpanded()\">\n ...\n </div>\n <div #lastPart class=\"lastPart\">\n <ng-container *ngFor=\"let link of links; let i = index\">\n <a *ngIf=\"!multilineExpanded && i > 0\"\n class=\"breadcrumb last\"\n [attr.href]=\"isDisabled ? null : link?.href\"\n (click)=\"onLinkClicked(link, $event)\"\n [title]=\"link.tooltip || link.text\"\n >{{ link.text }}</a>\n <a *ngIf=\"multilineExpanded\"\n class=\"breadcrumb last\"\n [attr.href]=\"isDisabled ? null : link?.href\"\n (click)=\"onLinkClicked(link, $event)\"\n [title]=\"link.tooltip || link.text\"\n >{{ link.text }}\n </a>\n </ng-container>\n <span *ngIf=\"multiline && multilineExpanded && !isOverflowing && showArrow\" class=\"back_arrow\" [class.multilineExpanded]=\"multilineExpanded\" (click)=\"toggleMultilineExpanded()\">\n <icon>arrow_back</icon>\n </span>\n </div>\n </ng-template>\n\n <ng-template [ngIf]=\"routerLinks\">\n <a *ngIf=\"!multilineExpanded && routerLinks[0]\" class=\"breadcrumb\"\n [routerLink]=\"routerLinks[0].route\"\n (click)=\"onLinkClicked(routerLinks[0], $event)\"\n [title]=\"routerLinks[0].tooltip || routerLinks[0].text\"\n >{{ routerLinks[0].text }}</a>\n <div class=\"ellipsis\" *ngIf=\"isOverflowing && !multilineExpanded\" [class.multilineExpanded]=\"multilineExpanded\" (click)=\"toggleMultilineExpanded()\">\n ...\n </div>\n <div #lastPart class=\"lastPart\">\n <ng-container *ngFor=\"let routerLink of routerLinks; let i = index\">\n <a *ngIf=\"!multilineExpanded && i > 0\"\n class=\"breadcrumb last\"\n [routerLink]=\"routerLink.route\"\n (click)=\"onLinkClicked(routerLink, $event)\"\n [title]=\"routerLink.tooltip || routerLink.text\"\n >{{ routerLink.text }}</a>\n <a *ngIf=\"multilineExpanded\"\n class=\"breadcrumb last\"\n [routerLink]=\"routerLink.route\"\n (click)=\"onLinkClicked(routerLink, $event)\"\n [title]=\"routerLink.tooltip || routerLink.text\"\n >{{ routerLink.text }}\n </a>\n </ng-container>\n <span *ngIf=\"multiline && multilineExpanded && !isOverflowing && showArrow\" class=\"back_arrow\" [class.multilineExpanded]=\"multilineExpanded\" (click)=\"toggleMultilineExpanded()\">\n <icon>arrow_back</icon>\n </span>\n </div>\n </ng-template>\n </div>\n </div>\n</nav>\n", directives: [{ type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i3.RouterLinkWithHref, selector: "a[routerLink],area[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "preserveFragment", "skipLocationChange", "replaceUrl", "state", "relativeTo", "routerLink"] }, { type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: Icon, selector: "icon" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: Breadcrumbs, decorators: [{ type: Component, args: [{ selector: 'gtx-breadcrumbs', changeDetection: ChangeDetectionStrategy.OnPush, template: "<nav [class.disabled]=\"isDisabled\" [class.multiline]=\"multiline\">\n <div #navWrapper class=\"nav-wrapper\" [class.is-overflowing]=\"isOverflowing\" [class.multilineExpanded]=\"multilineExpanded\" [class.multiline]=\"multiline\" (window:resize)=\"onResize($event)\">\n <div class=\"inner-wrapper\">\n <a class=\"back-button\" *ngIf=\"backLink && backLink.route\"\n (click)=\"onLinkClicked(backLink, $event)\"\n [routerLink]=\"backLink.route\"\n [title]=\"backLink.text\"></a>\n <a class=\"back-button\" *ngIf=\"backLink && !backLink.route\"\n (click)=\"onLinkClicked(backLink, $event)\"\n [attr.href]=\"isDisabled ? null : backLink?.href\"\n [title]=\"backLink.text\"></a>\n\n <div class=\"other-content\">\n <ng-content></ng-content>\n </div>\n\n <ng-template [ngIf]=\"links\">\n <a *ngIf=\"!multilineExpanded && links[0]\" class=\"breadcrumb\"\n [attr.href]=\"isDisabled ? null : links[0].href\"\n (click)=\"onLinkClicked(links[0], $event)\"\n [title]=\"links[0].tooltip || links[0].text\"\n >{{ links[0].text }}</a>\n <div class=\"ellipsis\" *ngIf=\"isOverflowing && !multilineExpanded\" [class.multilineExpanded]=\"multilineExpanded\" (click)=\"toggleMultilineExpanded()\">\n ...\n </div>\n <div #lastPart class=\"lastPart\">\n <ng-container *ngFor=\"let link of links; let i = index\">\n <a *ngIf=\"!multilineExpanded && i > 0\"\n class=\"breadcrumb last\"\n [attr.href]=\"isDisabled ? null : link?.href\"\n (click)=\"onLinkClicked(link, $event)\"\n [title]=\"link.tooltip || link.text\"\n >{{ link.text }}</a>\n <a *ngIf=\"multilineExpanded\"\n class=\"breadcrumb last\"\n [attr.href]=\"isDisabled ? null : link?.href\"\n (click)=\"onLinkClicked(link, $event)\"\n [title]=\"link.tooltip || link.text\"\n >{{ link.text }}\n </a>\n </ng-container>\n <span *ngIf=\"multiline && multilineExpanded && !isOverflowing && showArrow\" class=\"back_arrow\" [class.multilineExpanded]=\"multilineExpanded\" (click)=\"toggleMultilineExpanded()\">\n <icon>arrow_back</icon>\n </span>\n </div>\n </ng-template>\n\n <ng-template [ngIf]=\"routerLinks\">\n <a *ngIf=\"!multilineExpanded && routerLinks[0]\" class=\"breadcrumb\"\n [routerLink]=\"routerLinks[0].route\"\n (click)=\"onLinkClicked(routerLinks[0], $event)\"\n [title]=\"routerLinks[0].tooltip || routerLinks[0].text\"\n >{{ routerLinks[0].text }}</a>\n <div class=\"ellipsis\" *ngIf=\"isOverflowing && !multilineExpanded\" [class.multilineExpanded]=\"multilineExpanded\" (click)=\"toggleMultilineExpanded()\">\n ...\n </div>\n <div #lastPart class=\"lastPart\">\n <ng-container *ngFor=\"let routerLink of routerLinks; let i = index\">\n <a *ngIf=\"!multilineExpanded && i > 0\"\n class=\"breadcrumb last\"\n [routerLink]=\"routerLink.route\"\n (click)=\"onLinkClicked(routerLink, $event)\"\n [title]=\"routerLink.tooltip || routerLink.text\"\n >{{ routerLink.text }}</a>\n <a *ngIf=\"multilineExpanded\"\n class=\"breadcrumb last\"\n [routerLink]=\"routerLink.route\"\n (click)=\"onLinkClicked(routerLink, $event)\"\n [title]=\"routerLink.tooltip || routerLink.text\"\n >{{ routerLink.text }}\n </a>\n </ng-container>\n <span *ngIf=\"multiline && multilineExpanded && !isOverflowing && showArrow\" class=\"back_arrow\" [class.multilineExpanded]=\"multilineExpanded\" (click)=\"toggleMultilineExpanded()\">\n <icon>arrow_back</icon>\n </span>\n </div>\n </ng-template>\n </div>\n </div>\n</nav>\n" }] }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }, { type: i0.ElementRef }, { type: UserAgentRef }]; }, propDecorators: { links: [{ type: Input }], routerLinks: [{ type: Input }], collapsedColor: [{ type: Input }], multiline: [{ type: Input }], multilineExpanded: [{ type: Input }], disabled: [{ type: Input }], linkClick: [{ type: Output }], multilineExpandedChange: [{ type: Output }], routerLinkChildren: [{ type: ViewChildren, args: [RouterLinkWithHref] }], navWrapper: [{ type: ViewChild, args: ['navWrapper'] }], lastPart: [{ type: ViewChild, args: ['lastPart'] }] } }); /** * A Button component. * * ```html * <gtx-button>Click me</gtx-button> * <gtx-button size="large">Buy Now!</gtx-button> * <gtx-button type="alert">Delete all stuff</gtx-button> * <gtx-button icon> * <i class="material-icons">settings</i> * </gtx-button> * ``` */ class Button { constructor() { /** * Sets the input field to be auto-focused. Handled by `AutofocusDirective`. */ this.autofocus = false; /** * Specify the size of the button. Can be "small", "regular" or "large". */ this.size = 'regular'; /** * Type determines the style of the button. Can be "default", "secondary", * "success", "warning" or "alert". */ this.type = 'default'; this.buttonType = 'button'; this.isFlat = false; this.isIcon = false; this.isDisabled = false; } /** * Setting the "flat" attribute gives the button a transparent background * and only depth on hover. */ get flat() { return this.isFlat; } set flat(val) { this.isFlat = val != null && val !== false; } /** * Setting the "icon" attribute turns the button into an "icon button", which is * like a flat button without a border, suitable for wrapping an icon. */ get icon() { return this.isIcon; } set icon(val) { this.isIcon = val != null && val !== false; } /** * Controls whether the button is disabled. */ get disabled() { return this.isDisabled; } set disabled(disabled) { this.isDisabled = disabled === '' || !!disabled; } /** * Set button as a submit button. */ set submit(value) { this.buttonType = (value != null && value !== false) ? 'submit' : 'button'; } // In some browsers, disabled elements don't fire mouse events, but bubble them up the DOM tree. // To not trigger actions when the button is disabled, we need to prevent them manually. preventDisabledClick(event) { if (event && this.isDisabled) { event.preventDefault(); event.stopImmediatePropagation(); event.stopPropagation(); } } } /** @nocollapse */ Button.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: Button, deps: [], target: i0.ɵɵFactoryTarget.Component }); /** @nocollapse */ Button.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.8", type: Button, selector: "gtx-button", inputs: { autofocus: "autofocus", size: "size", type: "type", flat: "flat", icon: "icon", disabled: "disabled", submit: "submit" }, host: { listeners: { "click": "preventDisabledClick($event)" } }, ngImport: i0, template: "<div class=\"button-event-wrapper\" (click)=\"preventDisabledClick($event)\">\n <button class=\"btn {{size}} {{type}}\"\n [type]=\"buttonType\"\n [disabled]=\"disabled\"\n [class.btn-flat]=\"flat || icon\"\n [class.btn-icon]=\"icon\"\n (click)=\"preventDisabledClick($event)\"\n ><ng-content></ng-content></button>\n</div>\n" }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: Button, decorators: [{ type: Component, args: [{ selector: 'gtx-button', template: "<div class=\"button-event-wrapper\" (click)=\"preventDisabledClick($event)\">\n <button class=\"btn {{size}} {{type}}\"\n [type]=\"buttonType\"\n [disabled]=\"disabled\"\n [class.btn-flat]=\"flat || icon\"\n [class.btn-icon]=\"icon\"\n (click)=\"preventDisabledClick($event)\"\n ><ng-content></ng-content></button>\n</div>\n" }] }], propDecorators: { autofocus: [{ type: Input }], size: [{ type: Input }], type: [{ type: Input }], flat: [{ type: Input }], icon: [{ type: Input }], disabled: [{ type: Input }], submit: [{ type: Input }], preventDisabledClick: [{ type: HostListener, args: ['click', ['$event']] }] } }); var KeyCode; (function (KeyCode) { KeyCode[KeyCode["UpArrow"] = 38] = "UpArrow"; KeyCode[KeyCode["DownArrow"] = 40] = "DownArrow"; KeyCode[KeyCode["RightArrow"] = 39] = "RightArrow"; KeyCode[KeyCode["LeftArrow"] = 37] = "LeftArrow"; KeyCode[KeyCode["PageUp"] = 33] = "PageUp"; KeyCode[KeyCode["PageDown"] = 34] = "PageDown"; KeyCode[KeyCode["Home"] = 36] = "Home"; KeyCode[KeyCode["End"] = 35] = "End"; KeyCode[KeyCode["Enter"] = 13] = "Enter"; KeyCode[KeyCode["Space"] = 32] = "Space"; KeyCode[KeyCode["Tab"] = 9] = "Tab"; KeyCode[KeyCode["Escape"] = 27] = "Escape"; KeyCode[KeyCode["Backspace"] = 8] = "Backspace"; KeyCode[KeyCode["Delete"] = 46] = "Delete"; })(KeyCode || (KeyCode = {})); const GTX_CHECKBOX_VALUE_ACCESSOR = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => Checkbox), multi: true }; /** * Checkbox wraps the native `<input type="checkbox">` form element. * * ```html * <gtx-checkbox [(ngModel)]="isOkay" label="Is it okay?"></gtx-checkbox> * <gtx-checkbox [(ngModel)]="checkStates.B" value="B" label="B"></gtx-checkbox> * ``` * * ## Stateless Mode * By default, the Checkbox keeps track of its own internal checked state. This makes sense * for most use cases, such as when used in a form bound to NgControl. * * However, in some cases we want to explicitly set the state from outside. This is done by binding * to the <code>checked</code> attribute. When this attribute is bound, the checked state of the * Checkbox will *only* change when the value of the binding changes. Clicking on the Checkbox * will have no effect other than to emit an event which the parent can use to update the binding. * * Here is a basic example of a stateless checkbox where the parent component manages the state: * * ```html * <gtx-checkbox [checked]="isChecked" * (change)="isChecked = $event"></gtx-checkbox> * ``` */ class Checkbox { constructor(changeDetector) { this.changeDetector = changeDetector; /** * Sets the checkbox to be auto-focused. Handled by `AutofocusDirective`. */ this.autofocus = false; /** * Set the checkbox to its disabled state. */ this.disabled = false; /** * Checkbox ID */ this.id = randomID$1(); /** * Label for the checkbox */ this.label = ''; /** * Sets the required property */ this.required = false; /** * The value of the checkbox */ this.value = ''; /** * Blur event */ this.blur = new EventEmitter(); /** * Focus event */ this.focus = new EventEmitter(); /** * Change event */ this.change = new EventEmitter(); this.checkState = false; this.tabbedFocus = false; /** * See note above on stateless mode. */ this.statelessMode = false; this.onChange = () => { }; this.onTouched = () => { }; } /** * Checked state of the checkbox. When set, the Checkbox will be * in stateless mode. */ get checked() { return this.checkState === true; } set checked(value) { this.statelessMode = true; let val = value; let nowChecked = val === true || val === 'true' || val === ''; if (nowChecked != this.checkState) { this.onChange(this.checkState = nowChecked); this.changeDetector.markForCheck(); } } /** * Set to "indeterminate" for an indeterminate state (-) */ get indeterminate() { return this.checkState === 'indeterminate'; } set indeterminate(val) { if (val != (this.checkState === 'indeterminate')) { this.checkState = val ? 'indeterminate' : false; this.change.emit(this.checkState); this.onChange(this.checkState); } } onBlur() { this.blur.emit(this.checkState); this.onTouched(); this.tabbedFocus = false; } onFocus() { this.focus.emit(this.checkState); } focusHandler(e) { if (e.keyCode === KeyCode.Tab) { if (!this.tabbedFocus) { this.tabbedFocus = true; } } } writeValue(value) { if (value !== this.checkState) { this.checkState = value; this.changeDetector.markForCheck(); } } ngOnInit() { this.onChange(this.checkState); } ngAfterViewInit() { this.fixInitialAnimation(); } onInputChanged(e, input) { if (e) { e.stopPropagation(); } let newState = input.indeterminate ? 'indeterminate' : input.checked; if (this.statelessMode) { if (input.checked !== this.checkState) { input.checked = !!this.checkState; } this.change.emit(newState); return false; } if (newState != this.checkState) { this.checkState = newState; this.onChange(newState); this.change.emit(newState); return true; } } registerOnChange(fn) { this.onChange = fn; } registerOnTouched(fn) { this.onTouched = fn; } setDisabledState(disabled) { this.disabled = disabled; this.changeDetector.markForCheck(); } /** * This is a hacky fix to prevent Materialize from animating ticked checkboxes which * kicks in when a checkbox is added to the dom with checked=false and immediately * set to checked=true. */ fixInitialAnimation() { if (this.labelElement && this.labelElement.nativeElement) { let label = this.labelElement.nativeElement; label.style.display = 'none'; let ignored = label.offsetWidth; label.style.display = ''; } } } /** @nocollapse */ Checkbox.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: Checkbox, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); /** @nocollapse */ Checkbox.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.8", type: Checkbox, selector: "gtx-checkbox", inputs: { autofocus: "autofocus", checked: "checked", indeterminate: "indeterminate", disabled: "disabled", id: "id", label: "label", name: "name", required: "required", value: "value" }, outputs: { blur: "blur", focus: "focus", change: "change" }, host: { listeners: { "keyup": "focusHandler($event)" } }, providers: [GTX_CHECKBOX_VALUE_ACCESSOR], viewQueries: [{ propertyName: "labelElement", first: true, predicate: ["labelElement"], descendants: true, static: true }], ngImport: i0, template: "<div>\n <input type=\"checkbox\"\n [attr.id]=\"id\"\n [attr.name]=\"name\"\n [checked]=\"checkState === true\"\n [indeterminate]=\"checkState === 'indeterminate'\"\n [disabled]=\"disabled\"\n [required]=\"required\"\n [value]=\"value\"\n\n (blur)=\"onBlur()\"\n (focus)=\"onFocus()\"\n (change)=\"onInputChanged($event, input)\"\n\n [class.tabbed]=\"tabbedFocus\"\n\n #input\n >\n <label [attr.for]=\"id\" #labelElement>{{ label }}</label>\n</div>\n" }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: Checkbox, decorators: [{ type: Component, args: [{ selector: 'gtx-checkbox', providers: [GTX_CHECKBOX_VALUE_ACCESSOR], template: "<div>\n <input type=\"checkbox\"\n [attr.id]=\"id\"\n [attr.name]=\"name\"\n [checked]=\"checkState === true\"\n [indeterminate]=\"checkState === 'indeterminate'\"\n [disabled]=\"disabled\"\n [required]=\"required\"\n [value]=\"value\"\n\n (blur)=\"onBlur()\"\n (focus)=\"onFocus()\"\n (change)=\"onInputChanged($event, input)\"\n\n [class.tabbed]=\"tabbedFocus\"\n\n #input\n >\n <label [attr.for]=\"id\" #labelElement>{{ label }}</label>\n</div>\n" }] }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }]; }, propDecorators: { autofocus: [{ type: Input }], checked: [{ type: Input }], indeterminate: [{ type: Input }], disabled: [{ type: Input }], id: [{ type: Input }], label: [{ type: Input }], name: [{ type: Input }], required: [{ type: Input }], value: [{ type: Input }], blur: [{ type: Output }], focus: [{ type: Output }], change: [{ type: Output }], labelElement: [{ type: ViewChild, args: ['labelElement', { static: true }] }], focusHandler: [{ type: HostListener, args: ['keyup', ['$event']] }] } }); function randomID$1() { return 'checkbox-' + Math.random().toString(36).substr(2); } /** * A wrapper around items that appear in the list pane of the SplitViewComponent. * * Two component-specific classes can be used: * * * `.item-avatar`: The content of this element will be styled in a circular container. * * `.item-primary`: The primary content of the item, which will take up all the remaining space via `flex: 1`. * * `.show-on-hover`: Any element with this class will appear faded out until the user hovers the list item. * * * ```html * <gtx-contents-list-item *ngFor="let item of listItems"> * <!-- this will be styled as a circular icon --> * <div class="item-avatar"><i class="material-icons">{{ item.icon }}</i></div> * <!-- this will stretch to use all available space --> * <div class="item-primary"><a [routerLink]="[item.route]">{{ item.title }}</a></div> * <!-- these will use remaining space to the right --> * <i class="material-icons show-on-hover">edit</i> * <i class="material-icons show-on-hover">star</i> * </gtx-contents-list-item> * ``` */ class ContentsListItem { } /** @nocollapse */ ContentsListItem.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: ContentsListItem, deps: [], target: i0.ɵɵFactoryTarget.Component }); /** @nocollapse */ ContentsListItem.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.8", type: ContentsListItem, selector: "gtx-contents-list-item", ngImport: i0, template: "<ng-content></ng-content>\n" }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: ContentsListItem, decorators: [{ type: Component, args: [{ selector: 'gtx-contents-list-item', template: "<ng-content></ng-content>\n" }] }] }); /** * Components with boolean inputs may receive the value as an actual boolean (if data-bound `[prop]="false"`) or as * a string representation of a boolean (if passed as a regular attribute `prop="false"`). * In the latter case, we want to ensure that the string version is correctly coerced to its boolean counterpart. */ function coerceToBoolean(val) { return val === true || val === 'true' || val === ''; } const defaultStrings = { hours: 'Hours', minutes: 'Minutes', seconds: 'Seconds', okay: 'Okay', cancel: 'Cancel', months: [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ], monthsShort: [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ], weekdays: [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ], weekdaysShort: [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ], weekdaysMin: [ 'Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa' ] }; /** * Format provider to localize the DateTimePicker component. */ class DateTimePickerFormatProvider { constructor() { /** Texts uses by the DateTimePicker modal. */ this.strings = defaultStrings; /** May emit a value when the translations or the date format changed. */ this.changed$ = NEVER; } /** Formats a human-readable string to be displayed in the control input field. */ format(date, displayTime, displaySeconds) { let formatString = displayTime ? (displaySeconds ? 'L, LTS' : 'L, LT') : 'L'; return date.format(formatString); } } /** @nocollapse */ DateTimePickerFormatProvider.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: DateTimePickerFormatProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); /** @nocollapse */ DateTimePickerFormatProvider.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: DateTimePickerFormatProvider }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: DateTimePickerFormatProvider, decorators: [{ type: Injectable }] }); /** * This is a workaround for loading moment JS in TypeScript 3.2 and Angular 7. * This is based on https://github.com/rollup/rollup/issues/1267#issuecomment-446681320 */ /** The moment namespace and moment function. */ const momentjs = moment_.default || moment_; window.moment = momentjs; rome_.use(momentjs); delete window.moment; if (momentum.moment === void 0) { throw new Error('rome depends on moment.js, you can get it at http://momentjs.com.'); } /** The rome namespace and rome function. */ const rome = rome_.default || rome_; class DropdownItem { constructor() { this.tabIndex = 0; this.isDisabled = false; } /** * If true, the DropdownItem cannot be clicked or selected. *Default: false* */ get disabled() { return this.isDisabled; } set disabled(value) { this.isDisabled = coerceToBoolean(value); } } /** @nocollapse */ DropdownItem.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: DropdownItem, deps: [], target: i0.ɵɵFactoryTarget.Component }); /** @nocollapse */ DropdownItem.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.8", type: DropdownItem, selector: "gtx-dropdown-item", inputs: { disabled: "disabled" }, host: { properties: { "tabindex": "this.tabIndex", "class.disabled": "this.isDisabled" } }, ngImport: i0, template: `<ng-content></ng-content>`, isInline: true }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: DropdownItem, decorators: [{ type: Component, args: [{ selector: 'gtx-dropdown-item', template: `<ng-content></ng-content>` }] }], propDecorators: { disabled: [{ type: Input }], tabIndex: [{ type: HostBinding, args: ['tabindex'] }], isDisabled: [{ type: HostBinding, args: ['class.disabled'] }] } }); const FOCUSABLE_SELECTOR = `gtx-dropdown-item, a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, *[tabindex], *[contenteditable]`; class DropdownTriggerDirective { constructor(elementRef) { this.elementRef = elementRef; } /** * Focus the first focusable descendant of this element. */ focus() { const focusable = this.elementRef.nativeElement.querySelector(FOCUSABLE_SELECTOR); if (focusable && focusable.focus) { focusable.focus(); } } } /** @nocollapse */ DropdownTriggerDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: DropdownTriggerDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); /** @nocollapse */ DropdownTriggerDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "13.3.8", type: DropdownTriggerDirective, selector: "gtx-dropdown-trigger", ngImport: i0 }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: DropdownTriggerDirective, decorators: [{ type: Directive, args: [{ selector: 'gtx-dropdown-trigger' }] }], ctorParameters: function () { return [{ type: i0.ElementRef }]; } }); /** * Wraps the content and handles keyboard control (tabbing and focus) of the contents. */ class DropdownContent { constructor(elementRef) { this.elementRef = elementRef; this.focusLost = new EventEmitter(); this.focusableItems = []; } keyHandler(e) { if (e.keyCode === KeyCode.Tab) { if (e.shiftKey) { this.focusPrevious(e.target, e); } else { this.focusNext(e.target, e); } } } ngAfterContentInit() { this.focusableItems = Array.from(this.elementRef.nativeElement.querySelectorAll(FOCUSABLE_SELECTOR)); } focusFirstItem() { const firstItem = this.focusableItems[0]; if (firstItem && firstItem.focus) { firstItem.focus(); } } focusNext(currentElement, e) { const items = this.focusableItems; const index = this.getIndexOfElement(currentElement); if (index === items.length - 1) { e.preventDefault(); this.focusLost.emit(true); } } focusPrevious(currentElement, e) { const index = this.getIndexOfElement(currentElement); if (index === 0) { e.preventDefault(); this.focusLost.emit(true); } } getIndexOfElement(element) { return this.focusableItems.indexOf(element); } } /** @nocollapse */ DropdownContent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: DropdownContent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component }); /** @nocollapse */ DropdownContent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.8", type: DropdownContent, selector: "gtx-dropdown-content", host: { listeners: { "keydown": "keyHandler($event)" } }, queries: [{ propertyName: "items", predicate: i0.forwardRef(function () { return DropdownItem; }), read: ElementRef }], ngImport: i0, template: `<div class="scroller"><ng-content></ng-content></div>`, isInline: true }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: DropdownContent, decorators: [{ type: Component, args: [{ selector: 'gtx-dropdown-content', template: `<div class="scroller"><ng-content></ng-content></div>` }] }], ctorParameters: function () { return [{ type: i0.ElementRef }]; }, propDecorators: { items: [{ type: ContentChildren, args: [forwardRef(() => DropdownItem), { read: ElementRef }] }], keyHandler: [{ type: HostListener, args: ['keydown', ['$event']] }] } }); class DropdownContentWrapper { constructor(elementRef, cd, config) { this.elementRef = elementRef; this.cd = cd; this.config = config; this.contentStyles = { position: 'absolute' }; this.options = { alignment: 'left', width: 'contents', belowTrigger: false }; this.id = 'dropdown-' + Math.random().toString(36).substr(2); this.clicked = new EventEmitter(); this.escapeKeyPressed = new EventEmitter(); this.widthHasBeenAdjusted = false; this.pageMargin = this.config.dropDownPageMargin; this.dropDownMaxHeight = this.config.dropDownMaxHeight; } ngAfterViewInit() { this.setPositionAndSize(true); } /** * Positions and resizes the dropdown contents container. */ setPositionAndSize(initialOpening = false) { const content = this.getDropdownContent(); if (!content) { return; } if (initialOpening) { // When opening for the first time, some extra logic is required this.contentStyles.height = 0; this.contentStyles.opacity = 0; content.setAttribute('id', this.id); } const positionStyles = this.calculatePositionStyles(); Object.assign(this.contentStyles, positionStyles); // const flowUpwards = parseInt(positionStyles.top, 10) < Math.floor(this.trigger.getBoundingClientRect().top); const contentHeight = this.innerHeight(this.elementRef.nativeElement.querySelector('gtx-dropdown-content')); // when flowing upwards, we animate the `top` property, so must remember the final value. const finalTop = parseInt(this.contentStyles.top); if (positi