ohayolibs
Version:
Ohayo is a set of essential modules for ohayojp.
185 lines (161 loc) • 4.72 kB
text/typescript
import { Platform } from '@angular/cdk/platform';
import {
ChangeDetectionStrategy,
Component,
ElementRef,
EventEmitter,
Input,
NgZone,
OnChanges,
OnDestroy,
OnInit,
Output,
TemplateRef,
ViewChild,
ViewEncapsulation,
} from '@angular/core';
import { Chart, Event, Types } from '@antv/g2';
import { G2InteractionType } from '@ohayo/chart/core';
import { OhayoConfigService, BooleanInput, InputBoolean, InputNumber, NumberInput } from '@ohayo/util';
import { NzSafeAny } from 'ng-zorro-antd/core/types';
import { fromEvent, Subscription } from 'rxjs';
import { debounceTime, filter } from 'rxjs/operators';
const TITLE_HEIGHT = 41;
export interface G2BarData {
x: NzSafeAny;
y: NzSafeAny;
color?: string;
[key: string]: NzSafeAny;
}
export interface G2BarClickItem {
item: G2BarData;
ev: Event;
}
export class G2BarComponent implements OnInit, OnChanges, OnDestroy {
static ngAcceptInputType_delay: NumberInput;
static ngAcceptInputType_height: NumberInput;
static ngAcceptInputType_autoLabel: BooleanInput;
private resize$: Subscription;
private _chart: Chart;
private node: ElementRef;
get chart(): Chart {
return this._chart;
}
// #region fields
delay = 0;
title: string | TemplateRef<void>;
color = 'rgba(24, 144, 255, 0.85)';
height = 0;
padding: number | number[] | 'auto' = 'auto';
data: G2BarData[] = [];
autoLabel = true;
interaction: G2InteractionType = 'none';
theme: string | Types.LooseObject;
clickItem = new EventEmitter<G2BarClickItem>();
// #endregion
constructor(private ngZone: NgZone, configSrv: OhayoConfigService, private platform: Platform) {
configSrv.attachKey(this, 'chart', 'theme');
}
private getHeight(): number {
return this.title ? this.height - TITLE_HEIGHT : this.height;
}
private install(): void {
const { node, padding, interaction, theme } = this;
const container = node.nativeElement as HTMLElement;
const chart = (this._chart = new Chart({
container,
autoFit: true,
height: this.getHeight(),
padding,
theme,
}));
this.updatelabel();
chart.axis('y', {
title: null,
line: null,
tickLine: null,
});
chart.scale({
x: {
type: 'cat',
},
y: {
min: 0,
},
});
chart.tooltip({
showTitle: false,
});
if (interaction !== 'none') {
chart.interaction(interaction);
}
chart.legend(false);
chart
.interval()
.position('x*y')
.color('x*y', (x, y) => {
const colorItem = this.data.find(w => w.x === x && w.y === y);
return colorItem && colorItem.color ? colorItem.color : this.color;
})
.tooltip('x*y', (x, y) => ({ name: x, value: y }));
chart.on(`interval:click`, (ev: Event) => {
this.ngZone.run(() => this.clickItem.emit({ item: ev.data?.data, ev }));
});
this.attachChart();
}
private attachChart(): void {
const { _chart, padding, data } = this;
if (!_chart || !data || data.length <= 0) return;
this.installResizeEvent();
const height = this.getHeight();
if (_chart.height !== height) {
_chart.height = height;
}
_chart.padding = padding;
_chart.data(data);
_chart.render();
}
private updatelabel(): void {
const { node, data, _chart } = this;
const canvasWidth = node.nativeElement.clientWidth;
const minWidth = data.length * 30;
_chart.axis('x', canvasWidth > minWidth).render();
}
private installResizeEvent(): void {
if (!this.autoLabel || this.resize$) return;
this.resize$ = fromEvent(window, 'resize')
.pipe(
filter(() => !!this._chart),
debounceTime(200),
)
.subscribe(() => this.ngZone.runOutsideAngular(() => this.updatelabel()));
}
ngOnInit(): void {
if (!this.platform.isBrowser) {
return;
}
this.ngZone.runOutsideAngular(() => setTimeout(() => this.install(), this.delay));
}
ngOnChanges(): void {
this.ngZone.runOutsideAngular(() => this.attachChart());
}
ngOnDestroy(): void {
if (this.resize$) {
this.resize$.unsubscribe();
}
if (this._chart) {
this.ngZone.runOutsideAngular(() => this._chart.destroy());
}
}
}