Skip to content

Commit

Permalink
Merge pull request #222 from VisActor/feat/vchart-editor-runtime
Browse files Browse the repository at this point in the history
Feat/vchart editor runtime
  • Loading branch information
xuefei1313 authored Jan 26, 2025
2 parents dfcbec6 + 55e3800 commit e1918f1
Show file tree
Hide file tree
Showing 8 changed files with 1,571 additions and 5 deletions.
55 changes: 55 additions & 0 deletions packages/vstory-core/src/character/chart/runtime/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,58 @@ export const SeriesMarkStyleMap: {

export const FieldLink = '_filedLink_';
export const ValueLink = '_valueLink_';

export enum LineSymbolType {
none = 'none',
arrow = 'arrow',
solidArrow = 'solidArrow',
hollowArrow = 'hollowArrow',
solidCircle = 'solidCircle',
hollowCircle = 'hollowCircle'
}

export const lineSymbolPathMap = {
[LineSymbolType.none]: '',
[LineSymbolType.arrow]: 'M -1 1 L 0 0 L 1 1',
[LineSymbolType.solidArrow]: 'M -1 1.5 L 0 0 L 1 1.5 Z',
[LineSymbolType.hollowArrow]: 'M -1 1.5 L 0 0 L 1 1.5 Z',
[LineSymbolType.solidCircle]: 'M 1 0 A 1 1 0 1 1 -1 0 A 1 1 0 1 1 1 0',
[LineSymbolType.hollowCircle]: 'M 1 0 A 1 1 0 1 1 -1 0 A 1 1 0 1 1 1 0'
};

// 不支持标注的图表
export const MARKER_NOT_SUPPORT_CHARTS = [
'pie',
'rose',
'radar',
'sequence',
'map',
'circularProgress',
'linearProgress',
'wordCloud',
'wordCloud3d',
'funnel',
'funnel3d',
'boxPlot',
'gauge',
'sankey',
'treemap',
'sunburst',
'circlePacking',
'heatmap',
'correlation'
];

export enum MarkerTypeEnum {
horizontalLine = 'h-line', // 水平值线
verticalLine = 'v-line', // 垂直值线
horizontalArea = 'h-area', // 水平区域标注
verticalArea = 'v-area', // 垂直区域标注
growthLine = 'growth-line', // 复合增长标注
totalDiffLine = 'total-diff-line', // 总计差异标注
hierarchyDiffLine = 'hierarchy-diff-line', // 层级差异标记
point = 'mark-point', // 点标注
trendLine = 'trend-line', // 趋势线
partitionLine = 'partition-line', // 分区标注线
partitionArea = 'partition-area' // 分区标注面
}
89 changes: 89 additions & 0 deletions packages/vstory-core/src/character/chart/runtime/marker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import type { Dict } from '@visactor/vutils';
import type { IChartCharacterRuntime } from '../interface/runtime';
import type { ICharacterChart } from '../interface/character-chart';
import type { IVChart } from '@visactor/vchart';
import { getChartType, transformMarkerSymbolAttributeByKey } from './utils';
import { LineSymbolType, MARKER_NOT_SUPPORT_CHARTS, MarkerTypeEnum } from './const';

export class ChartMarkerRuntime implements IChartCharacterRuntime {
type = 'ChartMarker';

applyConfigToAttribute(character: ICharacterChart) {
const config = character.getRuntimeConfig().config;
const marker = config.options?.marker;
const rawAttribute = character.getRuntimeConfig().getAttribute();
const { spec } = rawAttribute;
const chartType = getChartType(config.options);

if (marker) {
Object.keys(marker).forEach(key => {
if (spec[key]) {
spec[key] = spec[key].filter((s: Dict<any>) => !s.name);
} else {
spec[key] = [];
}
if (marker[key as 'markLine' | 'markPoint' | 'markArea']) {
spec[key].push(...marker[key as 'markLine' | 'markPoint' | 'markArea']);
}
});

if (chartType !== 'scatter' && spec.markLine && spec.markLine.length) {
// 非散点图,过滤分区标注
spec.markLine = spec.markLine.filter((spec: any) => spec.name !== MarkerTypeEnum.partitionLine);
}
}

// 如果当前图表不支持标注
if (MARKER_NOT_SUPPORT_CHARTS.includes(chartType)) {
spec.markArea = [];
spec.markLine = [];
}

if (spec.markLine && spec.markLine.length) {
// 处理 marker 的 symbol 样式
spec.markLine.forEach((mark: any) => {
if (
[
MarkerTypeEnum.growthLine,
MarkerTypeEnum.hierarchyDiffLine,
MarkerTypeEnum.horizontalLine,
MarkerTypeEnum.partitionLine,
MarkerTypeEnum.totalDiffLine,
MarkerTypeEnum.verticalLine
].includes(mark.name)
) {
transformMarkerSymbolAttributeByKey(mark, 'startSymbol');
transformMarkerSymbolAttributeByKey(mark, 'endSymbol');
}
});
}

if (spec.markPoint && spec.markPoint.length) {
spec.markPoint.forEach((mark: any) => {
if (mark.name === MarkerTypeEnum.point) {
const itemLine = mark.itemLine;
transformMarkerSymbolAttributeByKey(itemLine, 'startSymbol');
transformMarkerSymbolAttributeByKey(itemLine, 'endSymbol');

if (itemLine.startSymbol.originSymbolType) {
if (itemLine.type !== 'type-arc' && itemLine.startSymbol.originSymbolType === LineSymbolType.arrow) {
itemLine.startSymbol.refX = -itemLine.startSymbol.size / 2;
} else {
itemLine.startSymbol.refX = 0;
}
}

if (itemLine.endSymbol.originSymbolType) {
if (itemLine.type !== 'type-arc' && itemLine.endSymbol.originSymbolType === LineSymbolType.arrow) {
itemLine.endSymbol.refX = -itemLine.endSymbol.size / 2;
} else {
itemLine.endSymbol.refX = 0;
}
}
}
});
}
}
}

export const ChartMarkerRuntimeInstance = new ChartMarkerRuntime();
66 changes: 63 additions & 3 deletions packages/vstory-core/src/character/chart/runtime/utils.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { isArray, isValid } from '@visactor/vutils';
import { array, isArray, isString, isValid, merge } from '@visactor/vutils';
import type { IChart } from '@visactor/vchart/esm/chart/interface';
import type { ICartesianSeries, ISeries } from '@visactor/vchart';
import { isContinuous } from '@visactor/vscale';
import { VCHART_DATA_INDEX, ValueLink, FieldLink } from './const';
import type { IComponentMatch, IMarkStyle } from '../../../interface/dsl/chart';
import { VCHART_DATA_INDEX, ValueLink, FieldLink, lineSymbolPathMap, LineSymbolType } from './const';
import type { IChartCharacterConfig, IComponentMatch, IMarkStyle } from '../../../interface/dsl/chart';

export function GetVChartSeriesWithMatch(vchart: IChart, seriesMatch: IComponentMatch & { type: string }) {
if (!isValid(seriesMatch.specIndex) && seriesMatch.type) {
Expand Down Expand Up @@ -115,3 +115,63 @@ export function getMarkStyleId(markName: string, itemKeys: string[], itemKeyMap:
return pre + `${FieldLink}${cur}${ValueLink}${itemKeyMap[cur]}`;
}, markName);
}

export function transformMarkerSymbolAttributeByKey(attr: any, key: string) {
if (attr[key]?.originSymbolType) {
attr[key].symbolType = lineSymbolPathMap[attr[key].originSymbolType as LineSymbolType];
attr[key].refX = 0;
const symbolStyle = attr[key].style ?? {};
const color =
symbolStyle.color ??
(isString(symbolStyle.fill) ? symbolStyle.fill : isString(symbolStyle.stroke) ? symbolStyle.stroke : '#000');
if (attr[key].originSymbolType === LineSymbolType.arrow) {
attr[key].style = merge({}, symbolStyle, {
fill: false,
stroke: color,
lineWidth: 1,
color
});
attr[key].symbolType = '<svg><path d="M -1 1 L 0 0 L 1 1"/></svg>';
} else if (
attr[key].originSymbolType === LineSymbolType.solidArrow ||
attr[key].originSymbolType === LineSymbolType.solidCircle
) {
attr[key].style = merge({}, symbolStyle, {
fillOpacity: 1,
fill: color,
color,
lineWidth: 0
});
} else if (
attr[key].originSymbolType === LineSymbolType.hollowArrow ||
attr[key].originSymbolType === LineSymbolType.hollowCircle
) {
attr[key].style = merge({}, symbolStyle, {
fillOpacity: 1,
fill: '#fff',
stroke: color,
color,
lineWidth: 1
});
}
}

return attr;
}

export function getChartType(options: IChartCharacterConfig['options']) {
const { chartType, spec = {} } = options;

if (chartType) {
return chartType;
}

if (spec.series && spec.series.length) {
// whether all series types are consistent.
const seriesTypes = spec.series.map((s: ISeries) => s.type);
if (seriesTypes.every((t: string) => t === seriesTypes[0])) {
return seriesTypes[0];
}
}
return spec.type ?? 'common';
}
14 changes: 13 additions & 1 deletion packages/vstory-core/src/interface/dsl/chart.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { ITextGraphicAttribute } from '@visactor/vrender-core';
import type { IInitOption, ISpec } from '@visactor/vchart';
import type { ChartSpecMap, IInitOption, IMarkAreaSpec, IMarkLineSpec, IMarkPointSpec, ISpec } from '@visactor/vchart';
import type { ICharacterConfigBase } from './dsl';
import type { IFormatConfig } from './common';

Expand Down Expand Up @@ -66,6 +66,10 @@ export interface ITotalLabelConfig {

export interface IChartCharacterConfig extends ICharacterConfigBase {
options: {
/**
* 指定图表类型,如果不指定,会根据 spec 自动推断
*/
chartType?: keyof ChartSpecMap;
/**
* 图表spec
*/
Expand Down Expand Up @@ -148,5 +152,13 @@ export interface IChartCharacterConfig extends ICharacterConfigBase {
* 直接合并的配置
*/
rootConfig?: Record<string, any>;
/**
* 图表标注
*/
marker?: {
markLine?: IMarkLineSpec[];
markArea?: IMarkAreaSpec[];
markPoint?: IMarkPointSpec[];
};
};
}
2 changes: 2 additions & 0 deletions packages/vstory-core/src/module/character.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import { ColStyleRuntimeInstance } from '../character/table/runtime/col-style';
import { RowStyleRuntimeInstance } from '../character/table/runtime/row-style';
import { ContentColStyleRuntimeInstance } from '../character/table/runtime/content-col-style';
import { ContentRowStyleRuntimeInstance } from '../character/table/runtime/content-row-style';
import { ChartMarkerRuntimeInstance } from '../character/chart/runtime/marker';

let _register = false;
export function registerCharacters() {
Expand Down Expand Up @@ -83,6 +84,7 @@ export function registerRuntime() {
RuntimeStore.register(MarkStyleRuntimeInstance);
RuntimeStore.register(LabelStyleRuntimeInstance);
RuntimeStore.register(TotalLabelRuntimeInstance);
RuntimeStore.register(ChartMarkerRuntimeInstance);

// 图表相关运行时
RuntimeStore.register(RankingBarRuntimeInstance);
Expand Down
2 changes: 1 addition & 1 deletion packages/vstory-core/src/theme/builtin/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const DefaultTheme: {
},
Chart: {
runtime: {
list: ['ChartCommonSpec', 'CommonLayout', 'MarkStyle', 'LabelStyle', 'TotalLabel'],
list: ['ChartCommonSpec', 'CommonLayout', 'MarkStyle', 'LabelStyle', 'TotalLabel', 'ChartMarker'],
functions: {
formatValue: formatValue
}
Expand Down
5 changes: 5 additions & 0 deletions packages/vstory/demo/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ import { ShowHeader } from './demos/table/runtime/show-header';
import { TableTheme } from './demos/table/runtime/theme';
import { TableStyle } from './demos/table/runtime/style';
import { TableVisible } from './demos/table/runtime/visible';
import { SpecMarker } from './demos/chart/runtime/spec-marker';

type MenusType = (
| {
Expand Down Expand Up @@ -354,6 +355,10 @@ const App = () => {
{
name: 'Total Label',
component: RuntimeTotalLabel
},
{
name: 'Marker',
component: SpecMarker
}
]
},
Expand Down
Loading

0 comments on commit e1918f1

Please sign in to comment.