zz-chart
Version:
Alauda Chart components by Alauda Frontend Team
314 lines • 9.17 kB
JavaScript
import { isBoolean, isObject, merge, set, cloneDeep } from 'lodash-es';
import { Coordinate } from '../components/coordinate.js';
import { getInteraction } from '../interaction/index.js';
import Interaction from '../interaction/interaction.js';
import { reactive } from '../reactivity/index.js';
import { UPlotViewStrategy, ViewStrategyManager, InternalViewStrategy, } from '../strategy/index.js';
import { getTheme } from '../theme/index.js';
import { ChartEvent, } from '../types/index.js';
import { getChartColor } from '../utils/index.js';
import EventEmitter from './event-emitter.js';
export class View extends EventEmitter {
constructor(props) {
super();
/** 所有的组件 */
this.components = new Map();
/** 图形组件 */
this.shapeComponents = new Map();
// 配置信息存储
this.options = {};
this.interactions = new Map();
this.systemThemeType = 'light';
this.size = { width: 0, height: 0 };
this.fixedSize = {
width: 0,
height: 0,
};
this.shapeCache = new Map();
this.systemChangeTheme = (e) => {
const theme = e.matches ? 'dark' : 'light';
this.theme(theme);
};
const { width, height, chartEle, ele, options, data, theme, chartOption, padding, defaultInteractions, } = props;
this.reactivity = reactive(chartOption, this);
this.chartContainer = chartEle;
this.container = ele;
if (options) {
this.options = { ...options, padding };
}
data && this.data(data);
this.defaultInteractions = defaultInteractions;
this.size = { ...this.size, width, height };
this.fixedSize = {
width,
height,
};
this.initTheme(theme);
this.init();
}
// 判断是否是 element active [point]
get isElementAction() {
return !!this.shapeComponents.get('point');
}
get hideTooltip() {
return this.options.tooltip === false;
}
init() {
this.initViewStrategy();
this.initComponent();
}
reactive() {
return this.reactivity.reactiveObject;
}
render(size) {
if (size) {
this.size = size;
}
[...this.components.values()].forEach(c => c.render());
this.strategy.forEach(item => {
item.render();
});
// TODO: 去除依赖 shape 判断 is point
this.initDefaultInteractions(this.defaultInteractions);
}
interaction(name, steps) {
const interactionStep = getInteraction(name);
if ((steps || interactionStep) && !this.interactions.get(name)) {
const step = steps && interactionStep
? merge(interactionStep, steps)
: steps || interactionStep;
const interaction = new Interaction(this, cloneDeep(step));
interaction.init();
this.interactions.set(name, interaction);
}
}
initDefaultInteractions(interactions) {
for (const name of interactions) {
if (name) {
this.interaction(name);
}
}
}
/**
* 基于注册组件初始化
*/
initComponent() {
this.createCoordinate();
this.strategyManage.getComponent().forEach(c => {
this.components.set(c.name, c);
});
}
/**
* 初始化策略 uPlot internal
*/
initViewStrategy() {
this.strategyManage = new ViewStrategyManager();
const internal = new InternalViewStrategy(this);
this.strategyManage.add(internal);
const uPlot = new UPlotViewStrategy(this);
this.strategyManage.add(uPlot);
this.strategy = this.strategyManage.getAllStrategy();
}
/**
*
* @param theme 主题
* 不设置默认根据系统切换 light dark
*/
initTheme(theme) {
this.mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
this.systemThemeType = this.mediaQuery.matches ? 'dark' : 'light';
if (!theme || theme?.type === 'system') {
this.bindThemeListener();
}
this.theme(theme || this.systemThemeType);
}
bindThemeListener() {
this.mediaQuery.addEventListener('change', this.systemChangeTheme);
}
unbindThemeListener() {
this.mediaQuery.removeEventListener('change', this.systemChangeTheme);
}
/**
* 设置主题。
* @param theme 主题名或者主题配置
* @returns View
*/
theme(theme) {
this.themeObject = isObject(theme)
? getTheme(theme.type, theme)
: getTheme(theme);
this.emit(ChartEvent.THEME_CHANGE);
return this;
}
/**
* 获取主题配置。
* @returns themeObject
*/
getTheme() {
return this.themeObject;
}
/**
* 获取 view options 配置
*/
getOption() {
return this.options;
}
/**
* 装载数据源。
*
* ```ts
* chart.data();
* ```
*
* @param data 数据源。
* @returns View
*/
data(data) {
data.forEach((d, index) => {
if (!d.color) {
d.color = getChartColor(index);
}
});
set(this.options, 'data', data);
this.emit(ChartEvent.DATA_CHANGE, data);
return this;
}
getData() {
return this.options.data || [];
}
// -------------- Component ---------------//
title(titleOption) {
set(this.options, 'title', titleOption);
return this;
}
legend(legendOption) {
set(this.options, 'legend', legendOption);
return this.components.get('legend');
}
axis(field, axisOption) {
if (isBoolean(field)) {
set(this.options, ['axis'], field);
}
else {
set(this.options, ['axis', field], axisOption);
}
return this;
}
/**
* 对x y 度量进行配置。
* ```
* @param field 度量 x y
* @param scaleOption 度量配置
*/
scale(field, axisOption) {
if (isBoolean(field)) {
set(this.options, ['scale'], field);
}
else {
set(this.options, ['scale', field], axisOption);
}
return this.components.get('scale');
}
setScale(field, limits) {
const scale = this.components.get('scale');
scale.setScale(field, limits);
}
/**
* 创建坐标系
* @private
*/
createCoordinate() {
this.coordinateInstance = new Coordinate(this);
}
/**
* 坐标系配置。
*
* ```ts
* // 直角坐标系,并进行转置变换
* chart.coordinate().transpose();
* ```
* @returns
*/
coordinate(option) {
set(this.options, 'coordinate', option);
// 更新 coordinate 配置
// this.coordinateInstance.update(option);
return this.coordinateInstance;
}
getCoordinate() {
return this.coordinateInstance;
}
tooltip(tooltipOption) {
set(this.options, 'tooltip', tooltipOption);
return this;
}
/**
* 辅助标记配置
*/
annotation() {
return this.components.get('annotation');
}
// 命令式设置 option
setOption(name, option) {
set(this.options, name, option);
return this;
}
redraw() {
this.emit(ChartEvent.HOOKS_REDRAW);
}
setShape(name, shape) {
const shapeValue = this.shapeCache.get(name);
if (shapeValue) {
this.shapeCache.set(name, [...shapeValue, shape]);
return;
}
this.shapeCache.set(name, [shape]);
}
getShapeList() {
const keys = Array.from(this.shapeCache.keys());
return keys
.map(key => {
return this.shapeCache.get(key).flat();
})
.flat();
}
getShapeDataName() {
return this.getShapeList()
.map((shape) => {
return shape.getSeries();
})
.flat()
.map(s => s.label);
}
/**
* 生命周期:销毁,完全无法使用。
*/
destroy() {
// ...
this.chartContainer.innerHTML = '';
this.options = {};
this.shapeCache.clear();
this.reactivity.unsubscribe();
[...this.components.values()].forEach(c => c.destroy());
[...this.shapeComponents.values()].forEach(c => c.destroy());
this.strategyManage.getStrategy('uPlot')?.destroy();
this.unbindThemeListener();
this.off();
}
}
/**
* 注册 geometry 组件
* @param name
* @param Ctor
* @returns Geometry
*/
export function registerShape(name, Ctor) {
const key = name.toLowerCase();
// 语法糖,在 view API 上增加原型方法
View.prototype[key] = function (options) {
const shape = new Ctor(this, options);
this.shapeComponents.set(key, shape);
return shape;
};
}
//# sourceMappingURL=view.js.map