UNPKG

@swimlane/ngx-charts

Version:

Declarative Charting Framework for Angular

999 lines (985 loc) 947 kB
import * as i0 from '@angular/core'; import { PLATFORM_ID, Component, ViewEncapsulation, Inject, Input, ViewChild, HostBinding, HostListener, Injectable, EventEmitter, ChangeDetectionStrategy, Output, Directive, NgModule, ContentChild } from '@angular/core'; import * as i1 from '@angular/common'; import { isPlatformBrowser, isPlatformServer, CommonModule } from '@angular/common'; import { __decorate } from 'tslib'; import { DomPortalOutlet, ComponentPortal } from '@angular/cdk/portal'; import { fromEvent } from 'rxjs'; import { debounceTime } from 'rxjs/operators'; import { trigger, transition, style, animate } from '@angular/animations'; import { select } from 'd3-selection'; import { brushX } from 'd3-brush'; import { scaleTime, scaleLinear, scalePoint, scaleOrdinal, scaleQuantile, scaleBand } from 'd3-scale'; import { area, curveLinear, line, arc, lineRadial, curveCardinalClosed, pie } from 'd3-shape'; import { range, min, max, quantile } from 'd3-array'; import { interpolate } from 'd3-interpolate'; import { easeSinInOut } from 'd3-ease'; import { format } from 'd3-format'; import * as d3_color from 'd3-color'; import { treemap, stratify } from 'd3-hierarchy'; import { sankey, sankeyLeft, sankeyLinkHorizontal } from 'd3-sankey'; import { timeFormat } from 'd3-time-format'; /** * Throttle a function * */ function throttle(func, wait, options) { options = options || {}; let context; let args; let result; let timeout = null; let previous = 0; function later() { previous = options.leading === false ? 0 : +new Date(); timeout = null; result = func.apply(context, args); } return function () { const now = +new Date(); if (!previous && options.leading === false) { previous = now; } const remaining = wait - (now - previous); // eslint-disable-next-line @typescript-eslint/no-this-alias context = this; // eslint-disable-next-line prefer-rest-params args = arguments; if (remaining <= 0) { clearTimeout(timeout); timeout = null; previous = now; result = func.apply(context, args); } else if (!timeout && options.trailing !== false) { timeout = setTimeout(later, remaining); } return result; }; } /** * Throttle decorator * * class MyClass { * throttleable(10) * myFn() { ... } * } */ function throttleable(duration, options) { return function innerDecorator(target, key, descriptor) { return { configurable: true, enumerable: descriptor.enumerable, get: function getter() { Object.defineProperty(this, key, { configurable: true, enumerable: descriptor.enumerable, value: throttle(descriptor.value, duration, options) }); return this[key]; } }; }; } var PlacementTypes; (function (PlacementTypes) { PlacementTypes["Top"] = "top"; PlacementTypes["Bottom"] = "bottom"; PlacementTypes["Left"] = "left"; PlacementTypes["Right"] = "right"; PlacementTypes["Center"] = "center"; })(PlacementTypes || (PlacementTypes = {})); const caretOffset = 7; function verticalPosition(elDimensions, popoverDimensions, alignment) { if (alignment === PlacementTypes.Top) { return elDimensions.top - caretOffset; } if (alignment === PlacementTypes.Bottom) { return elDimensions.top + elDimensions.height - popoverDimensions.height + caretOffset; } if (alignment === PlacementTypes.Center) { return elDimensions.top + elDimensions.height / 2 - popoverDimensions.height / 2; } return undefined; } function horizontalPosition(elDimensions, popoverDimensions, alignment) { if (alignment === PlacementTypes.Left) { return elDimensions.left - caretOffset; } if (alignment === PlacementTypes.Right) { return elDimensions.left + elDimensions.width - popoverDimensions.width + caretOffset; } if (alignment === PlacementTypes.Center) { return elDimensions.left + elDimensions.width / 2 - popoverDimensions.width / 2; } return undefined; } /** * Position helper for the popover directive. * * @export */ class PositionHelper { /** * Calculate vertical alignment position * * @memberOf PositionHelper */ static calculateVerticalAlignment(elDimensions, popoverDimensions, alignment) { let result = verticalPosition(elDimensions, popoverDimensions, alignment); if (result + popoverDimensions.height > window.innerHeight) { result = window.innerHeight - popoverDimensions.height; } return result; } /** * Calculate vertical caret position * * @memberOf PositionHelper */ static calculateVerticalCaret(elDimensions, popoverDimensions, caretDimensions, alignment) { let result; if (alignment === PlacementTypes.Top) { result = elDimensions.height / 2 - caretDimensions.height / 2 + caretOffset; } if (alignment === PlacementTypes.Bottom) { result = popoverDimensions.height - elDimensions.height / 2 - caretDimensions.height / 2 - caretOffset; } if (alignment === PlacementTypes.Center) { result = popoverDimensions.height / 2 - caretDimensions.height / 2; } const popoverPosition = verticalPosition(elDimensions, popoverDimensions, alignment); if (popoverPosition + popoverDimensions.height > window.innerHeight) { result += popoverPosition + popoverDimensions.height - window.innerHeight; } return result; } /** * Calculate horz alignment position * * @memberOf PositionHelper */ static calculateHorizontalAlignment(elDimensions, popoverDimensions, alignment) { let result = horizontalPosition(elDimensions, popoverDimensions, alignment); if (result + popoverDimensions.width > window.innerWidth) { result = window.innerWidth - popoverDimensions.width; } return result; } /** * Calculate horz caret position * * @memberOf PositionHelper */ static calculateHorizontalCaret(elDimensions, popoverDimensions, caretDimensions, alignment) { let result; if (alignment === PlacementTypes.Left) { result = elDimensions.width / 2 - caretDimensions.width / 2 + caretOffset; } if (alignment === PlacementTypes.Right) { result = popoverDimensions.width - elDimensions.width / 2 - caretDimensions.width / 2 - caretOffset; } if (alignment === PlacementTypes.Center) { result = popoverDimensions.width / 2 - caretDimensions.width / 2; } const popoverPosition = horizontalPosition(elDimensions, popoverDimensions, alignment); if (popoverPosition + popoverDimensions.width > window.innerWidth) { result += popoverPosition + popoverDimensions.width - window.innerWidth; } return result; } /** * Checks if the element's position should be flipped * * @memberOf PositionHelper */ static shouldFlip(elDimensions, popoverDimensions, placement, spacing) { let flip = false; if (placement === PlacementTypes.Right) { if (elDimensions.left + elDimensions.width + popoverDimensions.width + spacing > window.innerWidth) { flip = true; } } if (placement === PlacementTypes.Left) { if (elDimensions.left - popoverDimensions.width - spacing < 0) { flip = true; } } if (placement === PlacementTypes.Top) { if (elDimensions.top - popoverDimensions.height - spacing < 0) { flip = true; } } if (placement === PlacementTypes.Bottom) { if (elDimensions.top + elDimensions.height + popoverDimensions.height + spacing > window.innerHeight) { flip = true; } } return flip; } /** * Position caret * * @memberOf PositionHelper */ static positionCaret(placement, elmDim, hostDim, caretDimensions, alignment) { let top = 0; let left = 0; if (placement === PlacementTypes.Right) { left = -7; top = PositionHelper.calculateVerticalCaret(hostDim, elmDim, caretDimensions, alignment); } else if (placement === PlacementTypes.Left) { left = elmDim.width; top = PositionHelper.calculateVerticalCaret(hostDim, elmDim, caretDimensions, alignment); } else if (placement === PlacementTypes.Top) { top = elmDim.height; left = PositionHelper.calculateHorizontalCaret(hostDim, elmDim, caretDimensions, alignment); } else if (placement === PlacementTypes.Bottom) { top = -7; left = PositionHelper.calculateHorizontalCaret(hostDim, elmDim, caretDimensions, alignment); } return { top, left }; } /** * Position content * * @memberOf PositionHelper */ static positionContent(placement, elmDim, hostDim, spacing, alignment) { let top = 0; let left = 0; if (placement === PlacementTypes.Right) { left = hostDim.left + hostDim.width + spacing; top = PositionHelper.calculateVerticalAlignment(hostDim, elmDim, alignment); } else if (placement === PlacementTypes.Left) { left = hostDim.left - elmDim.width - spacing; top = PositionHelper.calculateVerticalAlignment(hostDim, elmDim, alignment); } else if (placement === PlacementTypes.Top) { top = hostDim.top - elmDim.height - spacing; left = PositionHelper.calculateHorizontalAlignment(hostDim, elmDim, alignment); } else if (placement === PlacementTypes.Bottom) { top = hostDim.top + hostDim.height + spacing; left = PositionHelper.calculateHorizontalAlignment(hostDim, elmDim, alignment); } return { top, left }; } /** * Determine placement based on flip * * @memberOf PositionHelper */ static determinePlacement(placement, elmDim, hostDim, spacing) { const shouldFlip = PositionHelper.shouldFlip(hostDim, elmDim, placement, spacing); if (shouldFlip) { if (placement === PlacementTypes.Right) { return PlacementTypes.Left; } else if (placement === PlacementTypes.Left) { return PlacementTypes.Right; } else if (placement === PlacementTypes.Top) { return PlacementTypes.Bottom; } else if (placement === PlacementTypes.Bottom) { return PlacementTypes.Top; } } return placement; } } class TooltipContentComponent { get cssClasses() { let clz = 'ngx-charts-tooltip-content'; clz += ` position-${this.placement}`; clz += ` type-${this.type}`; clz += ` ${this.cssClass}`; return clz; } constructor(element, renderer, platformId) { this.element = element; this.renderer = renderer; this.platformId = platformId; } ngAfterViewInit() { setTimeout(this.position.bind(this)); } position() { if (!isPlatformBrowser(this.platformId)) { return; } const nativeElm = this.element.nativeElement; const hostDim = this.host.nativeElement.getBoundingClientRect(); // if no dims were found, never show if (!hostDim.height && !hostDim.width) return; const elmDim = nativeElm.getBoundingClientRect(); this.checkFlip(hostDim, elmDim); this.positionContent(nativeElm, hostDim, elmDim); if (this.showCaret) { this.positionCaret(hostDim, elmDim); } // animate its entry setTimeout(() => this.renderer.addClass(nativeElm, 'animate'), 1); } positionContent(nativeElm, hostDim, elmDim) { const { top, left } = PositionHelper.positionContent(this.placement, elmDim, hostDim, this.spacing, this.alignment); this.renderer.setStyle(nativeElm, 'top', `${top}px`); this.renderer.setStyle(nativeElm, 'left', `${left}px`); } positionCaret(hostDim, elmDim) { const caretElm = this.caretElm.nativeElement; const caretDimensions = caretElm.getBoundingClientRect(); const { top, left } = PositionHelper.positionCaret(this.placement, elmDim, hostDim, caretDimensions, this.alignment); this.renderer.setStyle(caretElm, 'top', `${top}px`); this.renderer.setStyle(caretElm, 'left', `${left}px`); } checkFlip(hostDim, elmDim) { this.placement = PositionHelper.determinePlacement(this.placement, elmDim, hostDim, this.spacing); } onWindowResize() { this.position(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: TooltipContentComponent, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }, { token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.0.4", type: TooltipContentComponent, isStandalone: false, selector: "ngx-tooltip-content", inputs: { host: "host", showCaret: "showCaret", type: "type", placement: "placement", alignment: "alignment", spacing: "spacing", cssClass: "cssClass", title: "title", template: "template", context: "context" }, host: { listeners: { "window:resize": "onWindowResize()" }, properties: { "class": "this.cssClasses" } }, viewQueries: [{ propertyName: "caretElm", first: true, predicate: ["caretElm"], descendants: true }], ngImport: i0, template: ` <div> <span #caretElm [hidden]="!showCaret" class="tooltip-caret position-{{ this.placement }}"> </span> <div class="tooltip-content"> <span *ngIf="!title"> <ng-template [ngTemplateOutlet]="template" [ngTemplateOutletContext]="{ model: context }"> </ng-template> </span> <span *ngIf="title" [innerHTML]="title"> </span> </div> </div> `, isInline: true, styles: [".ngx-charts-tooltip-content{position:fixed;border-radius:3px;z-index:5000;display:block;font-weight:400;opacity:0;pointer-events:none!important}.ngx-charts-tooltip-content.type-popover{background:#fff;color:#060709;border:1px solid #72809b;box-shadow:0 1px 3px #0003,0 1px 1px #00000024,0 2px 1px -1px #0000001f;font-size:13px;padding:4px}.ngx-charts-tooltip-content.type-popover .tooltip-caret{position:absolute;z-index:5001;width:0;height:0}.ngx-charts-tooltip-content.type-popover .tooltip-caret.position-left{border-top:7px solid transparent;border-bottom:7px solid transparent;border-left:7px solid #fff}.ngx-charts-tooltip-content.type-popover .tooltip-caret.position-top{border-left:7px solid transparent;border-right:7px solid transparent;border-top:7px solid #fff}.ngx-charts-tooltip-content.type-popover .tooltip-caret.position-right{border-top:7px solid transparent;border-bottom:7px solid transparent;border-right:7px solid #fff}.ngx-charts-tooltip-content.type-popover .tooltip-caret.position-bottom{border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #fff}.ngx-charts-tooltip-content.type-tooltip{color:#fff;background:#000000bf;font-size:12px;padding:0 10px;text-align:center;pointer-events:auto}.ngx-charts-tooltip-content.type-tooltip .tooltip-caret.position-left{border-top:7px solid transparent;border-bottom:7px solid transparent;border-left:7px solid rgba(0,0,0,.75)}.ngx-charts-tooltip-content.type-tooltip .tooltip-caret.position-top{border-left:7px solid transparent;border-right:7px solid transparent;border-top:7px solid rgba(0,0,0,.75)}.ngx-charts-tooltip-content.type-tooltip .tooltip-caret.position-right{border-top:7px solid transparent;border-bottom:7px solid transparent;border-right:7px solid rgba(0,0,0,.75)}.ngx-charts-tooltip-content.type-tooltip .tooltip-caret.position-bottom{border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid rgba(0,0,0,.75)}.ngx-charts-tooltip-content .tooltip-label{display:block;line-height:1em;padding:8px 5px 5px;font-size:1em}.ngx-charts-tooltip-content .tooltip-val{display:block;font-size:1.3em;line-height:1em;padding:0 5px 8px}.ngx-charts-tooltip-content .tooltip-caret{position:absolute;z-index:5001;width:0;height:0}.ngx-charts-tooltip-content.position-right{transform:translate3d(10px,0,0)}.ngx-charts-tooltip-content.position-left{transform:translate3d(-10px,0,0)}.ngx-charts-tooltip-content.position-top{transform:translate3d(0,-10px,0)}.ngx-charts-tooltip-content.position-bottom{transform:translate3d(0,10px,0)}.ngx-charts-tooltip-content.animate{opacity:1;transition:opacity .3s,transform .3s;transform:translateZ(0);pointer-events:auto}.area-tooltip-container{padding:5px 0;pointer-events:none}.tooltip-item{text-align:left;line-height:1.2em;padding:5px 0}.tooltip-item .tooltip-item-color{display:inline-block;height:12px;width:12px;margin-right:5px;color:#5b646b;border-radius:3px}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], encapsulation: i0.ViewEncapsulation.None }); } } __decorate([ throttleable(100) ], TooltipContentComponent.prototype, "onWindowResize", null); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: TooltipContentComponent, decorators: [{ type: Component, args: [{ selector: 'ngx-tooltip-content', template: ` <div> <span #caretElm [hidden]="!showCaret" class="tooltip-caret position-{{ this.placement }}"> </span> <div class="tooltip-content"> <span *ngIf="!title"> <ng-template [ngTemplateOutlet]="template" [ngTemplateOutletContext]="{ model: context }"> </ng-template> </span> <span *ngIf="title" [innerHTML]="title"> </span> </div> </div> `, encapsulation: ViewEncapsulation.None, standalone: false, styles: [".ngx-charts-tooltip-content{position:fixed;border-radius:3px;z-index:5000;display:block;font-weight:400;opacity:0;pointer-events:none!important}.ngx-charts-tooltip-content.type-popover{background:#fff;color:#060709;border:1px solid #72809b;box-shadow:0 1px 3px #0003,0 1px 1px #00000024,0 2px 1px -1px #0000001f;font-size:13px;padding:4px}.ngx-charts-tooltip-content.type-popover .tooltip-caret{position:absolute;z-index:5001;width:0;height:0}.ngx-charts-tooltip-content.type-popover .tooltip-caret.position-left{border-top:7px solid transparent;border-bottom:7px solid transparent;border-left:7px solid #fff}.ngx-charts-tooltip-content.type-popover .tooltip-caret.position-top{border-left:7px solid transparent;border-right:7px solid transparent;border-top:7px solid #fff}.ngx-charts-tooltip-content.type-popover .tooltip-caret.position-right{border-top:7px solid transparent;border-bottom:7px solid transparent;border-right:7px solid #fff}.ngx-charts-tooltip-content.type-popover .tooltip-caret.position-bottom{border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #fff}.ngx-charts-tooltip-content.type-tooltip{color:#fff;background:#000000bf;font-size:12px;padding:0 10px;text-align:center;pointer-events:auto}.ngx-charts-tooltip-content.type-tooltip .tooltip-caret.position-left{border-top:7px solid transparent;border-bottom:7px solid transparent;border-left:7px solid rgba(0,0,0,.75)}.ngx-charts-tooltip-content.type-tooltip .tooltip-caret.position-top{border-left:7px solid transparent;border-right:7px solid transparent;border-top:7px solid rgba(0,0,0,.75)}.ngx-charts-tooltip-content.type-tooltip .tooltip-caret.position-right{border-top:7px solid transparent;border-bottom:7px solid transparent;border-right:7px solid rgba(0,0,0,.75)}.ngx-charts-tooltip-content.type-tooltip .tooltip-caret.position-bottom{border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid rgba(0,0,0,.75)}.ngx-charts-tooltip-content .tooltip-label{display:block;line-height:1em;padding:8px 5px 5px;font-size:1em}.ngx-charts-tooltip-content .tooltip-val{display:block;font-size:1.3em;line-height:1em;padding:0 5px 8px}.ngx-charts-tooltip-content .tooltip-caret{position:absolute;z-index:5001;width:0;height:0}.ngx-charts-tooltip-content.position-right{transform:translate3d(10px,0,0)}.ngx-charts-tooltip-content.position-left{transform:translate3d(-10px,0,0)}.ngx-charts-tooltip-content.position-top{transform:translate3d(0,-10px,0)}.ngx-charts-tooltip-content.position-bottom{transform:translate3d(0,10px,0)}.ngx-charts-tooltip-content.animate{opacity:1;transition:opacity .3s,transform .3s;transform:translateZ(0);pointer-events:auto}.area-tooltip-container{padding:5px 0;pointer-events:none}.tooltip-item{text-align:left;line-height:1.2em;padding:5px 0}.tooltip-item .tooltip-item-color{display:inline-block;height:12px;width:12px;margin-right:5px;color:#5b646b;border-radius:3px}\n"] }] }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }, { type: undefined, decorators: [{ type: Inject, args: [PLATFORM_ID] }] }], propDecorators: { host: [{ type: Input }], showCaret: [{ type: Input }], type: [{ type: Input }], placement: [{ type: Input }], alignment: [{ type: Input }], spacing: [{ type: Input }], cssClass: [{ type: Input }], title: [{ type: Input }], template: [{ type: Input }], context: [{ type: Input }], caretElm: [{ type: ViewChild, args: ['caretElm'] }], cssClasses: [{ type: HostBinding, args: ['class'] }], onWindowResize: [{ type: HostListener, args: ['window:resize'] }] } }); class InjectionRegisteryService { constructor(injectionService) { this.injectionService = injectionService; this.defaults = {}; this.components = new Map(); } getByType(type = this.type) { return this.components.get(type); } create(bindings) { return this.createByType(this.type, bindings); } createByType(type, bindings) { bindings = this.assignDefaults(bindings); const component = this.injectComponent(type, bindings); this.register(type, component); return component; } destroy(instance) { const compsByType = this.components.get(instance.componentType); if (compsByType && compsByType.length) { const idx = compsByType.indexOf(instance); if (idx > -1) { const component = compsByType[idx]; component.destroy(); compsByType.splice(idx, 1); } } } destroyAll() { this.destroyByType(this.type); } destroyByType(type) { const comps = this.components.get(type); if (comps && comps.length) { let i = comps.length - 1; while (i >= 0) { this.destroy(comps[i--]); } } } injectComponent(type, bindings) { return this.injectionService.appendComponent(type, bindings); } assignDefaults(bindings) { const inputs = { ...this.defaults.inputs }; const outputs = { ...this.defaults.outputs }; if (!bindings.inputs && !bindings.outputs) { bindings = { inputs: bindings }; } if (inputs) { bindings.inputs = { ...inputs, ...bindings.inputs }; } if (outputs) { bindings.outputs = { ...outputs, ...bindings.outputs }; } return bindings; } register(type, component) { if (!this.components.has(type)) { this.components.set(type, []); } const types = this.components.get(type); types.push(component); } } function isViewContainerRef(x) { return x.element; } /** * Injection service is a helper to append components * dynamically to a known location in the DOM, most * noteably for dialogs/tooltips appending to body. * * @export */ class InjectionService { static { this.globalRootViewContainer = null; } /** * Sets a default global root view container. This is useful for * things like ngUpgrade that doesn't have a ApplicationRef root. * * @param container */ static setGlobalRootViewContainer(container) { InjectionService.globalRootViewContainer = container; } constructor(applicationRef, injector) { this.applicationRef = applicationRef; this.injector = injector; } /** * Gets the root view container to inject the component to. * * @memberOf InjectionService */ getRootViewContainer() { if (this._container) return this._container; if (InjectionService.globalRootViewContainer) return InjectionService.globalRootViewContainer; if (this.applicationRef.components.length) return this.applicationRef.components[0]; throw new Error('View Container not found! ngUpgrade needs to manually set this via setRootViewContainer or setGlobalRootViewContainer.'); } /** * Overrides the default root view container. This is useful for * things like ngUpgrade that doesn't have a ApplicationRef root. * * @param container * * @memberOf InjectionService */ setRootViewContainer(container) { this._container = container; } /** * Gets the html element for a component ref. * * @param componentRef * * @memberOf InjectionService */ getComponentRootNode(component) { if (isViewContainerRef(component)) { return component.element.nativeElement; } if (component.hostView && component.hostView.rootNodes.length > 0) { return component.hostView.rootNodes[0]; } // the top most component root node has no `hostView` return component.location.nativeElement; } /** * Gets the root component container html element. * * @memberOf InjectionService */ getRootViewContainerNode(component) { return this.getComponentRootNode(component); } /** * Projects the bindings onto the component * * @param component * @param options * * @memberOf InjectionService */ projectComponentBindings(component, bindings) { if (bindings) { if (bindings.inputs !== undefined) { const bindingKeys = Object.getOwnPropertyNames(bindings.inputs); for (const bindingName of bindingKeys) { component.instance[bindingName] = bindings.inputs[bindingName]; } } if (bindings.outputs !== undefined) { const eventKeys = Object.getOwnPropertyNames(bindings.outputs); for (const eventName of eventKeys) { component.instance[eventName] = bindings.outputs[eventName]; } } } return component; } /** * Appends a component to a adjacent location * * @param componentClass * @param [options={}] * @param [location] * * @memberOf InjectionService */ appendComponent(componentClass, bindings = {}, location) { if (!location) location = this.getRootViewContainer(); const appendLocation = this.getComponentRootNode(location); const portalHost = new DomPortalOutlet(appendLocation, this.applicationRef, this.injector); const portal = new ComponentPortal(componentClass); const componentRef = portalHost.attach(portal); this.projectComponentBindings(componentRef, bindings); return componentRef; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: InjectionService, deps: [{ token: i0.ApplicationRef }, { token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: InjectionService }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: InjectionService, decorators: [{ type: Injectable }], ctorParameters: () => [{ type: i0.ApplicationRef }, { type: i0.Injector }] }); class TooltipService extends InjectionRegisteryService { constructor(injectionService) { super(injectionService); this.type = TooltipContentComponent; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: TooltipService, deps: [{ token: InjectionService }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: TooltipService }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: TooltipService, decorators: [{ type: Injectable }], ctorParameters: () => [{ type: InjectionService }] }); var LegendPosition; (function (LegendPosition) { LegendPosition["Right"] = "right"; LegendPosition["Below"] = "below"; })(LegendPosition || (LegendPosition = {})); var LegendType; (function (LegendType) { LegendType["ScaleLegend"] = "scaleLegend"; LegendType["Legend"] = "legend"; })(LegendType || (LegendType = {})); var ScaleType; (function (ScaleType) { ScaleType["Time"] = "time"; ScaleType["Linear"] = "linear"; ScaleType["Ordinal"] = "ordinal"; ScaleType["Quantile"] = "quantile"; })(ScaleType || (ScaleType = {})); /** * Formats a label given a date, number or string. * * @export */ function formatLabel(label) { if (label instanceof Date) { label = label.toLocaleDateString(); } else { label = label.toLocaleString(); } return label; } /** * Escapes a label. * * @export */ function escapeLabel(label) { return label.toLocaleString().replace(/[&'`"<>]/g, match => { return { '&': '&amp;', // tslint:disable-next-line: quotemark "'": '&#x27;', '`': '&#x60;', '"': '&quot;', '<': '&lt;', '>': '&gt;' }[match]; }); } class LegendEntryComponent { constructor() { this.isActive = false; this.select = new EventEmitter(); this.activate = new EventEmitter(); this.deactivate = new EventEmitter(); this.toggle = new EventEmitter(); } get trimmedLabel() { return this.formattedLabel || '(empty)'; } onMouseEnter() { this.activate.emit({ name: this.label }); } onMouseLeave() { this.deactivate.emit({ name: this.label }); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: LegendEntryComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.0.4", type: LegendEntryComponent, isStandalone: false, selector: "ngx-charts-legend-entry", inputs: { color: "color", label: "label", formattedLabel: "formattedLabel", isActive: "isActive" }, outputs: { select: "select", activate: "activate", deactivate: "deactivate", toggle: "toggle" }, host: { listeners: { "mouseenter": "onMouseEnter()", "mouseleave": "onMouseLeave()" } }, ngImport: i0, template: ` <span [title]="formattedLabel" tabindex="-1" [class.active]="isActive" (click)="select.emit(formattedLabel)"> <span class="legend-label-color" [style.background-color]="color" (click)="toggle.emit(formattedLabel)"> </span> <span class="legend-label-text"> {{ trimmedLabel }} </span> </span> `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: LegendEntryComponent, decorators: [{ type: Component, args: [{ selector: 'ngx-charts-legend-entry', template: ` <span [title]="formattedLabel" tabindex="-1" [class.active]="isActive" (click)="select.emit(formattedLabel)"> <span class="legend-label-color" [style.background-color]="color" (click)="toggle.emit(formattedLabel)"> </span> <span class="legend-label-text"> {{ trimmedLabel }} </span> </span> `, changeDetection: ChangeDetectionStrategy.OnPush, standalone: false }] }], propDecorators: { color: [{ type: Input }], label: [{ type: Input }], formattedLabel: [{ type: Input }], isActive: [{ type: Input }], select: [{ type: Output }], activate: [{ type: Output }], deactivate: [{ type: Output }], toggle: [{ type: Output }], onMouseEnter: [{ type: HostListener, args: ['mouseenter'] }], onMouseLeave: [{ type: HostListener, args: ['mouseleave'] }] } }); class LegendComponent { constructor(cd) { this.cd = cd; this.horizontal = false; this.labelClick = new EventEmitter(); this.labelActivate = new EventEmitter(); this.labelDeactivate = new EventEmitter(); this.legendEntries = []; } ngOnChanges(changes) { this.update(); } update() { this.cd.markForCheck(); this.legendEntries = this.getLegendEntries(); } getLegendEntries() { const items = []; for (const label of this.data) { const formattedLabel = formatLabel(label); const idx = items.findIndex(i => { return i.label === formattedLabel; }); if (idx === -1) { items.push({ label, formattedLabel, color: this.colors.getColor(label) }); } } return items; } isActive(entry) { if (!this.activeEntries) return false; const item = this.activeEntries.find(d => { return entry.label === d.name; }); return item !== undefined; } activate(item) { this.labelActivate.emit(item); } deactivate(item) { this.labelDeactivate.emit(item); } trackBy(index, item) { return item.label; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: LegendComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.0.4", type: LegendComponent, isStandalone: false, selector: "ngx-charts-legend", inputs: { data: "data", title: "title", colors: "colors", height: "height", width: "width", activeEntries: "activeEntries", horizontal: "horizontal" }, outputs: { labelClick: "labelClick", labelActivate: "labelActivate", labelDeactivate: "labelDeactivate" }, usesOnChanges: true, ngImport: i0, template: ` <div [style.width.px]="width"> <header class="legend-title" *ngIf="title?.length > 0"> <span class="legend-title-text">{{ title }}</span> </header> <div class="legend-wrap"> <ul class="legend-labels" [class.horizontal-legend]="horizontal" [style.max-height.px]="height - 45"> <li *ngFor="let entry of legendEntries; trackBy: trackBy" class="legend-label"> <ngx-charts-legend-entry [label]="entry.label" [formattedLabel]="entry.formattedLabel" [color]="entry.color" [isActive]="isActive(entry)" (select)="labelClick.emit($event)" (activate)="activate($event)" (deactivate)="deactivate($event)" > </ngx-charts-legend-entry> </li> </ul> </div> </div> `, isInline: true, styles: [".chart-legend{display:inline-block;padding:0;width:auto!important}.chart-legend .legend-title{white-space:nowrap;overflow:hidden;margin-left:10px;margin-bottom:5px;font-size:14px;font-weight:700}.chart-legend ul,.chart-legend li{padding:0;margin:0;list-style:none}.chart-legend .horizontal-legend li{display:inline-block}.chart-legend .legend-wrap{width:calc(100% - 10px)}.chart-legend .legend-labels{line-height:85%;list-style:none;text-align:left;float:left;width:100%;border-radius:3px;overflow-y:auto;overflow-x:hidden;white-space:nowrap;background:#0000000d}.chart-legend .legend-label{cursor:pointer;font-size:90%;margin:8px;color:#afb7c8}.chart-legend .legend-label:hover{color:#000;-webkit-transition:.2s;-moz-transition:.2s;transition:.2s}.chart-legend .legend-label .active .legend-label-text{color:#000}.chart-legend .legend-label-color{display:inline-block;height:15px;width:15px;margin-right:5px;color:#5b646b;border-radius:3px}.chart-legend .legend-label-text{display:inline-block;vertical-align:top;line-height:15px;font-size:12px;width:calc(100% - 20px);text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.chart-legend .legend-title-text{vertical-align:bottom;display:inline-block;line-height:16px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: LegendEntryComponent, selector: "ngx-charts-legend-entry", inputs: ["color", "label", "formattedLabel", "isActive"], outputs: ["select", "activate", "deactivate", "toggle"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: LegendComponent, decorators: [{ type: Component, args: [{ selector: 'ngx-charts-legend', template: ` <div [style.width.px]="width"> <header class="legend-title" *ngIf="title?.length > 0"> <span class="legend-title-text">{{ title }}</span> </header> <div class="legend-wrap"> <ul class="legend-labels" [class.horizontal-legend]="horizontal" [style.max-height.px]="height - 45"> <li *ngFor="let entry of legendEntries; trackBy: trackBy" class="legend-label"> <ngx-charts-legend-entry [label]="entry.label" [formattedLabel]="entry.formattedLabel" [color]="entry.color" [isActive]="isActive(entry)" (select)="labelClick.emit($event)" (activate)="activate($event)" (deactivate)="deactivate($event)" > </ngx-charts-legend-entry> </li> </ul> </div> </div> `, encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, styles: [".chart-legend{display:inline-block;padding:0;width:auto!important}.chart-legend .legend-title{white-space:nowrap;overflow:hidden;margin-left:10px;margin-bottom:5px;font-size:14px;font-weight:700}.chart-legend ul,.chart-legend li{padding:0;margin:0;list-style:none}.chart-legend .horizontal-legend li{display:inline-block}.chart-legend .legend-wrap{width:calc(100% - 10px)}.chart-legend .legend-labels{line-height:85%;list-style:none;text-align:left;float:left;width:100%;border-radius:3px;overflow-y:auto;overflow-x:hidden;white-space:nowrap;background:#0000000d}.chart-legend .legend-label{cursor:pointer;font-size:90%;margin:8px;color:#afb7c8}.chart-legend .legend-label:hover{color:#000;-webkit-transition:.2s;-moz-transition:.2s;transition:.2s}.chart-legend .legend-label .active .legend-label-text{color:#000}.chart-legend .legend-label-color{display:inline-block;height:15px;width:15px;margin-right:5px;color:#5b646b;border-radius:3px}.chart-legend .legend-label-text{display:inline-block;vertical-align:top;line-height:15px;font-size:12px;width:calc(100% - 20px);text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.chart-legend .legend-title-text{vertical-align:bottom;display:inline-block;line-height:16px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}\n"] }] }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { data: [{ type: Input }], title: [{ type: Input }], colors: [{ type: Input }], height: [{ type: Input }], width: [{ type: Input }], activeEntries: [{ type: Input }], horizontal: [{ type: Input }], labelClick: [{ type: Output }], labelActivate: [{ type: Output }], labelDeactivate: [{ type: Output }] } }); class ScaleLegendComponent { constructor() { this.horizontal = false; } ngOnChanges(changes) { const gradientValues = this.gradientString(this.colors.range(), this.colors.domain()); const direction = this.horizontal ? 'right' : 'bottom'; this.gradient = `linear-gradient(to ${direction}, ${gradientValues})`; } /** * Generates the string used in the gradient stylesheet properties * @param colors array of colors * @param splits array of splits on a scale of (0, 1) */ gradientString(colors, splits) { // add the 100% splits.push(1); const pairs = []; colors.reverse().forEach((c, i) => { pairs.push(`${c} ${Math.round(splits[i] * 100)}%`); }); return pairs.join(', '); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: ScaleLegendComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.0.4", type: ScaleLegendComponent, isStandalone: false, selector: "ngx-charts-scale-legend", inputs: { valueRange: "valueRange", colors: "colors", height: "height", width: "width", horizontal: "horizontal" }, usesOnChanges: true, ngImport: i0, template: ` <div class="scale-legend" [class.horizontal-legend]="horizontal" [style.height.px]="horizontal ? undefined : height" [style.width.px]="width" > <div class="scale-legend-label"> <span>{{ valueRange[1].toLocaleString() }}</span> </div> <div class="scale-legend-wrap" [style.background]="gradient"></div> <div class="scale-legend-label"> <span>{{ valueRange[0].toLocaleString() }}</span> </div> </div> `, isInline: true, styles: [".chart-legend{display:inline-block;padding:0;width:auto!important}.chart-legend .scale-legend{text-align:center;display:flex;flex-direction:column}.chart-legend .scale-legend-wrap{display:inline-block;flex:1;width:30px;border-radius:5px;margin:0 auto}.chart-legend .scale-legend-label{font-size:12px}.chart-legend .horizontal-legend.scale-legend{flex-direction:row}.chart-legend .horizontal-legend .scale-legend-wrap{width:auto;height:30px;margin:0 16px}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: ScaleLegendComponent, decorators: [{ type: Component, args: [{ selector: 'ngx-charts-scale-legend', template: ` <div class="scale-legend" [class.horizontal-legend]="horizontal" [style.height.px]="horizontal ? undefined : height" [style.width.px]="width" > <div class="scale-legend-label"> <span>{{ valueRange[1].toLocaleString() }}</span> </div> <div class="scale-legend-wrap" [style.background]="gradient"></div> <div class="scale-legend-label"> <span>{{ valueRange[0].toLocaleString() }}</span> </div> </div> `, encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, styles: [".chart-legend{display:inline-block;padding:0;width:auto!important}.chart-legend .scale-legend{text-align:center;display:flex;flex-direction:column}.chart-legend .scale-legend-wrap{display:inline-block;flex:1;width:30px;border-radius:5px;margin:0 auto}.chart-legend .scale-legend-label{font-size:12px}.chart-legend .horizontal-legend.scale-legend{flex-direction:row}.chart-legend .horizontal-legend .scale-legend-wrap{width:auto;height:30px;margin:0 16px}\n"] }] }], propDecorators: { valueRange: [{ type: Input }], colors: [{ type: Input }], height: [{ type: Input }], width: [{ type: Input }], horizontal: [{ type: Input }] } }); class ChartComponent { constructor() { this.showLegend = false; this.animations = true; this.legendLabelClick = new EventEmitter(); this.legendLabelActivate = new EventEmitter(); this.legendLabelDeactivate = new EventEmitter(); this.LegendPosition = LegendPosition; this.LegendType = LegendType; } ngOnChanges(changes) { this.update(); } update() { let legendColumns = 0; if (this.showLegend) { this.legendType = this.getLegendType(); if (!this.legendOptions || this.legendOptions.position === LegendPosition.Right) { if (this.legendType === LegendType.ScaleLegend) { legendColumns = 1; } else { legendColumns = 2; } } } const chartColumns = 12 - legendColumns; this.chartWidth = Math.floor((this.view[0] * chartColumns) / 12.0); this.legendWidth = !this.legendOptions || this.legendOptions.position === LegendPosition.Right ? Math.floor((this.view[0] * legendColumns) / 12.0) : this.chartWidth; } getLegendType() { return this.legendOptions.scaleType === ScaleType.Linear ? LegendType.ScaleLegend : LegendType.Legend; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: ChartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.0.4", type: ChartComponent, isStandalone: false, selector: "ngx-charts-chart", inputs: { view: "view", showLegend: "showLegend", legendOptions: "legendOptions", legendType: "legendType", activeEntries: "activeEntries", animations: "animations" }, outputs: { legendLabelClick: "legendLabelClick", legendLabelActivate: "legendLabelActivate", legendLabelDeactivate: "legendLabelDeactivate" }, providers: [TooltipService], usesOnChanges: true, ngImport: i0, template: ` <div class="ngx-charts-outer" [style.width.px]="view[0]" [style.height.px]="view[1]"> <svg class="ngx-charts" [attr.width]="chartWidth" [attr.height]="view[1]"> <ng-content></ng-content> </svg> <ngx-charts-scale-legend *ngIf="showLegend && legendType === LegendType.ScaleLegend" class="chart-legend" [horizontal]="legendOptions && legendOptions.position === LegendPosition.Below" [valueRange]="legendOptions.domain" [colors]="legendOptions.colors" [height]="view[1]" [width]="legendWidth" > </ngx-charts-scale-legend> <ngx-charts-legend *ngIf="showLegend && legendType === LegendType.Legend" class="chart-legend" [horizontal]="legendOptions && legendOptions.position === LegendPosition.Below" [data]="legendOptions.domain" [title]="legendOptions.title" [colors]="legendOptions.colors" [height]="view[1]" [width]="legendWidth" [activeEntries]="activeEntries" (labelClick)="legendLabelClick.emit($event)" (labelActivate)="legendLabelActivate.emit($event)" (labelDeactivate)="legendLabelDeactivate.emit($event)" > </ngx-charts-legend> </div> `, isInline: true, dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: LegendComponent, selector: "ngx-charts-legend", inputs: ["data", "title", "colors", "height", "width", "activeEntries", "horizontal"], outputs: ["labelClick", "labelActivate", "labelDeactivate"] }, { kind: "comp