@swimlane/ngx-charts
Version:
Declarative Charting Framework for Angular
999 lines (985 loc) • 947 kB
JavaScript
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 {
'&': '&',
// tslint:disable-next-line: quotemark
"'": ''',
'`': '`',
'"': '"',
'<': '<',
'>': '>'
}[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