vue-echarts 8.2.0 → 8.3.1

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/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
- import { a as hasZeroDimension, c as isOn, d as parseOnEvent, f as warn$1, i as hasEventHandler, l as isPlainObject, n as useRuntime, o as isBrowser, r as createEventInvoker, s as isIgnorableWatchChange, u as isValidArrayIndex } from "./runtime-BSiQKM1W.js";
1
+ import { a as hasEventHandler, c as isIgnorableWatchChange, d as isValidArrayIndex, f as parseOnEvent, i as createEventInvoker, l as isOn, o as hasZeroDimension, p as warn$1, r as useRuntime, s as isBrowser, t as planUpdate, u as isPlainObject } from "./update-DiQcDAnW.js";
2
2
  import { Teleport, computed, defineComponent, getCurrentInstance, h, inject, nextTick, onBeforeUnmount, onMounted, onScopeDispose, onUpdated, shallowReactive, shallowRef, toRef, toValue, watch, watchSyncEffect } from "vue";
3
- import { ComponentModel, init, throttle } from "echarts/core";
3
+ import { init, throttle } from "echarts/core";
4
4
  //#region src/composables/api.ts
5
5
  const METHOD_NAMES = [
6
6
  "getWidth",
@@ -81,27 +81,22 @@ const autoresizeProps = { autoresize: [Boolean, Object] };
81
81
  const LOADING_OPTIONS_KEY = Symbol();
82
82
  function useLoading(chart, loading, loadingType, loadingOptions) {
83
83
  const defaultLoadingOptions = inject(LOADING_OPTIONS_KEY, void 0);
84
- return watch(() => loading.value ? {
85
- instance: chart.value,
86
- visible: true,
84
+ return watch(() => chart.value && loading.value ? {
87
85
  type: loadingType.value || void 0,
88
86
  options: {
89
87
  ...toValue(defaultLoadingOptions),
90
88
  ...loadingOptions.value
91
89
  }
92
- } : {
93
- instance: chart.value,
94
- visible: false
95
- }, (state) => {
96
- const { instance } = state;
90
+ } : null, (state) => {
91
+ const instance = chart.value;
97
92
  if (!instance) return;
98
- if (!state.visible) {
93
+ if (!state) {
99
94
  instance.hideLoading();
100
95
  return;
101
96
  }
102
- const { type, options: currentOptions } = state;
103
- if (type) instance.showLoading(type, currentOptions);
104
- else instance.showLoading(currentOptions);
97
+ const { type, options } = state;
98
+ if (type) instance.showLoading(type, options);
99
+ else instance.showLoading(options);
105
100
  }, {
106
101
  deep: true,
107
102
  immediate: true,
@@ -139,15 +134,16 @@ function getSlotPath(key) {
139
134
  } else path.push(...target);
140
135
  return path;
141
136
  }
142
- function ensureChild(parent, seg, nextSeg) {
137
+ function ensureChild(parent, seg, nextSeg, writable) {
143
138
  const parentIsArray = Array.isArray(parent);
144
139
  if (parentIsArray !== isValidArrayIndex(seg)) return;
145
140
  const next = parentIsArray ? parent[Number(seg)] : parent[seg];
146
141
  let child;
147
- if (Array.isArray(next)) child = [...next];
148
- else if (isPlainObject(next)) child = { ...next };
142
+ if (Array.isArray(next)) child = writable.has(next) ? next : [...next];
143
+ else if (isPlainObject(next)) child = writable.has(next) ? next : { ...next };
149
144
  else if (next === void 0 && !parentIsArray && !isValidArrayIndex(nextSeg)) child = {};
150
145
  else return;
146
+ writable.add(child);
151
147
  writeSegment(parent, seg, child);
152
148
  return child;
153
149
  }
@@ -158,10 +154,10 @@ function writeSegment(parent, seg, value) {
158
154
  }
159
155
  parent[seg] = value;
160
156
  }
161
- function writePath(root, path, value, preserveDefined = false) {
157
+ function writePath(root, path, value, writable, preserveDefined = false) {
162
158
  let current = root;
163
159
  for (let i = 0; i < path.length - 1; i++) {
164
- const child = ensureChild(current, path[i], path[i + 1]);
160
+ const child = ensureChild(current, path[i], path[i + 1], writable);
165
161
  if (!child) return false;
166
162
  current = child;
167
163
  }
@@ -178,40 +174,42 @@ function useSlotOption(slots, onSlotsChange) {
178
174
  const isMounted = shallowRef(false);
179
175
  const ready = shallowRef(false);
180
176
  const collectSlotNames = (warnInvalid = false) => {
181
- const names = [];
177
+ const names = /* @__PURE__ */ new Set();
182
178
  for (const key in slots) {
183
179
  if (key === "graphic") continue;
184
- if (isValidSlotName(key)) names.push(key);
180
+ if (isValidSlotName(key)) names.add(key);
185
181
  else if (warnInvalid) warn$1(`Invalid slot name: ${key}`);
186
182
  }
187
183
  return names;
188
184
  };
189
185
  collectSlotNames(true);
190
- let slotNames = [];
186
+ let slotNames = /* @__PURE__ */ new Set();
191
187
  let nextSlotNames = slotNames;
192
- let patchedSlotNames = slotNames;
193
- const hasNewSlots = () => collectSlotNames().some((name) => !slotNames.includes(name));
188
+ const patchedSlotNames = /* @__PURE__ */ new Set();
189
+ const callbacks = /* @__PURE__ */ new Map();
194
190
  function setReady(value) {
195
191
  ready.value = value;
196
192
  if (value) return;
197
- patchedSlotNames = [];
193
+ patchedSlotNames.clear();
198
194
  for (const key of Object.keys(params)) delete params[key];
199
195
  }
200
196
  function syncSlotNames(names) {
201
- if (names.length === slotNames.length && slotNames.every((name) => names.includes(name))) return false;
202
- for (const key of slotNames) if (!names.includes(key)) {
197
+ let changed = names.size !== slotNames.size;
198
+ for (const key of slotNames) if (!names.has(key)) {
199
+ changed = true;
203
200
  delete params[key];
204
201
  delete containers[key];
202
+ callbacks.delete(key);
205
203
  }
206
- slotNames = names;
207
- return true;
204
+ if (changed) slotNames = names;
205
+ return changed;
208
206
  }
209
207
  const render = () => {
210
208
  nextSlotNames = collectSlotNames();
211
- if (nextSlotNames.length === 0 || !ready.value || !isMounted.value) return;
209
+ if (nextSlotNames.size === 0 || !ready.value || !isMounted.value) return;
212
210
  const ownerDocument = instance.vnode.el.ownerDocument;
213
211
  detachedRoot ??= ownerDocument.createElement("div");
214
- return h(Teleport, { to: detachedRoot }, nextSlotNames.map((slotName) => {
212
+ return h(Teleport, { to: detachedRoot }, Array.from(nextSlotNames, (slotName) => {
215
213
  const slot = slots[slotName];
216
214
  const slotContent = slotName in params ? slot?.(params[slotName]) : void 0;
217
215
  return h("div", {
@@ -224,32 +222,41 @@ function useSlotOption(slots, onSlotsChange) {
224
222
  }, slotContent);
225
223
  }));
226
224
  };
227
- function patchOption(src) {
225
+ function prepare(src) {
228
226
  const names = collectSlotNames();
229
227
  syncSlotNames(names);
228
+ const writable = /* @__PURE__ */ new WeakSet();
230
229
  let root;
231
- for (const key of patchedSlotNames) if (!names.includes(key)) {
230
+ for (const key of patchedSlotNames) if (!names.has(key)) {
232
231
  root ??= { ...src };
233
- writePath(root, getSlotPath(key), null, true);
234
- }
235
- if (names.length === 0) {
236
- patchedSlotNames = [];
237
- return root ?? src;
232
+ writePath(root, getSlotPath(key), null, writable, true);
238
233
  }
239
- root ??= { ...src };
240
234
  const patchedNames = [];
241
235
  for (const key of names) {
242
- const formatter = (payload) => {
243
- if (!slots[key]) return;
244
- if (key in params && Object.is(params[key], payload)) delete params[key];
245
- params[key] = payload;
246
- return containers[key];
247
- };
248
- if (!writePath(root, getSlotPath(key), formatter)) continue;
236
+ root ??= { ...src };
237
+ let callback = callbacks.get(key);
238
+ if (!callback) {
239
+ const formatter = (payload) => {
240
+ if (!slots[key]) return;
241
+ if (key in params && Object.is(params[key], payload)) delete params[key];
242
+ params[key] = payload;
243
+ return containers[key];
244
+ };
245
+ callback = {
246
+ path: getSlotPath(key),
247
+ formatter
248
+ };
249
+ callbacks.set(key, callback);
250
+ }
251
+ if (!writePath(root, callback.path, callback.formatter, writable)) continue;
249
252
  patchedNames.push(key);
250
253
  }
251
- patchedSlotNames = patchedNames;
252
- return root;
254
+ return {
255
+ option: root ?? src,
256
+ commit: () => {
257
+ patchedNames.forEach((name) => patchedSlotNames.add(name));
258
+ }
259
+ };
253
260
  }
254
261
  onUpdated(() => {
255
262
  if (syncSlotNames(nextSlotNames)) onSlotsChange();
@@ -260,9 +267,11 @@ function useSlotOption(slots, onSlotsChange) {
260
267
  });
261
268
  return {
262
269
  render,
263
- hasNewSlots,
264
- patchOption,
265
- setReady
270
+ prepare,
271
+ setReady,
272
+ cancelPendingUpdate: () => {
273
+ syncSlotNames(collectSlotNames());
274
+ }
266
275
  };
267
276
  }
268
277
  //#endregion
@@ -303,7 +312,7 @@ function useReactiveChartListeners(chart, attrs) {
303
312
  for (const binding of bindings.values()) binding.emitter.off(binding.event, binding.handler);
304
313
  bindings.clear();
305
314
  }
306
- const stopWatch = watchSyncEffect(() => {
315
+ function sync() {
307
316
  seen.clear();
308
317
  const instance = chart.value;
309
318
  for (const [key, source] of consumedSources) if (attrs[key] !== source) consumedSources.delete(key);
@@ -354,219 +363,46 @@ function useReactiveChartListeners(chart, attrs) {
354
363
  binding.emitter.off(binding.event, binding.handler);
355
364
  bindings.delete(key);
356
365
  }
357
- });
366
+ }
367
+ const stopWatch = watchSyncEffect(sync);
368
+ if (getCurrentInstance()) onUpdated(sync);
358
369
  const stop = () => {
359
370
  stopWatch();
360
371
  clearBindings();
361
372
  };
362
373
  onScopeDispose(stop);
363
- return stop;
364
- }
365
- function useRootAttrs(attrs) {
366
- return computed(() => {
367
- const result = {};
368
- for (const key in attrs) {
369
- if (key.startsWith(NATIVE_EVENT_PREFIX)) {
370
- const event = key.slice(9);
371
- if (event) result[`on:${event}`] = attrs[key];
372
- continue;
373
- }
374
- if (!isOn(key)) result[key] = attrs[key];
375
- }
376
- return result;
377
- });
378
- }
379
- //#endregion
380
- //#region src/update.ts
381
- function toIdentity(value) {
382
- return typeof value === "string" || typeof value === "number" ? String(value) : void 0;
383
- }
384
- function buildShape(value, context, mode) {
385
- if (!isPlainObject(value)) return true;
386
- const shape = Object.create(null);
387
- for (const key of Object.keys(value)) {
388
- const child = value[key];
389
- if (child === void 0) continue;
390
- if (mode === "graphic" && key === "$action") context.hasAction = true;
391
- if (mode === "graphic" && (key === "elements" || key === "children") && Array.isArray(child)) {
392
- shape[key] = analyzeItems(child, context, "graphic", true);
393
- continue;
394
- }
395
- if (mode === "option") {
396
- const componentItems = ComponentModel.hasClass(key);
397
- if (Array.isArray(child) || componentItems && isPlainObject(child)) {
398
- shape[key] = analyzeItems(Array.isArray(child) ? child : [child], context, key === "graphic" ? "graphic" : void 0, componentItems);
399
- continue;
400
- }
401
- }
402
- shape[key] = mode === "media" && key === "option" ? buildShape(child, context, "option") : buildShape(child, context);
403
- }
404
- return shape;
405
- }
406
- function analyzeItems(items, context, mode, componentItems) {
407
- const shapes = [];
408
- for (const item of items) {
409
- const identity = componentItems ? item : void 0;
410
- shapes.push({
411
- id: toIdentity(identity?.id),
412
- name: toIdentity(identity?.name),
413
- shape: buildShape(item, context, mode)
414
- });
415
- }
416
- return shapes;
417
- }
418
- /**
419
- * Build a structural signature that retains component identities but not option payload values.
420
- * Data arrays inside component items remain leaves. Graphic element trees and nested option units
421
- * (`baseOption`, timeline options, and media options) reuse collection summaries.
422
- */
423
- function buildSignature(option) {
424
- const opt = option;
425
- const context = { hasAction: false };
426
- const collections = Object.create(null);
427
- const objectShapes = Object.create(null);
428
- const leaves = [];
429
- for (const key of Object.keys(opt)) {
430
- const value = opt[key];
431
- const componentItems = ComponentModel.hasClass(key);
432
- if (Array.isArray(value) || componentItems && isPlainObject(value)) {
433
- const mode = key === "options" ? "option" : key === "media" ? "media" : key === "graphic" ? "graphic" : void 0;
434
- collections[key] = analyzeItems(Array.isArray(value) ? value : [value], context, mode, componentItems);
435
- continue;
436
- }
437
- if (isPlainObject(value)) {
438
- objectShapes[key] = buildShape(value, context, key === "baseOption" ? "option" : void 0);
439
- continue;
440
- }
441
- if (value !== void 0 && !componentItems) leaves.push(key);
442
- }
443
374
  return {
444
- collections,
445
- objectShapes,
446
- leaves,
447
- hasAction: context.hasAction
375
+ sync,
376
+ stop
448
377
  };
449
378
  }
450
- function hasCollectionRemoval(prev, next) {
451
- if (!next) return prev.length > 0;
452
- return next.length < prev.length;
453
- }
454
- function getItemIdentity(item) {
455
- return item.id !== void 0 ? `id:${item.id}` : item.name !== void 0 ? `name:${item.name}` : void 0;
456
- }
457
- function hasIdentityChange(prev, next) {
458
- const length = Math.min(prev.length, next.length);
459
- for (let index = 0; index < length; index++) {
460
- const previous = getItemIdentity(prev[index]);
461
- const current = getItemIdentity(next[index]);
462
- if (previous !== current && (previous !== void 0 || current !== void 0)) return true;
463
- }
464
- return false;
465
- }
466
- function preservesReplacementOrder(prev, next) {
467
- const positions = /* @__PURE__ */ new Map();
468
- for (let index = 0; index < prev.length; index++) {
469
- const item = prev[index];
470
- if (item.id !== void 0) positions.set(item.id, index);
471
- }
472
- return next.every((item, index) => {
473
- const previousIndex = item.id === void 0 ? void 0 : positions.get(item.id);
474
- return previousIndex === void 0 || previousIndex === index;
475
- });
476
- }
477
- function hasShapeRemoval(prev, next) {
478
- if (prev === true || next === true) return false;
479
- const prevIsCollection = Array.isArray(prev);
480
- const nextIsCollection = Array.isArray(next);
481
- if (prevIsCollection || nextIsCollection) return !prevIsCollection || !nextIsCollection || hasCollectionRemoval(prev, next) || hasIdentityChange(prev, next) || findItemShapeRemoval(prev, next) !== void 0;
482
- for (const key in prev) {
483
- const nextChild = next[key];
484
- if (nextChild === void 0 || hasShapeRemoval(prev[key], nextChild)) return true;
485
- }
486
- return false;
487
- }
488
- function findItemShapeRemoval(prev, next) {
489
- let anonymousRemoval;
490
- for (let index = 0; index < prev.length; index++) {
491
- const item = prev[index];
492
- let nextShape;
493
- if (item.id !== void 0) nextShape = next.find((candidate) => candidate.id === item.id)?.shape;
494
- else nextShape = next[index]?.shape;
495
- if (nextShape && hasShapeRemoval(item.shape, nextShape)) {
496
- if (item.id !== void 0) return item;
497
- anonymousRemoval ??= item;
498
- }
499
- }
500
- return anonymousRemoval;
501
- }
502
- /** Returns replacements, undefined for a plain merge, or null when a rebuild is required. */
503
- function collectReplacements(prev, next) {
504
- let replaceMerge;
505
- if (next.objectShapes.aria && !prev.objectShapes.aria) return null;
506
- for (const key in next.collections) if (!prev.collections[key] && !prev.objectShapes[key] && !prev.leaves.includes(key) && !ComponentModel.hasClass(key)) return null;
507
- for (const key in prev.objectShapes) {
508
- const prevShape = prev.objectShapes[key];
509
- const nextShape = next.objectShapes[key];
510
- if (prevShape && nextShape) {
511
- if (hasShapeRemoval(prevShape, nextShape)) return null;
379
+ function getRootAttrs(attrs) {
380
+ const result = {};
381
+ for (const key in attrs) {
382
+ if (key.startsWith(NATIVE_EVENT_PREFIX)) {
383
+ const event = key.slice(9);
384
+ if (event) result[`on:${event}`] = attrs[key];
512
385
  continue;
513
386
  }
514
- if (!next.leaves.includes(key)) return null;
387
+ if (!isOn(key)) result[key] = attrs[key];
515
388
  }
516
- for (const key of prev.leaves) if (!next.leaves.includes(key) && next.collections[key] === void 0 && next.objectShapes[key] === void 0) return null;
517
- for (const key in prev.collections) {
518
- const prevCollection = prev.collections[key];
519
- if (key === "graphic" && next.hasAction) continue;
520
- const nextCollection = next.collections[key];
521
- if (!nextCollection && !ComponentModel.hasClass(key)) return null;
522
- const collectionRemoval = hasCollectionRemoval(prevCollection, nextCollection);
523
- const identityChange = nextCollection && !collectionRemoval && hasIdentityChange(prevCollection, nextCollection);
524
- const shapeRemoval = nextCollection ? findItemShapeRemoval(prevCollection, nextCollection) : void 0;
525
- if (shapeRemoval?.id !== void 0) return null;
526
- if (!shapeRemoval && !collectionRemoval && !identityChange) continue;
527
- if (!ComponentModel.hasClass(key)) return null;
528
- if (nextCollection && !preservesReplacementOrder(prevCollection, nextCollection)) return null;
529
- (replaceMerge ??= []).push(key);
530
- }
531
- return replaceMerge;
532
- }
533
- /**
534
- * Produce an update plan that preserves option structure and initialization semantics.
535
- * Falls back to `notMerge: true` when the change looks complex.
536
- */
537
- function planUpdate(prev, option) {
538
- const next = buildSignature(option);
539
- if (!prev) return {
540
- signature: next,
541
- plan: { notMerge: false }
542
- };
543
- const replaceMerge = collectReplacements(prev, next);
544
- if (replaceMerge === null) return {
545
- signature: next,
546
- plan: { notMerge: !next.hasAction }
547
- };
548
- replaceMerge?.sort();
549
- return {
550
- signature: next,
551
- plan: replaceMerge ? {
552
- notMerge: false,
553
- replaceMerge
554
- } : { notMerge: false }
555
- };
389
+ return result;
556
390
  }
557
391
  //#endregion
558
392
  //#region src/style.css?raw
559
- var style_default = "x-vue-echarts{display:block;width:100%;height:100%;min-width:0;}\nx-vue-echarts>.echarts-host{display:block;width:100%;height:100%;min-width:0;}\nx-vue-echarts>.echarts-host>:first-child,x-vue-echarts>.echarts-host>:first-child>canvas{border-radius:inherit;}\n";
393
+ var style_default = "x-vue-echarts{display:block;width:100%;height:100%;min-width:0;min-height:0;}\nx-vue-echarts>.echarts-host{display:block;width:100%;height:100%;min-width:0;border-radius:inherit;}\nx-vue-echarts>.echarts-host>:first-child,x-vue-echarts>.echarts-host>:first-child>canvas{border-radius:inherit;}\n";
560
394
  //#endregion
561
395
  //#region src/style.ts
562
- if (isBrowser()) if (Array.isArray(document.adoptedStyleSheets) && "replaceSync" in CSSStyleSheet.prototype) {
563
- const sheet = new CSSStyleSheet();
564
- sheet.replaceSync(style_default);
565
- document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
566
- } else {
567
- const styleEl = document.createElement("style");
568
- styleEl.textContent = style_default;
569
- document.head.appendChild(styleEl);
396
+ if (isBrowser()) {
397
+ if (Array.isArray(document.adoptedStyleSheets) && "replaceSync" in CSSStyleSheet.prototype) {
398
+ const sheet = new CSSStyleSheet();
399
+ sheet.replaceSync(style_default);
400
+ document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
401
+ } else {
402
+ const styleEl = document.createElement("style");
403
+ styleEl.textContent = style_default;
404
+ document.head.appendChild(styleEl);
405
+ }
570
406
  }
571
407
  //#endregion
572
408
  //#region src/ECharts.ts
@@ -575,29 +411,30 @@ const SKIP_AUTO_UPDATE = Symbol();
575
411
  const THEME_KEY = Symbol();
576
412
  const INIT_OPTIONS_KEY = Symbol();
577
413
  const UPDATE_OPTIONS_KEY = Symbol();
414
+ const chartProps = {
415
+ option: Object,
416
+ theme: { type: [Object, String] },
417
+ initOptions: Object,
418
+ updateOptions: Object,
419
+ group: String,
420
+ manualUpdate: Boolean,
421
+ ...autoresizeProps,
422
+ ...loadingProps
423
+ };
578
424
  //#endregion
579
425
  //#region src/index.ts
580
426
  var src_default = /* @__PURE__ */ defineComponent({
581
427
  name: "Echarts",
582
428
  inheritAttrs: false,
583
- props: {
584
- option: Object,
585
- theme: { type: [Object, String] },
586
- initOptions: Object,
587
- updateOptions: Object,
588
- group: String,
589
- manualUpdate: Boolean,
590
- ...autoresizeProps,
591
- ...loadingProps
592
- },
429
+ props: chartProps,
593
430
  emits: {},
594
431
  slots: Object,
595
432
  setup(props, { attrs, expose, slots }) {
596
433
  const root = shallowRef();
597
434
  const chartHost = shallowRef();
598
435
  const chart = shallowRef();
599
- const themeRevision = shallowRef(0);
600
- const initOptionsRevision = shallowRef(0);
436
+ const updateRequest = shallowRef(0);
437
+ let pendingUpdate = 0;
601
438
  const defaultTheme = inject(THEME_KEY, null);
602
439
  const defaultInitOptions = inject(INIT_OPTIONS_KEY, null);
603
440
  const defaultUpdateOptions = inject(UPDATE_OPTIONS_KEY, null);
@@ -609,95 +446,131 @@ var src_default = /* @__PURE__ */ defineComponent({
609
446
  const realTheme = computed(() => props.theme ?? toValue(defaultTheme) ?? void 0);
610
447
  const realInitOptions = computed(() => props.initOptions ?? toValue(defaultInitOptions) ?? void 0);
611
448
  const realUpdateOptions = computed(() => props.updateOptions ?? toValue(defaultUpdateOptions));
612
- const rootAttrs = useRootAttrs(attrs);
613
449
  const terminallyDisposed = shallowRef(false);
614
- const { render: renderSlot, hasNewSlots, patchOption, setReady: setSlotsReady } = useSlotOption(slots, requestUpdate);
615
- const { patchOption: patchGraphicOption, render: renderGraphic } = useRuntime({
450
+ const { render: renderSlot, prepare: prepareSlots, setReady: setSlotsReady, cancelPendingUpdate: cancelSlotUpdate } = useSlotOption(slots, requestUpdate);
451
+ const graphic = useRuntime({
616
452
  slots,
617
453
  manualUpdate,
618
- requestUpdate: () => requestUpdate("graphic")
619
- }) ?? {};
454
+ requestUpdate: () => requestUpdate(2)
455
+ });
620
456
  let lastSignature;
621
457
  let lastAutoOption;
622
458
  let themeApplied = false;
623
- let initOptionsInvalidated = false;
624
459
  let optionApplied = false;
625
- let manualUpdateAtInit = manualUpdate.value;
626
460
  let initDeferred = false;
627
461
  let graphicSlotApplied = false;
628
- const updateFlush = patchGraphicOption ? "post" : "pre";
462
+ let updateRevision = 0;
463
+ let modelValid = false;
629
464
  function getAutoOption() {
630
- return props.option ?? (patchGraphicOption && (slots.graphic || graphicSlotApplied) ? {} : void 0);
465
+ return props.option ?? (graphic && (slots.graphic || graphicSlotApplied) ? {} : void 0);
631
466
  }
632
467
  function isCurrent(instance) {
633
468
  return instance !== void 0 && chart.value === instance && !terminallyDisposed.value;
634
469
  }
470
+ function runUpdate(instance, update, signature) {
471
+ const revision = ++updateRevision;
472
+ lastSignature = null;
473
+ modelValid = false;
474
+ update();
475
+ if (!isCurrent(instance) || updateRevision !== revision) return false;
476
+ lastSignature = signature;
477
+ modelValid = true;
478
+ return true;
479
+ }
635
480
  function applyTheme(instance) {
636
481
  if (!optionApplied || themeApplied) return false;
482
+ syncListeners();
483
+ const revision = updateRevision + 1;
637
484
  themeApplied = true;
638
- instance.setTheme(realTheme.value || {});
639
- return isCurrent(instance);
485
+ try {
486
+ return runUpdate(instance, () => instance.setTheme(realTheme.value || {}), lastSignature === void 0 ? void 0 : null);
487
+ } catch (error) {
488
+ if (updateRevision === revision) themeApplied = false;
489
+ throw error;
490
+ }
640
491
  }
641
492
  function applyOption(instance, option, mode, manualOptions) {
642
493
  const manual = mode === "manual";
643
494
  initDeferred = false;
644
- const slotted = patchOption(option);
645
- const patched = patchGraphicOption ? patchGraphicOption(slotted) : slotted;
646
- const forceGraphic = mode === "graphic";
647
- const skipPlanning = manual || forceGraphic;
648
- let updateOptions;
649
- let nextSignature;
650
- if (skipPlanning) updateOptions = forceGraphic ? realUpdateOptions.value ?? void 0 : manualOptions;
651
- else if (realUpdateOptions.value) {
652
- updateOptions = realUpdateOptions.value;
653
- nextSignature = null;
654
- } else {
655
- const planned = planUpdate(lastSignature ?? void 0, slotted);
656
- updateOptions = !planned.signature.hasAction && (lastSignature === null || mode === "theme") ? {
495
+ const preparedSlots = prepareSlots(option);
496
+ const slotted = preparedSlots.option;
497
+ const hasGraphicSlot = Boolean(graphic && slots.graphic);
498
+ let updateOptions = manual ? manualOptions : realUpdateOptions.value ?? void 0;
499
+ let nextSignature = null;
500
+ const graphicOnly = mode === "graphic" && hasGraphicSlot && graphicSlotApplied && modelValid;
501
+ if (graphicOnly) nextSignature = lastSignature ?? null;
502
+ else if (!manual && !updateOptions) {
503
+ const source = hasGraphicSlot ? {
504
+ ...slotted,
505
+ graphic: void 0
506
+ } : slotted;
507
+ const planned = planUpdate(lastSignature ?? void 0, source);
508
+ updateOptions = !planned.signature.hasAction && lastSignature === null ? {
657
509
  ...planned.plan,
658
510
  notMerge: true
659
511
  } : planned.plan;
660
512
  nextSignature = planned.signature;
661
513
  }
662
- const hasGraphicSlot = Boolean(patchGraphicOption && slots.graphic);
663
- if ((forceGraphic || graphicSlotApplied || hasGraphicSlot) && !updateOptions?.notMerge) {
664
- const replaceMerge = updateOptions?.replaceMerge;
665
- const replacements = typeof replaceMerge === "string" ? [replaceMerge] : replaceMerge;
666
- if (!replacements?.includes("graphic")) updateOptions = {
667
- ...updateOptions,
668
- replaceMerge: replacements ? [...replacements, "graphic"] : ["graphic"]
669
- };
670
- }
514
+ const replaceMerge = updateOptions?.replaceMerge;
515
+ const replacements = typeof replaceMerge === "string" ? [replaceMerge] : replaceMerge ?? [];
516
+ const preparedGraphic = graphic?.prepare(slotted, !modelValid || !graphicSlotApplied || Boolean(updateOptions?.notMerge) || replacements.includes("graphic") || manual);
517
+ let patched = preparedGraphic?.option ?? slotted;
518
+ if ((preparedGraphic?.replace || graphicSlotApplied && !hasGraphicSlot) && !updateOptions?.notMerge && !replacements.includes("graphic")) updateOptions = {
519
+ ...updateOptions,
520
+ replaceMerge: [...replacements, "graphic"]
521
+ };
522
+ const replacesSource = replacements.some((key) => key !== "graphic");
523
+ if (graphicOnly && !updateOptions?.notMerge && !replacesSource) patched = { graphic: patched.graphic };
524
+ syncListeners();
525
+ if (!runUpdate(instance, () => instance.setOption(patched, updateOptions), nextSignature)) return;
526
+ preparedSlots.commit();
527
+ preparedGraphic?.commit();
671
528
  graphicSlotApplied = hasGraphicSlot;
672
529
  optionApplied = true;
673
- if (!skipPlanning) lastSignature = nextSignature;
674
530
  if (!manual) lastAutoOption = option;
675
- instance.setOption(patched, updateOptions);
676
- if (!isCurrent(instance) || mode === "theme") return;
677
- if (applyTheme(instance) && !manual) applyOption(instance, option, "theme");
531
+ if (applyTheme(instance) && !manual) applyOption(instance, option);
532
+ }
533
+ function scheduleUpdate(reason) {
534
+ pendingUpdate |= reason;
535
+ updateRequest.value++;
678
536
  }
679
- function requestUpdate(mode) {
537
+ function requestUpdate(reason = 1) {
538
+ if (!manualUpdate.value && !initDeferred && !terminallyDisposed.value) scheduleUpdate(reason);
539
+ }
540
+ function flushUpdate() {
541
+ const reasons = pendingUpdate;
542
+ pendingUpdate = 0;
680
543
  const instance = chart.value;
681
- if (!isCurrent(instance) || manualUpdate.value || initDeferred) return;
682
- const option = getAutoOption() ?? lastAutoOption;
683
- if (!option) return;
684
- applyOption(instance, option, mode);
544
+ if (!reasons || !isCurrent(instance)) return;
545
+ if (reasons & 8) {
546
+ cleanup();
547
+ init$1();
548
+ return;
549
+ }
550
+ if (initDeferred) return;
551
+ const option = reasons & 3 ? getAutoOption() ?? lastAutoOption : lastAutoOption;
552
+ const needsTheme = !themeApplied && optionApplied;
553
+ if (needsTheme && !applyTheme(instance)) return;
554
+ if (!manualUpdate.value && option) applyOption(instance, option, reasons === 2 && !needsTheme ? "graphic" : void 0);
685
555
  }
686
- if (slots.graphic && !patchGraphicOption) warn$1("Detected `#graphic` slot but no extension is registered. Import from `vue-echarts/graphic` to enable it.");
687
- const stopListeners = useReactiveChartListeners(chart, attrs);
556
+ if (slots.graphic && !graphic) warn$1("Detected `#graphic` slot but no extension is registered. Import from `vue-echarts/graphic` to enable it.");
557
+ const { sync: syncListeners, stop: stopListeners } = useReactiveChartListeners(chart, attrs);
688
558
  function cleanup() {
559
+ pendingUpdate = 0;
560
+ graphic?.cancelPendingFlush();
561
+ updateRevision++;
689
562
  const instance = chart.value;
690
563
  chart.value = void 0;
691
564
  setSlotsReady(false);
692
565
  lastSignature = void 0;
566
+ modelValid = false;
693
567
  lastAutoOption = void 0;
694
568
  initDeferred = false;
695
569
  graphicSlotApplied = false;
696
570
  instance?.dispose();
697
571
  }
698
572
  function init$1() {
699
- initOptionsInvalidated = false;
700
- manualUpdateAtInit = manualUpdate.value;
573
+ pendingUpdate = 0;
701
574
  optionApplied = false;
702
575
  const host = chartHost.value;
703
576
  const instance = init(host, realTheme.value, realInitOptions.value);
@@ -706,8 +579,10 @@ var src_default = /* @__PURE__ */ defineComponent({
706
579
  themeApplied = true;
707
580
  function commit() {
708
581
  const option = manualUpdate.value ? props.option : getAutoOption();
709
- if (option) if (manualUpdate.value) applyOption(instance, option, "manual", realUpdateOptions.value ?? void 0);
710
- else applyOption(instance, option);
582
+ if (option) {
583
+ if (manualUpdate.value) applyOption(instance, option, "manual", realUpdateOptions.value ?? void 0);
584
+ else applyOption(instance, option);
585
+ }
711
586
  setSlotsReady(isCurrent(instance));
712
587
  }
713
588
  if (autoresize.value) {
@@ -741,46 +616,26 @@ var src_default = /* @__PURE__ */ defineComponent({
741
616
  };
742
617
  const stopThemeWatch = watch(realTheme, (theme, previousTheme) => {
743
618
  if (isIgnorableWatchChange(theme, previousTheme)) return;
744
- themeRevision.value++;
745
619
  themeApplied = false;
620
+ scheduleUpdate(4);
746
621
  }, {
747
622
  deep: true,
748
623
  flush: "sync"
749
624
  });
750
625
  const stopInitOptionsWatch = watch(realInitOptions, (options, previousOptions) => {
751
- if (!isIgnorableWatchChange(options, previousOptions)) {
752
- initOptionsInvalidated = true;
753
- initOptionsRevision.value++;
754
- }
626
+ if (!isIgnorableWatchChange(options, previousOptions)) scheduleUpdate(8);
755
627
  }, {
756
628
  deep: true,
757
629
  flush: "sync"
758
630
  });
759
- const stopOptionWatch = watch(() => manualUpdate.value ? SKIP_AUTO_UPDATE : props.option, (option, previousOption) => {
760
- if (option === SKIP_AUTO_UPDATE || previousOption === SKIP_AUTO_UPDATE) return;
761
- const nextOption = option ?? getAutoOption();
762
- if (!nextOption) return;
763
- const instance = chart.value;
764
- if (initOptionsInvalidated || !isCurrent(instance) || initDeferred) return;
765
- if (hasNewSlots()) return;
766
- applyOption(instance, nextOption);
767
- }, {
768
- deep: true,
769
- flush: updateFlush
770
- });
771
- const stopReinitWatch = watch([manualUpdate, initOptionsRevision], () => {
772
- if (!chart.value) return;
773
- cleanup();
774
- init$1();
775
- });
776
- const stopThemeApplyWatch = watch(themeRevision, () => {
777
- const instance = chart.value;
778
- if (!initOptionsInvalidated && manualUpdate.value === manualUpdateAtInit && isCurrent(instance) && !themeApplied) {
779
- if (!applyTheme(instance)) return;
780
- const option = lastAutoOption;
781
- if (option && !manualUpdate.value && !initDeferred && !hasNewSlots()) applyOption(instance, option, "theme");
782
- }
783
- }, { flush: updateFlush });
631
+ function watchOption() {
632
+ return watch(() => manualUpdate.value ? SKIP_AUTO_UPDATE : props.option, (option, previousOption) => {
633
+ if (option !== SKIP_AUTO_UPDATE && previousOption !== SKIP_AUTO_UPDATE && (option ?? getAutoOption())) requestUpdate();
634
+ }, { deep: true });
635
+ }
636
+ let stopOptionWatch = watchOption();
637
+ const stopModeWatch = watch(manualUpdate, () => scheduleUpdate(8), { flush: "sync" });
638
+ const stopUpdateWatch = watch(updateRequest, flushUpdate, { flush: "post" });
784
639
  const stopGroupWatch = watchSyncEffect(() => {
785
640
  const instance = chart.value;
786
641
  if (isCurrent(instance)) instance.group = props.group ?? "";
@@ -793,8 +648,8 @@ var src_default = /* @__PURE__ */ defineComponent({
793
648
  stopThemeWatch();
794
649
  stopInitOptionsWatch();
795
650
  stopOptionWatch();
796
- stopReinitWatch();
797
- stopThemeApplyWatch();
651
+ stopModeWatch();
652
+ stopUpdateWatch();
798
653
  stopGroupWatch();
799
654
  stopLoading();
800
655
  stopAutoresize();
@@ -807,14 +662,22 @@ var src_default = /* @__PURE__ */ defineComponent({
807
662
  const instance = chart.value;
808
663
  if (!isCurrent(instance)) return guardedClear();
809
664
  initDeferred = false;
810
- lastSignature = void 0;
811
- lastAutoOption = void 0;
812
- instance.clear();
665
+ pendingUpdate = 0;
666
+ graphic?.cancelPendingFlush();
667
+ cancelSlotUpdate();
668
+ stopOptionWatch();
669
+ stopOptionWatch = watchOption();
670
+ if (runUpdate(instance, () => instance.clear(), void 0)) {
671
+ lastAutoOption = void 0;
672
+ graphicSlotApplied = false;
673
+ optionApplied = true;
674
+ }
813
675
  };
814
676
  onMounted(() => {
815
677
  if (!terminallyDisposed.value) init$1();
816
678
  });
817
679
  onBeforeUnmount(() => {
680
+ stopOptionWatch();
818
681
  if (terminallyDisposed.value) return;
819
682
  terminallyDisposed.value = true;
820
683
  const element = root.value;
@@ -842,12 +705,10 @@ var src_default = /* @__PURE__ */ defineComponent({
842
705
  if (!terminallyDisposed.value) {
843
706
  const teleported = renderSlot();
844
707
  if (teleported) children.push(teleported);
845
- if (renderGraphic) {
846
- const graphic = renderGraphic();
847
- if (graphic) children.push(graphic);
848
- }
708
+ const graphicContent = graphic?.render();
709
+ if (graphicContent) children.push(graphicContent);
849
710
  }
850
- const forwardedAttrs = rootAttrs.value;
711
+ const forwardedAttrs = getRootAttrs(attrs);
851
712
  return h(TAG_NAME, {
852
713
  ...forwardedAttrs,
853
714
  ref: root,