UNPKG

ngx-echarts

Version:

<div align="center"> <a href="https://xieziyu.github.io/ngx-echarts"> <img src="./src/assets/img/logo.svg" alt="Logo" width="80"> </a> <h3 align="center">NGX-ECHARTS</h3> <p align="center"> Angular directive for <a href="https://github.com

322 lines (313 loc) 16.8 kB
import * as i0 from '@angular/core'; import { InjectionToken, inject, ElementRef, NgZone, input, output, Directive, NgModule } from '@angular/core'; import { outputFromObservable, outputToObservable } from '@angular/core/rxjs-interop'; import { ReplaySubject, Subscription, Subject, asyncScheduler, Observable } from 'rxjs'; import { throttleTime, switchMap } from 'rxjs/operators'; const NGX_ECHARTS_CONFIG = new InjectionToken('NGX_ECHARTS_CONFIG'); class ChangeFilterV2 { constructor() { this.subject = new ReplaySubject(1); this.subscriptions = new Subscription(); } doFilter(changes) { this.subject.next(changes); } dispose() { this.subscriptions.unsubscribe(); } notEmpty(key, handler) { this.subscriptions.add(this.subject.subscribe(changes => { if (changes[key]) { const value = changes[key].currentValue; if (value !== undefined && value !== null) { handler(value); } } })); } has(key, handler) { this.subscriptions.add(this.subject.subscribe(changes => { if (changes[key]) { const value = changes[key].currentValue; handler(value); } })); } notFirst(key, handler) { this.subscriptions.add(this.subject.subscribe(changes => { if (changes[key] && !changes[key].isFirstChange()) { const value = changes[key].currentValue; handler(value); } })); } notFirstAndEmpty(key, handler) { this.subscriptions.add(this.subject.subscribe(changes => { if (changes[key] && !changes[key].isFirstChange()) { const value = changes[key].currentValue; if (value !== undefined && value !== null) { handler(value); } } })); } } class NgxEchartsDirective { constructor() { this.el = inject(ElementRef); this.ngZone = inject(NgZone); this.config = inject(NGX_ECHARTS_CONFIG); this.options = input(null); this.theme = input(this.config.theme ?? null); this.initOpts = input(null); this.merge = input(null); this.autoResize = input(true); this.loading = input(false); this.loadingType = input('default'); this.loadingOpts = input(null); // ngx-echarts events this.chartInit = output(); this.optionsError = output(); // echarts mouse events this.chartClick = outputFromObservable(this.createLazyEvent('click')); this.chartDblClick = outputFromObservable(this.createLazyEvent('dblclick')); this.chartMouseDown = outputFromObservable(this.createLazyEvent('mousedown')); this.chartMouseMove = outputFromObservable(this.createLazyEvent('mousemove')); this.chartMouseUp = outputFromObservable(this.createLazyEvent('mouseup')); this.chartMouseOver = outputFromObservable(this.createLazyEvent('mouseover')); this.chartMouseOut = outputFromObservable(this.createLazyEvent('mouseout')); this.chartGlobalOut = outputFromObservable(this.createLazyEvent('globalout')); this.chartContextMenu = outputFromObservable(this.createLazyEvent('contextmenu')); // echarts events this.chartHighlight = outputFromObservable(this.createLazyEvent('highlight')); this.chartDownplay = outputFromObservable(this.createLazyEvent('downplay')); this.chartSelectChanged = outputFromObservable(this.createLazyEvent('selectchanged')); this.chartLegendSelectChanged = outputFromObservable(this.createLazyEvent('legendselectchanged')); this.chartLegendSelected = outputFromObservable(this.createLazyEvent('legendselected')); this.chartLegendUnselected = outputFromObservable(this.createLazyEvent('legendunselected')); this.chartLegendLegendSelectAll = outputFromObservable(this.createLazyEvent('legendselectall')); this.chartLegendLegendInverseSelect = outputFromObservable(this.createLazyEvent('legendinverseselect')); this.chartLegendScroll = outputFromObservable(this.createLazyEvent('legendscroll')); this.chartDataZoom = outputFromObservable(this.createLazyEvent('datazoom')); this.chartDataRangeSelected = outputFromObservable(this.createLazyEvent('datarangeselected')); this.chartGraphRoam = outputFromObservable(this.createLazyEvent('graphroam')); this.chartGeoRoam = outputFromObservable(this.createLazyEvent('georoam')); this.chartTreeRoam = outputFromObservable(this.createLazyEvent('treeroam')); this.chartTimelineChanged = outputFromObservable(this.createLazyEvent('timelinechanged')); this.chartTimelinePlayChanged = outputFromObservable(this.createLazyEvent('timelineplaychanged')); this.chartRestore = outputFromObservable(this.createLazyEvent('restore')); this.chartDataViewChanged = outputFromObservable(this.createLazyEvent('dataviewchanged')); this.chartMagicTypeChanged = outputFromObservable(this.createLazyEvent('magictypechanged')); this.chartGeoSelectChanged = outputFromObservable(this.createLazyEvent('geoselectchanged')); this.chartGeoSelected = outputFromObservable(this.createLazyEvent('geoselected')); this.chartGeoUnselected = outputFromObservable(this.createLazyEvent('geounselected')); this.chartAxisAreaSelected = outputFromObservable(this.createLazyEvent('axisareaselected')); this.chartBrush = outputFromObservable(this.createLazyEvent('brush')); this.chartBrushEnd = outputFromObservable(this.createLazyEvent('brushend')); this.chartBrushSelected = outputFromObservable(this.createLazyEvent('brushselected')); this.chartGlobalCursorTaken = outputFromObservable(this.createLazyEvent('globalcursortaken')); this.chartRendered = outputFromObservable(this.createLazyEvent('rendered')); this.chartFinished = outputFromObservable(this.createLazyEvent('finished')); this.animationFrameID = null; this.chart$ = new ReplaySubject(1); this.resize$ = new Subject(); this.changeFilter = new ChangeFilterV2(); this.resizeObFired = false; this.echarts = this.config.echarts; } ngOnChanges(changes) { this.changeFilter.doFilter(changes); } ngOnInit() { if (!window.ResizeObserver) { throw new Error('please install a polyfill for ResizeObserver'); } this.resizeSub = this.resize$ .pipe(throttleTime(100, asyncScheduler, { leading: false, trailing: true })) .subscribe(() => this.resize()); if (this.autoResize()) { // https://github.com/xieziyu/ngx-echarts/issues/413 this.resizeOb = this.ngZone.runOutsideAngular(() => new window.ResizeObserver(entries => { for (const entry of entries) { if (entry.target === this.el.nativeElement) { // Ignore first fire on insertion, no resize actually happened if (!this.resizeObFired) { this.resizeObFired = true; } else { this.animationFrameID = window.requestAnimationFrame(() => { this.resize$.next(); }); } } } })); this.resizeOb.observe(this.el.nativeElement); } this.changeFilter.notFirstAndEmpty('options', opt => this.onOptionsChange(opt)); this.changeFilter.notFirstAndEmpty('merge', opt => this.setOption(opt)); this.changeFilter.has('loading', v => this.toggleLoading(!!v)); this.changeFilter.notFirst('theme', () => this.refreshChart()); } ngOnDestroy() { window.clearTimeout(this.initChartTimer); if (this.resizeSub) { this.resizeSub.unsubscribe(); } if (this.animationFrameID) { window.cancelAnimationFrame(this.animationFrameID); } if (this.resizeOb) { this.resizeOb.unobserve(this.el.nativeElement); } if (this.loadingSub) { this.loadingSub.unsubscribe(); } this.changeFilter.dispose(); this.dispose(); } ngAfterViewInit() { this.initChartTimer = window.setTimeout(() => this.initChart()); } dispose() { if (this.chart) { if (!this.chart.isDisposed()) { this.chart.dispose(); } this.chart = null; } } /** * resize chart */ resize() { if (this.chart) { this.chart.resize(); } } toggleLoading(loading) { if (this.chart) { loading ? this.chart.showLoading(this.loadingType(), this.loadingOpts()) : this.chart.hideLoading(); } else { this.loadingSub = this.chart$.subscribe(chart => loading ? chart.showLoading(this.loadingType(), this.loadingOpts()) : chart.hideLoading()); } } setOption(option, opts) { if (this.chart) { try { this.chart.setOption(option, opts); } catch (e) { console.error(e); this.optionsError.emit(e); } } } /** * dispose old chart and create a new one. */ async refreshChart() { this.dispose(); await this.initChart(); } createChart() { const dom = this.el.nativeElement; if (window && window.getComputedStyle) { const prop = window.getComputedStyle(dom, null).getPropertyValue('height'); if ((!prop || prop === '0px') && (!dom.style.height || dom.style.height === '0px')) { dom.style.height = '400px'; } } // here a bit tricky: we check if the echarts module is provided as function returning native import('...') then use the promise // otherwise create the function that imitates behaviour above with a provided as is module return this.ngZone.runOutsideAngular(() => { const load = typeof this.echarts === 'function' ? this.echarts : () => Promise.resolve(this.echarts); return load().then(({ init }) => init(dom, this.theme ?? this.config?.theme, this.initOpts())); }); } async initChart() { await this.onOptionsChange(this.options()); const merge = this.merge(); if (merge && this.chart) { this.setOption(merge); } } async onOptionsChange(opt) { if (!opt) { return; } if (this.chart) { this.setOption(this.options(), true); } else { this.chart = await this.createChart(); this.chart$.next(this.chart); this.chartInit.emit(this.chart); this.setOption(this.options(), true); } } // allows to lazily bind to only those events that are requested through the `output()` by parent components // see https://stackoverflow.com/questions/51787972/optimal-reentering-the-ngzone-from-eventemitter-event for more info createLazyEvent(eventName) { return outputToObservable(this.chartInit).pipe(switchMap((chart) => new Observable(observer => { chart.on(eventName, (data) => this.ngZone.run(() => observer.next(data))); return () => { if (this.chart) { if (!this.chart.isDisposed()) { chart.off(eventName); } } }; }))); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: NgxEchartsDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.0.2", type: NgxEchartsDirective, isStandalone: true, selector: "echarts, [echarts]", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, theme: { classPropertyName: "theme", publicName: "theme", isSignal: true, isRequired: false, transformFunction: null }, initOpts: { classPropertyName: "initOpts", publicName: "initOpts", isSignal: true, isRequired: false, transformFunction: null }, merge: { classPropertyName: "merge", publicName: "merge", isSignal: true, isRequired: false, transformFunction: null }, autoResize: { classPropertyName: "autoResize", publicName: "autoResize", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, loadingType: { classPropertyName: "loadingType", publicName: "loadingType", isSignal: true, isRequired: false, transformFunction: null }, loadingOpts: { classPropertyName: "loadingOpts", publicName: "loadingOpts", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { chartInit: "chartInit", optionsError: "optionsError", chartClick: "chartClick", chartDblClick: "chartDblClick", chartMouseDown: "chartMouseDown", chartMouseMove: "chartMouseMove", chartMouseUp: "chartMouseUp", chartMouseOver: "chartMouseOver", chartMouseOut: "chartMouseOut", chartGlobalOut: "chartGlobalOut", chartContextMenu: "chartContextMenu", chartHighlight: "chartHighlight", chartDownplay: "chartDownplay", chartSelectChanged: "chartSelectChanged", chartLegendSelectChanged: "chartLegendSelectChanged", chartLegendSelected: "chartLegendSelected", chartLegendUnselected: "chartLegendUnselected", chartLegendLegendSelectAll: "chartLegendLegendSelectAll", chartLegendLegendInverseSelect: "chartLegendLegendInverseSelect", chartLegendScroll: "chartLegendScroll", chartDataZoom: "chartDataZoom", chartDataRangeSelected: "chartDataRangeSelected", chartGraphRoam: "chartGraphRoam", chartGeoRoam: "chartGeoRoam", chartTreeRoam: "chartTreeRoam", chartTimelineChanged: "chartTimelineChanged", chartTimelinePlayChanged: "chartTimelinePlayChanged", chartRestore: "chartRestore", chartDataViewChanged: "chartDataViewChanged", chartMagicTypeChanged: "chartMagicTypeChanged", chartGeoSelectChanged: "chartGeoSelectChanged", chartGeoSelected: "chartGeoSelected", chartGeoUnselected: "chartGeoUnselected", chartAxisAreaSelected: "chartAxisAreaSelected", chartBrush: "chartBrush", chartBrushEnd: "chartBrushEnd", chartBrushSelected: "chartBrushSelected", chartGlobalCursorTaken: "chartGlobalCursorTaken", chartRendered: "chartRendered", chartFinished: "chartFinished" }, exportAs: ["echarts"], usesOnChanges: true, ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: NgxEchartsDirective, decorators: [{ type: Directive, args: [{ standalone: true, selector: 'echarts, [echarts]', exportAs: 'echarts', }] }] }); function provideEchartsCore(config) { return { provide: NGX_ECHARTS_CONFIG, useValue: config, }; } class NgxEchartsModule { static forRoot(config) { return { ngModule: NgxEchartsModule, providers: [provideEchartsCore(config)], }; } static forChild() { return { ngModule: NgxEchartsModule, }; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: NgxEchartsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); } static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.0.2", ngImport: i0, type: NgxEchartsModule, imports: [NgxEchartsDirective], exports: [NgxEchartsDirective] }); } static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: NgxEchartsModule }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: NgxEchartsModule, decorators: [{ type: NgModule, args: [{ imports: [NgxEchartsDirective], exports: [NgxEchartsDirective], }] }] }); /* * Public API Surface of ngx-echarts */ /** * Generated bundle index. Do not edit. */ export { NGX_ECHARTS_CONFIG, NgxEchartsDirective, NgxEchartsModule, provideEchartsCore }; //# sourceMappingURL=ngx-echarts.mjs.map