UNPKG

@progress/kendo-angular-charts

Version:

Kendo UI Charts for Angular - A comprehensive package for creating beautiful and interactive data visualization. Every chart type, stock charts, and sparklines are included.

480 lines (479 loc) 18.6 kB
/**----------------------------------------------------------------------------------------- * Copyright © 2025 Progress Software Corporation. All rights reserved. * Licensed under commercial license. See LICENSE.md in the project root for more information *-------------------------------------------------------------------------------------------*/ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, Input, NgZone, Output, Renderer2, ViewChild, } from '@angular/core'; import { WatermarkOverlayComponent, isDocumentAvailable, shouldShowValidationUI } from '@progress/kendo-angular-common'; import { L10N_PREFIX, LocalizationService } from '@progress/kendo-angular-l10n'; import { exportImage, exportSVG } from '@progress/kendo-drawing'; import { validatePackage } from '@progress/kendo-licensing'; import { combineLatest } from 'rxjs'; import { auditTime, tap } from 'rxjs/operators'; import { ConfigurationService, THROTTLE_MS } from './common/configuration.service'; import { copyChanges } from './common/copy-changes'; import { SankeyThemeService } from './sankey/theme.service'; import { toSimpleChanges } from './common/to-simple-changes'; import { packageMetadata } from './package-metadata'; import { InstanceEventService } from './sankey/events'; import { Sankey, } from '@progress/kendo-charts'; import { hasObservers } from './common/has-observers'; import { SankeyTooltipPopupComponent } from './sankey/tooltip/tooltip-popup.component'; import { SankeyTooltipTemplateService } from './sankey/tooltip/tooltip-template.service'; import { NgIf } from '@angular/common'; import { LocalizedMessagesDirective } from './sankey/localization/localized-messages.directive'; import { IntlService } from '@progress/kendo-angular-intl'; import * as i0 from "@angular/core"; import * as i1 from "./common/configuration.service"; import * as i2 from "./sankey/theme.service"; import * as i3 from "@progress/kendo-angular-l10n"; import * as i4 from "./sankey/events"; import * as i5 from "@progress/kendo-angular-intl"; /** * The Sankey diagram component. * * @example * ```ts * import { Component } from '@angular/core'; * * _@Component({ * selector: 'my-app', * template: ` * <kendo-sankey [data]="data"> * </kendo-sankey> * `, * }) * export class AppComponent { * public data: SankeyData = { * nodes: [ * { id: 1, label: { text: 'Linux' } }, * { id: 0, label: { text: 'iOS'} }, * { id: 2, label: { text: 'Mobile' } }, * { id: 3, label: { text: 'Desktop' } }, * ], * links: [ * { sourceId: 0, targetId: 2, value: 1 }, * { sourceId: 1, targetId: 2, value: 2 }, * { sourceId: 1, targetId: 3, value: 3 }, * ], * }; * } * ``` */ export class SankeyComponent { element; configurationService; themeService; localizationService; instanceEventService; ngZone; changeDetector; renderer; intlService; /** * The data of the Sankey component containing the `links` and `nodes`. */ data; /** * The default configuration for links. * These settings will be applied to all links unless overridden by individual data items. */ links; /** * The default configuration for nodes. * These settings will be applied to all nodes unless overridden by individual data items. */ nodes; /** * The default configuration for labels. * These settings will be applied to all labels unless overridden by individual data items. */ labels; /** * The title configuration of the Sankey component. */ title; /** * The legend configuration of the Sankey component. */ legend; /** * The configuration of the Sankey tooltip. */ tooltip; /** * If set to `true`, the Sankey component will not perform automatic layout. */ disableAutoLayout; /** * If set to `false`, the Sankey keyboard navigation will be disabled. * By default, navigation is enabled. * * @default true */ navigable = true; /** * The settings for the tooltip popup. */ popupSettings; /** * Fires when the mouse pointer enters a node. Similar to the `mouseenter` event. */ nodeEnter = new EventEmitter(); /** * Fires when the mouse pointer leaves a node. Similar to the `mouseleave` event. */ nodeLeave = new EventEmitter(); /** * Fires when a node is clicked. */ nodeClick = new EventEmitter(); /** * Fires when the mouse pointer enters a link. Similar to the `mouseenter` event, */ linkEnter = new EventEmitter(); /** * Fires when the mouse pointer leaves a link. Similar to the `mouseleave` event. */ linkLeave = new EventEmitter(); /** * Fires when a link is clicked. */ linkClick = new EventEmitter(); tooltipInstance; instanceElement; /** * @hidden */ showLicenseWatermark = false; instance; options; theme; optionsChange; redrawTimeout; destroyed; subscriptions; rtl = false; hostClasses = ['k-chart', 'k-sankey']; constructor(element, configurationService, themeService, localizationService, instanceEventService, ngZone, changeDetector, renderer, intlService) { this.element = element; this.configurationService = configurationService; this.themeService = themeService; this.localizationService = localizationService; this.instanceEventService = instanceEventService; this.ngZone = ngZone; this.changeDetector = changeDetector; this.renderer = renderer; this.intlService = intlService; const isValid = validatePackage(packageMetadata); this.showLicenseWatermark = shouldShowValidationUI(isValid); this.themeService.loadTheme(); this.refreshWait(); } ngOnInit() { if (this.element) { this.hostClasses.forEach(name => { this.renderer.addClass(this.element.nativeElement, name); }); } } ngAfterViewInit() { this.setDirection(); this.subscriptions = this.intlService.changes.subscribe(this.intlChange.bind(this)); this.subscriptions.add(this.localizationService.changes.subscribe(this.rtlChange.bind(this))); } ngOnChanges(changes) { const store = this.configurationService.store; copyChanges(changes, store); store.popupSettings = null; this.configurationService.push(store); } /** * Updates the component fields with the specified values and refreshes the widget. * * Use this method when the configuration values cannot be set through the template. * * @example * ```ts-no-run * sankey.notifyChanges({ title: { text: 'New Title' } }); * ``` * * @param changes An object containing the updated input fields. */ notifyChanges(changes) { this.ngOnChanges(toSimpleChanges(changes)); } ngOnDestroy() { this.destroyed = true; if (this.optionsChange) { this.optionsChange.unsubscribe(); } if (this.instance) { this.instance.destroy(); this.instance = null; } if (this.subscriptions) { this.subscriptions.unsubscribe(); } clearTimeout(this.redrawTimeout); } /** * @hidden */ messageFor(key) { return this.localizationService.get(key); } createInstance(element) { this.instance = new Sankey(element, this.instanceOptions, this.theme); ['nodeEnter', 'nodeLeave', 'nodeClick', 'linkEnter', 'linkLeave', 'linkClick'].forEach((eventName) => this.instance.bind(eventName, (e) => this.trigger(eventName, e))); this.instance.bind('tooltipShow', (e) => this.onShowTooltip(e)); this.instance.bind('tooltipHide', () => this.onHideTooltip()); this.hostClasses.forEach(name => { this.renderer.removeClass(this.instanceElement.nativeElement, name); }); } /** * Exports the Sankey diagram as an image. The export operation is asynchronous and returns a promise. * * @param {ImageExportOptions} options - The parameters for the exported image. * @returns {Promise<string>} - A promise that will be resolved with a PNG image encoded as a Data URI. */ exportImage(options = {}) { return exportImage(this.exportVisual(options), options); } /** * Exports the Sankey diagram as an SVG document. The export operation is asynchronous and returns a promise. * * @param options - The parameters for the exported file. * @returns - A promise that will be resolved with an SVG document that is encoded as a Data URI. */ exportSVG(options = {}) { return exportSVG(this.exportVisual(options), options); } /** * Exports the visual of the Sankey component to a drawing group. * * @param options - The parameters for the export operation. * @returns - The root Group of the scene. */ exportVisual(options = {}) { return this.instance.exportVisual(options); } init() { if (!this.canRender) { return; } const element = this.instanceElement.nativeElement; this.createInstance(element); } /** * Reloads the Sankey appearance settings from the current [Kendo UI Theme]({% slug themesandstyles %}). * * Call this method after loading a different theme stylesheet. */ reloadTheme() { if (!this.instance) { return; } this.themeService.reset(); this.instance.destroy(); this.instance = null; } onShowTooltip(e) { this.run(() => { this.tooltipInstance.show(e); }, true, true); } onHideTooltip() { if (this.tooltipInstance.active) { this.tooltipInstance.hide(); this.detectChanges(); } } trigger(name, e) { const emitter = this.activeEmitter(name); if (emitter) { const args = this.instanceEventService.create(name, e, this); this.run(() => { emitter.emit(args); }); return args.isDefaultPrevented && args.isDefaultPrevented(); } } requiresHandlers(names) { for (let idx = 0; idx < names.length; idx++) { if (this.activeEmitter(names[idx])) { return true; } } return false; } refresh() { clearTimeout(this.redrawTimeout); if (!this.instance) { this.init(); return; } this.updateOptions(); } updateOptions() { this.instance.setOptions(this.instanceOptions); } get canRender() { return isDocumentAvailable() && Boolean(this.instanceElement); } get instanceOptions() { return { ...this.options, rtl: this.rtl, disableKeyboardNavigation: !this.navigable }; } activeEmitter(name) { const emitter = this[name]; if (emitter && emitter.emit && hasObservers(emitter)) { return emitter; } } refreshWait() { this.ngZone.runOutsideAngular(() => { this.optionsChange = combineLatest([this.configurationService.onChange$, this.themeService.onChange$]) .pipe(tap((result) => { this.options = result[0]; this.theme = result[1]; }), auditTime(THROTTLE_MS)) .subscribe(() => { this.refresh(); }); }); } run(callback, inZone = true, detectChanges) { if (inZone) { if (detectChanges) { this.changeDetector.markForCheck(); } this.ngZone.run(callback); } else { callback(); if (detectChanges) { this.detectChanges(); } } } detectChanges() { if (!this.destroyed) { this.changeDetector.detectChanges(); } } intlChange() { if (this.instance) { this.refresh(); } } rtlChange() { if (this.instance && this.rtl !== this.isRTL) { this.refresh(); } } setDirection() { this.rtl = this.isRTL; if (this.element) { this.renderer.setAttribute(this.element.nativeElement, 'dir', this.rtl ? 'rtl' : 'ltr'); } } get isRTL() { return Boolean(this.localizationService.rtl); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SankeyComponent, deps: [{ token: i0.ElementRef }, { token: i1.ConfigurationService }, { token: i2.SankeyThemeService }, { token: i3.LocalizationService }, { token: i4.InstanceEventService }, { token: i0.NgZone }, { token: i0.ChangeDetectorRef }, { token: i0.Renderer2 }, { token: i5.IntlService }], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: SankeyComponent, isStandalone: true, selector: "kendo-sankey", inputs: { data: "data", links: "links", nodes: "nodes", labels: "labels", title: "title", legend: "legend", tooltip: "tooltip", disableAutoLayout: "disableAutoLayout", navigable: "navigable", popupSettings: "popupSettings" }, outputs: { nodeEnter: "nodeEnter", nodeLeave: "nodeLeave", nodeClick: "nodeClick", linkEnter: "linkEnter", linkLeave: "linkLeave", linkClick: "linkClick" }, providers: [ ConfigurationService, LocalizationService, InstanceEventService, SankeyTooltipTemplateService, { provide: L10N_PREFIX, useValue: 'kendo.sankey', }, ], viewQueries: [{ propertyName: "tooltipInstance", first: true, predicate: SankeyTooltipPopupComponent, descendants: true, static: true }, { propertyName: "instanceElement", first: true, predicate: ["instance"], descendants: true, static: true }], exportAs: ["kendoSankey"], usesOnChanges: true, ngImport: i0, template: ` <ng-container kendoSankeyLocalizedMessages i18n-tooltipUnitFormat="kendo.sankey.tooltipUnitFormat|The format string to use when displaying node and link values in the tooltip" tooltipUnitFormat="({0} units)" ></ng-container> <div #instance class="k-chart-surface"></div> <kendo-sankey-tooltip-popup [popupSettings]="popupSettings" [tooltipUnitFormat]="messageFor('tooltipUnitFormat')" > </kendo-sankey-tooltip-popup> <div kendoWatermarkOverlay *ngIf="showLicenseWatermark"></div> `, isInline: true, dependencies: [{ kind: "directive", type: LocalizedMessagesDirective, selector: "[kendoSankeyLocalizedMessages]" }, { kind: "component", type: SankeyTooltipPopupComponent, selector: "kendo-sankey-tooltip-popup", inputs: ["animate", "wrapperClass", "tooltipUnitFormat", "offset"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: WatermarkOverlayComponent, selector: "div[kendoWatermarkOverlay]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SankeyComponent, decorators: [{ type: Component, args: [{ changeDetection: ChangeDetectionStrategy.OnPush, exportAs: 'kendoSankey', providers: [ ConfigurationService, LocalizationService, InstanceEventService, SankeyTooltipTemplateService, { provide: L10N_PREFIX, useValue: 'kendo.sankey', }, ], selector: 'kendo-sankey', template: ` <ng-container kendoSankeyLocalizedMessages i18n-tooltipUnitFormat="kendo.sankey.tooltipUnitFormat|The format string to use when displaying node and link values in the tooltip" tooltipUnitFormat="({0} units)" ></ng-container> <div #instance class="k-chart-surface"></div> <kendo-sankey-tooltip-popup [popupSettings]="popupSettings" [tooltipUnitFormat]="messageFor('tooltipUnitFormat')" > </kendo-sankey-tooltip-popup> <div kendoWatermarkOverlay *ngIf="showLicenseWatermark"></div> `, standalone: true, imports: [LocalizedMessagesDirective, SankeyTooltipPopupComponent, NgIf, WatermarkOverlayComponent] }] }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i1.ConfigurationService }, { type: i2.SankeyThemeService }, { type: i3.LocalizationService }, { type: i4.InstanceEventService }, { type: i0.NgZone }, { type: i0.ChangeDetectorRef }, { type: i0.Renderer2 }, { type: i5.IntlService }]; }, propDecorators: { data: [{ type: Input }], links: [{ type: Input }], nodes: [{ type: Input }], labels: [{ type: Input }], title: [{ type: Input }], legend: [{ type: Input }], tooltip: [{ type: Input }], disableAutoLayout: [{ type: Input }], navigable: [{ type: Input }], popupSettings: [{ type: Input }], nodeEnter: [{ type: Output }], nodeLeave: [{ type: Output }], nodeClick: [{ type: Output }], linkEnter: [{ type: Output }], linkLeave: [{ type: Output }], linkClick: [{ type: Output }], tooltipInstance: [{ type: ViewChild, args: [SankeyTooltipPopupComponent, { static: true }] }], instanceElement: [{ type: ViewChild, args: ['instance', { static: true }] }] } });