@fulate/echart 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +70 -0
- package/dist/clipboard.d.ts +5 -0
- package/dist/echart-worker.d.ts +1 -0
- package/dist/echart-worker.js +106 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +266 -0
- package/dist/pool.d.ts +15 -0
- package/dist/protocol.d.ts +58 -0
- package/dist/shape.d.ts +57 -0
- package/dist/worker-surface.d.ts +16 -0
- package/package.json +25 -0
package/README.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# `@fulate/echart`
|
|
2
|
+
|
|
3
|
+
> 文档角色:`GUIDE`。权威来源:ECharts adapter 源码 TSDoc 与测试;Core/UI 场景生命周期见各自指南。
|
|
4
|
+
|
|
5
|
+
`@fulate/echart` 提供 `EChartsShape`、`EChartsProperties`、`EChartsPool` 和 clipboard plugin。图表实例在 Worker surface
|
|
6
|
+
中运行,Fulate 场景只保存 authored/runtime 图表配置,不把 Worker pool 或 surface identity 写入
|
|
7
|
+
JSON、History 或 clipboard。
|
|
8
|
+
|
|
9
|
+
## 配置 Worker pool
|
|
10
|
+
|
|
11
|
+
每个 Root 提供一个 `EChartsPool`:
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { EChartsPool, eChartsPoolKey } from "@fulate/echart";
|
|
15
|
+
|
|
16
|
+
const pool = new EChartsPool({
|
|
17
|
+
echartsUrl: "https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.esm.min.js",
|
|
18
|
+
size: 2
|
|
19
|
+
});
|
|
20
|
+
root.provide(eChartsPoolKey, pool);
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`size` 省略时使用默认 Worker 数量。应用负责在 Root 生命周期结束时调用 `pool.dispose()`。
|
|
24
|
+
|
|
25
|
+
## EChartsShape
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
const chart = new EChartsShape({
|
|
29
|
+
width: 640,
|
|
30
|
+
height: 360,
|
|
31
|
+
echarts: { option: { series: [] } }
|
|
32
|
+
});
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`echarts` 是 whole-value authored/runtime 字段。`setOptions({ echarts })` 替换持久配置;
|
|
36
|
+
`setRuntimeOptions({ echarts })` 只覆盖当前运行态,`clearRuntimeOptions(["echarts"])` 恢复最新
|
|
37
|
+
持久值。有效`theme`引用变化会重建surface;`option`、`notMerge`或`lazyUpdate`变化且theme未变时
|
|
38
|
+
复用当前surface并发送一次`set-option`。width、height、DPR或Viewport scale变化只发送`resize`;
|
|
39
|
+
ECharts逻辑宽高始终等于authored宽高,字号、间距、线宽和原始option保持不变。Shape按
|
|
40
|
+
`Viewport scale × DPR × 2`计算`renderPixelRatio`,再按单图`8 × 1024 × 1024`像素预算裁剪上限;
|
|
41
|
+
该倍率没有固定下限。Worker把最终倍率映射到ECharts的`devicePixelRatio`渲染参数,输出
|
|
42
|
+
`authored尺寸 × renderPixelRatio`的位图。Layer把位图绘制到authored宽高,经过Viewport后按固定
|
|
43
|
+
质量倍率下采样。Viewport CSS preview和animated focus期间复用已有frame,settle后的真实重绘只按
|
|
44
|
+
最终倍率发送一次`resize`。DPR变化复用当前ECharts实例。相同有效字段为noop;Shape尚未激活时
|
|
45
|
+
不创建surface,激活时直接采用当前有效配置。
|
|
46
|
+
`setOption()`仍可直接调用当前Worker内原生ECharts的增量更新,不改变authored配置。
|
|
47
|
+
|
|
48
|
+
`EChartsProperties`是公开的properties基类;`EChartsShape<Properties, Option>`与Rectangle/Image一样
|
|
49
|
+
提供默认类型参数,并允许子类通过构造器第二参数交付扩展properties。surface始终由EChartsShape内部
|
|
50
|
+
唯一持有,不属于公开properties或子类配置。
|
|
51
|
+
|
|
52
|
+
## Action 与事件
|
|
53
|
+
|
|
54
|
+
`dispatchAction(payload, options?)` 将可 structured-clone 的 action 发给 Worker。通过
|
|
55
|
+
`addEChartsEventListener(name, listener)` 订阅原生或扩展事件;返回 cleanup,订阅会跨 surface
|
|
56
|
+
重建保持。custom action 由 ECharts 扩展自己注册,Fulate 不重复注册。
|
|
57
|
+
|
|
58
|
+
Scene鼠标坐标转成图元authored-local后原样进入Worker;Viewport scale、DPR和超采样质量都不参与
|
|
59
|
+
坐标换算。custom action返回的坐标同样是authored-local,可直接交回Scene几何。
|
|
60
|
+
|
|
61
|
+
## Clipboard
|
|
62
|
+
|
|
63
|
+
`eChartsClipboardPlugin` 按 `Select` 实例显式安装:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
const select = new Select().useClipboard(eChartsClipboardPlugin);
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
复制数据只包含图表配置和几何选项;恢复前应先在目标 Root 提供 pool。该 adapter 不引入旧格式
|
|
70
|
+
迁移或第二套运行路径。
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { BitmapFrameEmitter, runWorkerSurfaceHost } from "@fulate/ui/worker-surface-worker";
|
|
2
|
+
|
|
3
|
+
//#region packages/echart/src/worker-surface.ts
|
|
4
|
+
/** ECharts-specific renderer state owned by one Worker surface. */
|
|
5
|
+
var EChartsWorkerSurface = class {
|
|
6
|
+
canvas;
|
|
7
|
+
chart;
|
|
8
|
+
frames;
|
|
9
|
+
chartRenderPixelRatio;
|
|
10
|
+
frameRenderPixelRatio;
|
|
11
|
+
eventListeners = /* @__PURE__ */ new Map();
|
|
12
|
+
constructor(echarts, input, emitFrame, emitOutput) {
|
|
13
|
+
this.emitOutput = emitOutput;
|
|
14
|
+
const { authoredWidth, authoredHeight, renderPixelRatio, theme, option, notMerge, lazyUpdate } = input;
|
|
15
|
+
this.canvas = new OffscreenCanvas(authoredWidth * renderPixelRatio, authoredHeight * renderPixelRatio);
|
|
16
|
+
this.chartRenderPixelRatio = this.frameRenderPixelRatio = renderPixelRatio;
|
|
17
|
+
this.chart = echarts.init(this.canvas, theme ?? null, {
|
|
18
|
+
width: authoredWidth,
|
|
19
|
+
height: authoredHeight,
|
|
20
|
+
devicePixelRatio: renderPixelRatio
|
|
21
|
+
});
|
|
22
|
+
this.frames = new BitmapFrameEmitter(() => this.getFrameCanvas(), emitFrame);
|
|
23
|
+
this.chart.on("rendered", () => this.frames.request());
|
|
24
|
+
this.chart.setOption(option, notMerge, lazyUpdate);
|
|
25
|
+
}
|
|
26
|
+
receive(command) {
|
|
27
|
+
switch (command.type) {
|
|
28
|
+
case "set-option":
|
|
29
|
+
this.chart.setOption(command.option, command.notMerge, command.lazyUpdate);
|
|
30
|
+
break;
|
|
31
|
+
case "dispatch-action":
|
|
32
|
+
this.chart.dispatchAction(command.payload, command.opt);
|
|
33
|
+
break;
|
|
34
|
+
case "event":
|
|
35
|
+
this.chart.getZr().handler.dispatch(command.name, {
|
|
36
|
+
zrX: command.x,
|
|
37
|
+
zrY: command.y,
|
|
38
|
+
clientX: command.x,
|
|
39
|
+
clientY: command.y,
|
|
40
|
+
preventDefault: () => {},
|
|
41
|
+
stopPropagation: () => {}
|
|
42
|
+
});
|
|
43
|
+
break;
|
|
44
|
+
case "add-event-listener": {
|
|
45
|
+
const listener = (params) => {
|
|
46
|
+
this.emitOutput({
|
|
47
|
+
type: "event",
|
|
48
|
+
name: command.name,
|
|
49
|
+
params
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
this.eventListeners.set(command.name, listener);
|
|
53
|
+
this.chart.on(command.name, listener);
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
case "remove-event-listener":
|
|
57
|
+
this.chart.off(command.name, this.eventListeners.get(command.name));
|
|
58
|
+
this.eventListeners.delete(command.name);
|
|
59
|
+
break;
|
|
60
|
+
case "resize":
|
|
61
|
+
this.frameRenderPixelRatio = command.renderPixelRatio;
|
|
62
|
+
this.chart.resize({
|
|
63
|
+
width: command.authoredWidth,
|
|
64
|
+
height: command.authoredHeight
|
|
65
|
+
});
|
|
66
|
+
break;
|
|
67
|
+
case "visibility":
|
|
68
|
+
if (command.visible) {
|
|
69
|
+
this.chart.getZr().animation.resume();
|
|
70
|
+
this.frames.resume();
|
|
71
|
+
} else {
|
|
72
|
+
this.frames.pause();
|
|
73
|
+
this.chart.getZr().animation.pause();
|
|
74
|
+
}
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
dispose() {
|
|
79
|
+
this.frames.dispose();
|
|
80
|
+
this.eventListeners.clear();
|
|
81
|
+
this.chart.dispose();
|
|
82
|
+
}
|
|
83
|
+
getFrameCanvas() {
|
|
84
|
+
if (this.frameRenderPixelRatio === this.chartRenderPixelRatio) return this.canvas;
|
|
85
|
+
return this.chart.renderToCanvas({ pixelRatio: this.frameRenderPixelRatio });
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
//#endregion
|
|
90
|
+
//#region packages/echart/src/echart-worker.ts
|
|
91
|
+
runWorkerSurfaceHost({
|
|
92
|
+
async initialize(input) {
|
|
93
|
+
const module = await import(
|
|
94
|
+
/* @vite-ignore */
|
|
95
|
+
input.echartsUrl
|
|
96
|
+
);
|
|
97
|
+
const echarts = module.default ?? module;
|
|
98
|
+
echarts.setPlatformAPI({ createCanvas: () => new OffscreenCanvas(1, 1) });
|
|
99
|
+
return echarts;
|
|
100
|
+
},
|
|
101
|
+
create(echarts, input, emitFrame, emitOutput) {
|
|
102
|
+
return new EChartsWorkerSurface(echarts, input, emitFrame, emitOutput);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
//#endregion
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { EChartsPool, eChartsPoolKey } from "./pool";
|
|
2
|
+
export type { EChartsPoolOptions } from "./pool";
|
|
3
|
+
export { EChartsProperties, EChartsShape } from "./shape";
|
|
4
|
+
export type { EChartsEventListener, EChartsShapeOption, EChartsShapeOptionPatch } from "./shape";
|
|
5
|
+
export type { EChartsActionPayload, EChartsDispatchActionOptions, EChartsInitOpts } from "./protocol";
|
|
6
|
+
export { eChartsClipboardPlugin } from "./clipboard";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { Bitmap, RectangleProperties, RectangleTransform, WorkerSurfacePool } from "@fulate/ui";
|
|
2
|
+
import { createPointView } from "@fulate/share";
|
|
3
|
+
|
|
4
|
+
//#region packages/echart/src/pool.ts
|
|
5
|
+
const eChartsPoolKey = Symbol();
|
|
6
|
+
/** Configures one independent ECharts backend over the shared surface pool. */
|
|
7
|
+
var EChartsPool = class {
|
|
8
|
+
surfaces;
|
|
9
|
+
constructor(options) {
|
|
10
|
+
const { echartsUrl, size = 2 } = options;
|
|
11
|
+
this.surfaces = new WorkerSurfacePool({
|
|
12
|
+
size,
|
|
13
|
+
initialize: { echartsUrl: String(echartsUrl) },
|
|
14
|
+
createWorker: () => new Worker(new URL("./echart-worker.js", import.meta.url), { type: "module" })
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
/** @internal Creates the renderer for one active EChartsShape. */
|
|
18
|
+
create(input, onFrame, onOutput) {
|
|
19
|
+
return this.surfaces.create(input, onFrame, onOutput);
|
|
20
|
+
}
|
|
21
|
+
dispose() {
|
|
22
|
+
this.surfaces.dispose();
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
//#endregion
|
|
27
|
+
//#region packages/echart/src/shape.ts
|
|
28
|
+
const RENDER_QUALITY = 2;
|
|
29
|
+
const BITMAP_PIXEL_BUDGET = 8 * 1024 * 1024;
|
|
30
|
+
/** Public properties base for typed EChartsShape subclasses. */
|
|
31
|
+
var EChartsProperties = class extends RectangleProperties {
|
|
32
|
+
echarts;
|
|
33
|
+
constructor(options = {}) {
|
|
34
|
+
super(options);
|
|
35
|
+
this.echarts = options.echarts ?? { option: {} };
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
var EChartsTransform = class extends RectangleTransform {
|
|
39
|
+
hasInView() {
|
|
40
|
+
const inView = super.hasInView();
|
|
41
|
+
this.element.syncChartVisibility(inView);
|
|
42
|
+
return inView;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
var EChartsShape = class extends Bitmap {
|
|
46
|
+
type = "echarts";
|
|
47
|
+
surface = null;
|
|
48
|
+
lastVisibility;
|
|
49
|
+
sentAuthoredWidth = 0;
|
|
50
|
+
sentAuthoredHeight = 0;
|
|
51
|
+
sentRenderPixelRatio = 0;
|
|
52
|
+
cleanups = [];
|
|
53
|
+
eChartsEventListeners = /* @__PURE__ */ new Map();
|
|
54
|
+
/** Current effective chart configuration, including runtime overrides. */
|
|
55
|
+
get echarts() {
|
|
56
|
+
return this.propertyState.echarts;
|
|
57
|
+
}
|
|
58
|
+
constructor(options, properties = new EChartsProperties(options), Transform = EChartsTransform) {
|
|
59
|
+
super(options, properties, Transform);
|
|
60
|
+
}
|
|
61
|
+
/** Sends one native incremental ECharts update outside authored state. */
|
|
62
|
+
setOption(option, notMerge, lazyUpdate) {
|
|
63
|
+
this.surface?.post({
|
|
64
|
+
type: "set-option",
|
|
65
|
+
option,
|
|
66
|
+
notMerge,
|
|
67
|
+
lazyUpdate
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Dispatches one action to the current native ECharts instance in its Worker.
|
|
72
|
+
* Registered action events return through `addEChartsEventListener()`.
|
|
73
|
+
*/
|
|
74
|
+
dispatchAction(payload, opt) {
|
|
75
|
+
this.surface?.post({
|
|
76
|
+
type: "dispatch-action",
|
|
77
|
+
payload,
|
|
78
|
+
opt
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Subscribes to one native ECharts instance event across the Worker boundary.
|
|
83
|
+
* Custom action events must be registered by the loaded ECharts extension.
|
|
84
|
+
* The returned cleanup survives chart restarts and removes this subscription.
|
|
85
|
+
*/
|
|
86
|
+
addEChartsEventListener(name, listener) {
|
|
87
|
+
let listeners = this.eChartsEventListeners.get(name);
|
|
88
|
+
if (!listeners) {
|
|
89
|
+
listeners = /* @__PURE__ */ new Set();
|
|
90
|
+
this.eChartsEventListeners.set(name, listeners);
|
|
91
|
+
this.surface?.post({
|
|
92
|
+
type: "add-event-listener",
|
|
93
|
+
name
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
listeners.add(listener);
|
|
97
|
+
return () => {
|
|
98
|
+
const current = this.eChartsEventListeners.get(name);
|
|
99
|
+
if (!current) return;
|
|
100
|
+
current.delete(listener);
|
|
101
|
+
if (current.size > 0) return;
|
|
102
|
+
this.eChartsEventListeners.delete(name);
|
|
103
|
+
this.surface?.post({
|
|
104
|
+
type: "remove-event-listener",
|
|
105
|
+
name
|
|
106
|
+
});
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
resizeChart() {
|
|
110
|
+
const surface = this.surface;
|
|
111
|
+
if (!surface) return;
|
|
112
|
+
const authoredWidth = this.width;
|
|
113
|
+
const authoredHeight = this.height;
|
|
114
|
+
const renderPixelRatio = this.chartRenderPixelRatio;
|
|
115
|
+
if (authoredWidth === this.sentAuthoredWidth && authoredHeight === this.sentAuthoredHeight && renderPixelRatio === this.sentRenderPixelRatio) return;
|
|
116
|
+
this.sentAuthoredWidth = authoredWidth;
|
|
117
|
+
this.sentAuthoredHeight = authoredHeight;
|
|
118
|
+
this.sentRenderPixelRatio = renderPixelRatio;
|
|
119
|
+
surface.post({
|
|
120
|
+
type: "resize",
|
|
121
|
+
authoredWidth,
|
|
122
|
+
authoredHeight,
|
|
123
|
+
renderPixelRatio
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
onPropertiesChanged(current, changes) {
|
|
127
|
+
super.onPropertiesChanged(current, changes);
|
|
128
|
+
if (!this.isActive) return;
|
|
129
|
+
if (changes.has("echarts")) {
|
|
130
|
+
const previous = changes.get("echarts");
|
|
131
|
+
const next = current.echarts;
|
|
132
|
+
if (!Object.is(previous.theme, next.theme)) {
|
|
133
|
+
this.restartChart();
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (!Object.is(previous.option, next.option) || !Object.is(previous.notMerge, next.notMerge) || !Object.is(previous.lazyUpdate, next.lazyUpdate)) this.setOption(next.option, next.notMerge, next.lazyUpdate);
|
|
137
|
+
}
|
|
138
|
+
if (changes.has("width") || changes.has("height")) this.resizeChart();
|
|
139
|
+
}
|
|
140
|
+
onActivate(root, layer) {
|
|
141
|
+
super.onActivate(root, layer);
|
|
142
|
+
this.startChart(root.inject(eChartsPoolKey));
|
|
143
|
+
}
|
|
144
|
+
onDeactivate(root, layer) {
|
|
145
|
+
this.stopChart();
|
|
146
|
+
super.onDeactivate(root, layer);
|
|
147
|
+
}
|
|
148
|
+
onUnmount() {
|
|
149
|
+
this.stopChart();
|
|
150
|
+
this.eChartsEventListeners.clear();
|
|
151
|
+
super.onUnmount();
|
|
152
|
+
}
|
|
153
|
+
paintContent(ctx) {
|
|
154
|
+
this.resizeChart();
|
|
155
|
+
this.syncChartVisibility(true);
|
|
156
|
+
this.paintBitmap(ctx, this.width, this.height);
|
|
157
|
+
}
|
|
158
|
+
/** @internal Synchronizes ECharts animation with the actual paint path. */
|
|
159
|
+
syncChartVisibility(visible) {
|
|
160
|
+
const surface = this.surface;
|
|
161
|
+
if (!surface || visible === this.lastVisibility) return;
|
|
162
|
+
this.lastVisibility = visible;
|
|
163
|
+
surface.post({
|
|
164
|
+
type: "visibility",
|
|
165
|
+
visible
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
startChart(pool) {
|
|
169
|
+
const input = {
|
|
170
|
+
authoredWidth: this.width,
|
|
171
|
+
authoredHeight: this.height,
|
|
172
|
+
renderPixelRatio: this.chartRenderPixelRatio,
|
|
173
|
+
...this.echarts
|
|
174
|
+
};
|
|
175
|
+
this.sentAuthoredWidth = input.authoredWidth;
|
|
176
|
+
this.sentAuthoredHeight = input.authoredHeight;
|
|
177
|
+
this.sentRenderPixelRatio = input.renderPixelRatio;
|
|
178
|
+
this.lastVisibility = void 0;
|
|
179
|
+
this.surface = pool.create(input, (bitmap) => this.presentBitmap(bitmap), (output) => this.receiveEChartsOutput(output));
|
|
180
|
+
for (const name of this.eChartsEventListeners.keys()) this.surface.post({
|
|
181
|
+
type: "add-event-listener",
|
|
182
|
+
name
|
|
183
|
+
});
|
|
184
|
+
this.bindEvents();
|
|
185
|
+
}
|
|
186
|
+
restartChart() {
|
|
187
|
+
const pool = this.root.inject(eChartsPoolKey);
|
|
188
|
+
this.stopChart();
|
|
189
|
+
this.startChart(pool);
|
|
190
|
+
}
|
|
191
|
+
stopChart() {
|
|
192
|
+
this.surface?.dispose();
|
|
193
|
+
this.surface = null;
|
|
194
|
+
this.lastVisibility = void 0;
|
|
195
|
+
this.cleanups.forEach((cleanup) => cleanup());
|
|
196
|
+
this.cleanups = [];
|
|
197
|
+
}
|
|
198
|
+
toLocal(event) {
|
|
199
|
+
return this.transform.toLocal(createPointView(event.detail.x, event.detail.y)) ?? createPointView(-1e3, -1e3);
|
|
200
|
+
}
|
|
201
|
+
forwardEvent(name) {
|
|
202
|
+
return (event) => {
|
|
203
|
+
const local = this.toLocal(event);
|
|
204
|
+
this.surface?.post({
|
|
205
|
+
type: "event",
|
|
206
|
+
name,
|
|
207
|
+
x: local.x,
|
|
208
|
+
y: local.y
|
|
209
|
+
});
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
get chartRenderPixelRatio() {
|
|
213
|
+
const budgetRatio = Math.sqrt(BITMAP_PIXEL_BUDGET / (this.width * this.height));
|
|
214
|
+
return Math.min(this.renderPixelRatio * RENDER_QUALITY, budgetRatio);
|
|
215
|
+
}
|
|
216
|
+
receiveEChartsOutput(output) {
|
|
217
|
+
const listeners = this.eChartsEventListeners.get(output.name);
|
|
218
|
+
if (!listeners) return;
|
|
219
|
+
for (const listener of listeners) {
|
|
220
|
+
if (this.eChartsEventListeners.get(output.name) !== listeners) return;
|
|
221
|
+
listener(output.params);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
bindEvents() {
|
|
225
|
+
const bind = (name, handler) => {
|
|
226
|
+
this.cleanups.push(this.addEventListener(name, handler));
|
|
227
|
+
};
|
|
228
|
+
bind("click", this.forwardEvent("click"));
|
|
229
|
+
bind("dblclick", this.forwardEvent("dblclick"));
|
|
230
|
+
bind("mouseenter", this.forwardEvent("mouseover"));
|
|
231
|
+
bind("pointermove", this.forwardEvent("mousemove"));
|
|
232
|
+
bind("pointerdown", this.forwardEvent("mousedown"));
|
|
233
|
+
bind("pointerup", this.forwardEvent("mouseup"));
|
|
234
|
+
bind("contextmenu", this.forwardEvent("contextmenu"));
|
|
235
|
+
bind("mouseleave", () => {
|
|
236
|
+
this.surface?.post({
|
|
237
|
+
type: "event",
|
|
238
|
+
name: "mouseout",
|
|
239
|
+
x: -1e3,
|
|
240
|
+
y: -1e3
|
|
241
|
+
});
|
|
242
|
+
this.surface?.post({
|
|
243
|
+
type: "event",
|
|
244
|
+
name: "globalout",
|
|
245
|
+
x: -1e3,
|
|
246
|
+
y: -1e3
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
//#endregion
|
|
253
|
+
//#region packages/echart/src/clipboard.ts
|
|
254
|
+
const eChartsClipboardPlugin = {
|
|
255
|
+
type: "echarts",
|
|
256
|
+
deserialize(data) {
|
|
257
|
+
const { type: _type, children: _children, echarts: echartsOptions, ...shapeOptions } = data;
|
|
258
|
+
return new EChartsShape({
|
|
259
|
+
...shapeOptions,
|
|
260
|
+
echarts: echartsOptions
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
//#endregion
|
|
266
|
+
export { EChartsPool, EChartsProperties, EChartsShape, eChartsClipboardPlugin, eChartsPoolKey };
|
package/dist/pool.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type WorkerSurfaceHandle } from "@fulate/ui";
|
|
2
|
+
import type { EChartsCommand, EChartsOutput, EChartsSurfaceCreate } from "./protocol";
|
|
3
|
+
export interface EChartsPoolOptions {
|
|
4
|
+
echartsUrl: string | URL;
|
|
5
|
+
size?: number;
|
|
6
|
+
}
|
|
7
|
+
export declare const eChartsPoolKey: unique symbol;
|
|
8
|
+
/** Configures one independent ECharts backend over the shared surface pool. */
|
|
9
|
+
export declare class EChartsPool {
|
|
10
|
+
private readonly surfaces;
|
|
11
|
+
constructor(options: EChartsPoolOptions);
|
|
12
|
+
/** @internal Creates the renderer for one active EChartsShape. */
|
|
13
|
+
create(input: EChartsSurfaceCreate, onFrame: (bitmap: ImageBitmap) => void, onOutput?: (output: EChartsOutput) => void): WorkerSurfaceHandle<EChartsCommand>;
|
|
14
|
+
dispose(): void;
|
|
15
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export interface EChartsInitOpts {
|
|
2
|
+
theme?: string | object;
|
|
3
|
+
option: any;
|
|
4
|
+
notMerge?: boolean;
|
|
5
|
+
lazyUpdate?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface EChartsWorkerInitialize {
|
|
8
|
+
echartsUrl: string;
|
|
9
|
+
}
|
|
10
|
+
export interface EChartsSurfaceCreate extends EChartsInitOpts {
|
|
11
|
+
/** ECharts logical layout width in authored-local units. */
|
|
12
|
+
authoredWidth: number;
|
|
13
|
+
/** ECharts logical layout height in authored-local units. */
|
|
14
|
+
authoredHeight: number;
|
|
15
|
+
/** Physical bitmap pixels per authored-local unit. */
|
|
16
|
+
renderPixelRatio: number;
|
|
17
|
+
}
|
|
18
|
+
export interface EChartsActionPayload {
|
|
19
|
+
type: string;
|
|
20
|
+
}
|
|
21
|
+
export type EChartsDispatchActionOptions = boolean | Readonly<{
|
|
22
|
+
silent?: boolean;
|
|
23
|
+
flush?: boolean;
|
|
24
|
+
}>;
|
|
25
|
+
export type EChartsCommand = {
|
|
26
|
+
type: "set-option";
|
|
27
|
+
option: any;
|
|
28
|
+
notMerge?: boolean;
|
|
29
|
+
lazyUpdate?: boolean;
|
|
30
|
+
} | {
|
|
31
|
+
type: "dispatch-action";
|
|
32
|
+
payload: EChartsActionPayload;
|
|
33
|
+
opt?: EChartsDispatchActionOptions;
|
|
34
|
+
} | {
|
|
35
|
+
type: "resize";
|
|
36
|
+
authoredWidth: number;
|
|
37
|
+
authoredHeight: number;
|
|
38
|
+
renderPixelRatio: number;
|
|
39
|
+
} | {
|
|
40
|
+
type: "event";
|
|
41
|
+
name: string;
|
|
42
|
+
x: number;
|
|
43
|
+
y: number;
|
|
44
|
+
} | {
|
|
45
|
+
type: "add-event-listener";
|
|
46
|
+
name: string;
|
|
47
|
+
} | {
|
|
48
|
+
type: "remove-event-listener";
|
|
49
|
+
name: string;
|
|
50
|
+
} | {
|
|
51
|
+
type: "visibility";
|
|
52
|
+
visible: boolean;
|
|
53
|
+
};
|
|
54
|
+
export interface EChartsOutput {
|
|
55
|
+
type: "event";
|
|
56
|
+
name: string;
|
|
57
|
+
params: any;
|
|
58
|
+
}
|
package/dist/shape.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { ElementTransformClass, Layer, PropertyChanges, Root } from "@fulate/core";
|
|
2
|
+
import { Bitmap, RectangleProperties, type RectangleOption, type RectangleOptionPatch } from "@fulate/ui";
|
|
3
|
+
import type { EChartsActionPayload, EChartsDispatchActionOptions, EChartsInitOpts } from "./protocol";
|
|
4
|
+
export type EChartsEventListener<Params = any> = (params: Params) => void;
|
|
5
|
+
/** Whole-value chart configuration owned by authored/runtime property state. */
|
|
6
|
+
export interface EChartsShapeOption<T = EChartsShape> extends RectangleOption<T> {
|
|
7
|
+
echarts: EChartsInitOpts;
|
|
8
|
+
}
|
|
9
|
+
/** Authored/runtime patch; `echarts` is adopted as one whole value. */
|
|
10
|
+
export type EChartsShapeOptionPatch<O extends EChartsShapeOption<any> = EChartsShapeOption> = RectangleOptionPatch<O>;
|
|
11
|
+
/** Public properties base for typed EChartsShape subclasses. */
|
|
12
|
+
export declare class EChartsProperties extends RectangleProperties {
|
|
13
|
+
echarts: EChartsInitOpts;
|
|
14
|
+
constructor(options?: Partial<EChartsShapeOption>);
|
|
15
|
+
}
|
|
16
|
+
export declare class EChartsShape<P extends EChartsProperties = EChartsProperties, O extends EChartsShapeOption<any> = EChartsShapeOption<any>> extends Bitmap<P, O> {
|
|
17
|
+
readonly type: string;
|
|
18
|
+
private surface;
|
|
19
|
+
private lastVisibility;
|
|
20
|
+
private sentAuthoredWidth;
|
|
21
|
+
private sentAuthoredHeight;
|
|
22
|
+
private sentRenderPixelRatio;
|
|
23
|
+
private cleanups;
|
|
24
|
+
private readonly eChartsEventListeners;
|
|
25
|
+
/** Current effective chart configuration, including runtime overrides. */
|
|
26
|
+
get echarts(): EChartsInitOpts;
|
|
27
|
+
constructor(options: NoInfer<O>, properties?: P, Transform?: ElementTransformClass);
|
|
28
|
+
/** Sends one native incremental ECharts update outside authored state. */
|
|
29
|
+
setOption(option: any, notMerge?: boolean, lazyUpdate?: boolean): void;
|
|
30
|
+
/**
|
|
31
|
+
* Dispatches one action to the current native ECharts instance in its Worker.
|
|
32
|
+
* Registered action events return through `addEChartsEventListener()`.
|
|
33
|
+
*/
|
|
34
|
+
dispatchAction<Payload extends EChartsActionPayload>(payload: Payload, opt?: EChartsDispatchActionOptions): void;
|
|
35
|
+
/**
|
|
36
|
+
* Subscribes to one native ECharts instance event across the Worker boundary.
|
|
37
|
+
* Custom action events must be registered by the loaded ECharts extension.
|
|
38
|
+
* The returned cleanup survives chart restarts and removes this subscription.
|
|
39
|
+
*/
|
|
40
|
+
addEChartsEventListener<Params = any>(name: string, listener: EChartsEventListener<Params>): () => void;
|
|
41
|
+
resizeChart(): void;
|
|
42
|
+
protected onPropertiesChanged(current: P, changes: PropertyChanges): void;
|
|
43
|
+
protected onActivate(root: Root | null, layer: Layer | null): void;
|
|
44
|
+
protected onDeactivate(root: Root | null, layer: Layer | null): void;
|
|
45
|
+
protected onUnmount(): void;
|
|
46
|
+
protected paintContent(ctx: CanvasRenderingContext2D): void;
|
|
47
|
+
/** @internal Synchronizes ECharts animation with the actual paint path. */
|
|
48
|
+
syncChartVisibility(visible: boolean): void;
|
|
49
|
+
private startChart;
|
|
50
|
+
private restartChart;
|
|
51
|
+
private stopChart;
|
|
52
|
+
private toLocal;
|
|
53
|
+
private forwardEvent;
|
|
54
|
+
private get chartRenderPixelRatio();
|
|
55
|
+
private receiveEChartsOutput;
|
|
56
|
+
private bindEvents;
|
|
57
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type WorkerSurface } from "@fulate/ui/worker-surface-worker";
|
|
2
|
+
import type { EChartsCommand, EChartsOutput, EChartsSurfaceCreate } from "./protocol";
|
|
3
|
+
/** ECharts-specific renderer state owned by one Worker surface. */
|
|
4
|
+
export declare class EChartsWorkerSurface implements WorkerSurface<EChartsCommand> {
|
|
5
|
+
private readonly emitOutput;
|
|
6
|
+
private readonly canvas;
|
|
7
|
+
private readonly chart;
|
|
8
|
+
private readonly frames;
|
|
9
|
+
private readonly chartRenderPixelRatio;
|
|
10
|
+
private frameRenderPixelRatio;
|
|
11
|
+
private readonly eventListeners;
|
|
12
|
+
constructor(echarts: any, input: EChartsSurfaceCreate, emitFrame: (bitmap: ImageBitmap) => void, emitOutput: (output: EChartsOutput) => void);
|
|
13
|
+
receive(command: EChartsCommand): void;
|
|
14
|
+
dispose(): void;
|
|
15
|
+
private getFrameCanvas;
|
|
16
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fulate/echart",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist"
|
|
15
|
+
],
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"scripts": {
|
|
18
|
+
"test": "vitest run --config vitest.config.ts"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@fulate/share": "*",
|
|
22
|
+
"@fulate/core": "*",
|
|
23
|
+
"@fulate/ui": "*"
|
|
24
|
+
}
|
|
25
|
+
}
|