ohayolibs
Version:
Ohayo is a set of essential modules for ohayojp.
232 lines (206 loc) • 6.4 kB
text/typescript
import { Platform } from '@angular/cdk/platform';
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
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';
export interface G2PieData {
x: any;
y: number;
[key: string]: any;
}
export interface G2PieClickItem {
item: G2PieData;
ev: Event;
}
export class G2PieComponent implements OnInit, OnDestroy, OnChanges {
static ngAcceptInputType_delay: NumberInput;
static ngAcceptInputType_height: NumberInput;
static ngAcceptInputType_animate: BooleanInput;
static ngAcceptInputType_hasLegend: BooleanInput;
static ngAcceptInputType_percent: NumberInput;
static ngAcceptInputType_tooltip: BooleanInput;
static ngAcceptInputType_lineWidth: NumberInput;
static ngAcceptInputType_blockMaxWidth: NumberInput;
static ngAcceptInputType_select: BooleanInput;
private node: ElementRef;
private _chart: Chart;
private percentColor: (value: string) => string;
legendData: NzSafeAny[] = [];
isPercent: boolean;
// #region fields
delay = 0;
animate = true;
color = 'rgba(24, 144, 255, 0.85)';
subTitle: string | TemplateRef<void>;
total: string | TemplateRef<void>;
height = 0;
hasLegend = false;
inner = 0.75;
padding: number | number[] | 'auto' = [12, 0, 12, 0];
percent: number;
tooltip = true;
lineWidth = 0;
blockMaxWidth = 380;
select = true;
valueFormat: (y: number) => string;
data: G2PieData[] = [];
colors: any[];
interaction: G2InteractionType = 'none';
theme: string | Types.LooseObject;
clickItem = new EventEmitter<G2PieClickItem>();
// #endregion
get block(): boolean {
return this.hasLegend && this.el.nativeElement.clientWidth <= this.blockMaxWidth;
}
get chart(): Chart {
return this._chart;
}
constructor(
private el: ElementRef<HTMLElement>,
private ngZone: NgZone,
private cdr: ChangeDetectorRef,
configSrv: OhayoConfigService,
private platform: Platform,
) {
configSrv.attachKey(this, 'chart', 'theme');
}
private fixData(): void {
const { percent, color } = this;
this.isPercent = percent != null;
if (this.isPercent) {
this.select = false;
this.tooltip = false;
this.percentColor = (value: string) => (value === '占比' ? color || 'rgba(24, 144, 255, 0.85)' : '#F0F2F5');
this.data = [
{
x: '占比',
y: percent,
},
{
x: '反比',
y: 100 - percent,
},
];
}
}
private install(): void {
const { node, height, padding, tooltip, inner, hasLegend, interaction, theme } = this;
const chart = (this._chart = new Chart({
container: node.nativeElement,
autoFit: true,
height,
padding,
theme,
}));
if (!tooltip) {
chart.tooltip(false);
} else {
chart.tooltip({
showTitle: false,
showMarkers: false,
});
}
if (interaction !== 'none') {
chart.interaction(interaction);
}
chart.axis(false).legend(false).coordinate('theta', { innerRadius: inner });
chart.filter('x', (_val: any, item: any) => item.checked !== false);
chart
.interval()
.adjust('stack')
.position('y')
.tooltip('x*percent', (name: string, p: number) => ({
name,
value: `${hasLegend ? p : (p * 100).toFixed(2)} %`,
}))
.state({});
chart.on(`interval:click`, (ev: Event) => {
this.ngZone.run(() => this.clickItem.emit({ item: ev.data?.data, ev }));
});
this.attachChart();
}
private attachChart(): void {
const { _chart, height, padding, animate, data, lineWidth, isPercent, percentColor, colors } = this;
if (!_chart) return;
_chart.height = height;
_chart.padding = padding;
_chart.animate(animate);
_chart.geometries[0].style({ lineWidth, stroke: '#fff' }).color('x', isPercent ? percentColor : colors);
_chart.scale({
x: {
type: 'cat',
range: [0, 1],
},
});
// 转化 percent
const totalSum = data.reduce((cur, item) => cur + item.y, 0);
for (const item of data) {
item.percent = totalSum === 0 ? 0 : item.y / totalSum;
}
_chart.changeData(data);
_chart.render();
this.ngZone.run(() => this.genLegend());
}
private genLegend(): void {
const { hasLegend, isPercent, cdr, _chart } = this;
if (!hasLegend || isPercent) return;
this.legendData = _chart.geometries[0].dataArray.map((item: any) => {
const origin = item[0]._origin;
origin.color = item[0].color;
origin.checked = true;
origin.percent = (origin.percent * 100).toFixed(2);
return origin;
});
cdr.detectChanges();
}
_click(i: number): void {
const { legendData, _chart } = this;
legendData[i].checked = !legendData[i].checked;
_chart.render();
}
ngOnInit(): void {
if (!this.platform.isBrowser) {
return;
}
this.ngZone.runOutsideAngular(() => setTimeout(() => this.install(), this.delay));
}
ngOnChanges(): void {
this.fixData();
this.ngZone.runOutsideAngular(() => this.attachChart());
}
ngOnDestroy(): void {
if (this._chart) {
this.ngZone.runOutsideAngular(() => this._chart.destroy());
}
}
}