Compare commits

...

21 Commits

Author SHA1 Message Date
36c3cb97a9 chore: release v8.0.0-beta.1 2025-08-10 23:37:53 +08:00
55c68b48b7 chore: release and publish from github actions (#850) 2025-08-10 23:31:34 +08:00
c232e71c47 chore: add echarts 6 features in codegen
* chore: add echarts 6 features in codegen

* update according to 772cf01859
2025-08-10 23:31:34 +08:00
e568005bb2 docs: update version, deps and docs for 8.0 beta (#849)
* chore: up version and deps

* chore: use pnpm CLI to get versions

* add note for echarts 6 upgrade guide

* remove docs script

let cdn to redirect for us

* Revert "remove docs script"

This reverts commit 3bc237db9100864f2813249ac1693735a658e646.

* update demo links
2025-08-10 23:31:23 +08:00
8ed975e09b feat!: inject style via constructable CSSStyleSheet and remove CSP entry (#847)
* chore: not inject inline css on server

* feat!: remove csp entry

* keep csp title in readme

* chore: switch to rolldown and tsdown

* update

* dedupe

* update according to review

* emphasize "both" in csp section

* load css with unplugin-raw

* change tsdown entry
2025-08-10 23:26:17 +08:00
570a26c262 feat: rendering tooltips and dataView with slots (#838)
* feat: experimental component rendered tooltip

* revert slot in VChart

* feat: use tooltip composable

* feat: try createApp

* feat: use pie chart as tooltip

* feat: switch to createVNode

The limitation is that the tooltip detached from the current component tree, not provide/inject

will try teleport next

* feat: try component with teleport

* wip

* add xAxis example

* refactor with shallowReactive

* Support dynamic slot

* fix: fill empty elements with object in array

* shallow copy option along the path

* ssr friendly

* vibe docs

* typo

* update according to the review

* add dataView slot

* chore: fix warnings and errors in demo (#839)

* chore: suppress warning in demo

* chore: prevent multiple intializations of esbuild-wasm in demo HMR

* feat: dynamically update the theme (#841)

Co-authored-by: GU Yiling <justice360@gmail.com>

* feat: add dataView slot

* vibe docs

---------

Co-authored-by: GU Yiling <justice360@gmail.com>

* fix docs typo

* update according to the review

* small fix

* remove wrapper around slotProp

* update comments

* remove anys

* add tooltip slot prop type

* target to vue 3.3

* move slot related codes to slot.ts

---------

Co-authored-by: GU Yiling <justice360@gmail.com>
2025-08-10 23:26:17 +08:00
df640ebce6 chore: remove large mode for flight example (#845) 2025-08-10 23:26:17 +08:00
30e7934aab feat: dynamically update the theme (#841)
Co-authored-by: GU Yiling <justice360@gmail.com>
2025-08-10 23:26:12 +08:00
6155bbb409 chore: fix warnings and errors in demo (#839)
* chore: suppress warning in demo

* chore: prevent multiple intializations of esbuild-wasm in demo HMR
2025-08-10 23:22:04 +08:00
fa42af0723 refactor: use Web Components without native class support detection (#836) 2025-08-10 23:22:04 +08:00
8b7ef5e6e1 refactor: switch to generated .d.ts (#835)
* build: generate d.ts

* fix: preserve PublicMethods

* fix: avoid exposing types of attrs

* refactor: use existing setoption type

* fix: expose root and chart

* feat: use symbol as injection key

* chore: add comment for the type casting of the exposed
2025-08-10 23:22:04 +08:00
522dd7cc5c chore: ESLint Flat Config (#834)
* chore: eslint flat config

* chore: format

* update according to review

* chore: remove prettier config and format

* fix: move handler to script to bypass eslint

* chore: config eslint for lang=js block

* docs: add surrounding empty lines for code block

* chore: also minify css in csp build

* chore: publint
2025-08-10 23:22:04 +08:00
077bd3ec40 build: migrate demo from webpack to Vite (#832) 2025-08-10 23:22:04 +08:00
473fed37a2 chore: remove @vue/runtime-core from peerDependencies
@vue/runtime-core was added here for supporting typescript in vue < 2.7
2025-08-10 23:22:04 +08:00
7ae6892fe6 refactor: change listeners from object to Map 2025-08-10 23:22:03 +08:00
71c106ae29 refactor: rename realListeners to listeners 2025-08-10 23:22:03 +08:00
381489da2f docs: update provide/inject section 2025-08-10 23:22:03 +08:00
2fb0dc2233 refactor: simplify render function 2025-08-10 23:22:03 +08:00
b6c84aab7e feat: support getter in provide/inject 2025-08-10 23:22:03 +08:00
c6a1228c9d docs: remove vue 2 related content 2025-08-10 23:21:47 +08:00
9067505a3a feat!: remove vue 2 2025-08-10 23:19:04 +08:00
88 changed files with 3385 additions and 9875 deletions

View File

@ -1,3 +0,0 @@
> 1%
last 2 versions
not dead

View File

@ -1 +0,0 @@
dist

View File

@ -1,20 +0,0 @@
{
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/vue3-essential",
"eslint:recommended",
"@vue/eslint-config-typescript",
"@vue/eslint-config-prettier/skip-formatting"
],
"parserOptions": {
"ecmaVersion": 2020,
"parser": "@typescript-eslint/parser"
},
"rules": {
"no-console": "off",
"vue/multi-word-component-names": "off"
}
}

View File

@ -1,37 +0,0 @@
> ## Help / 帮助
>
> Replace the `[ ]` with `[x]` to check an option. / 将 `[ ]` 替换为 `[x]` 以选择对应选项。
## The type of this issue / Issue 类型
- [ ] Feature request / 新特性需求
- [ ] Bug report / Bug 报告
## Not introduced by ECharts / 非 ECharts 本身问题
Problems about ECharts itself are not handled in this repo. / 本 repo 不负责处理 ECharts 本身的问题。
- [ ] I've checked it's not a problem of ECharts itself. / 我已检查过,这个问题非 ECharts 本身的问题。
## Details / 详情
### Vue version / Vue 版本
- [ ] Vue 3
- [ ] Vue 2
### How are you importing Vue-ECharts? / 你是如何引入 Vue-ECharts 的?
- [ ] Importing `vue-echarts` with a bundler environment / 在 webpack 等打包工具环境下引入 `vue-echarts`
- [ ] Using the global variable by including `<script>` tags / 通过 `<script>` 标签引入全局变量
### The version of Vue-ECharts you are using / Vue-ECharts 的版本
> eg. 6.0.0-beta.5
## Reproduction link / 复现链接
**For bug reports, please provide the steps to reproduce and if possible a minimal demo of the problem. Please paste the link to your CodeSandbox demo below: ([Vue 3 template](https://codesandbox.io/s/charming-night-2y6m6?file=/src/App.vue) / [Vue 2 template](https://codesandbox.io/s/suspicious-glitter-mk66j?file=/src/App.vue))**
**对于 Bug 报告,请在下面提供复现的步骤,最好是最小化的能够重现问题的 demo。请在下方贴入在 CodeSandbox 上 demo 的链接:([Vue 3 模板](https://codesandbox.io/s/charming-night-2y6m6?file=/src/App.vue) / [Vue 2 模板](https://codesandbox.io/s/suspicious-glitter-mk66j?file=/src/App.vue)**

View File

@ -41,6 +41,6 @@ body:
id: repro
attributes:
label: Reproduction
description: "A link to a boiled-down reproduction (a minimal but runnable demo with unnecessary dependencies pruned). If the issue isn't reproducible within an online playground, please create a GitHub repo to reflect the problem. Please paste the link to your CodeSandbox demo or GitHub repo below: ([Vue 3 template](https://codesandbox.io/s/charming-night-2y6m6?file=/src/App.vue) / [Vue 2 template](https://codesandbox.io/s/suspicious-glitter-mk66j?file=/src/App.vue))"
description: "A link to a boiled-down reproduction (a minimal but runnable demo with unnecessary dependencies pruned). If the issue isn't reproducible within an online playground, please create a GitHub repo to reflect the problem. Please paste the link to your [StackBlitz demo](https://stackblitz.com/edit/vue-echarts-8?file=src%2FApp.vue) or GitHub repo below:"
validations:
required: true

View File

@ -41,6 +41,6 @@ body:
id: repro
attributes:
label: 问题复现
description: "请提供一个精炼的问题复现(去除无关依赖的最小化可运行 demo。如果在线环境无法复现可以创建对应的 GitHub repo 来提供复现环境。请在下方贴入在 CodeSandbox 上 demo 的链接或 GitHub repo 链接:([Vue 3 模板](https://codesandbox.io/s/charming-night-2y6m6?file=/src/App.vue) / [Vue 2 模板](https://codesandbox.io/s/suspicious-glitter-mk66j?file=/src/App.vue)"
description: "请提供一个精炼的问题复现(去除无关依赖的最小化可运行 demo。如果在线环境无法复现可以创建对应的 GitHub repo 来提供复现环境。请在下方贴入 [StackBlitz demo](https://stackblitz.com/edit/vue-echarts-8?file=src%2FApp.vue) 的链接或 GitHub repo 链接:"
validations:
required: true

38
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,38 @@
name: CI
on:
pull_request:
push:
branches:
- main
- "8.0" # remove this after 8.0 is merged into main
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: "pnpm"
- name: Install dependencies
run: pnpm install
- name: Lint
run: pnpm run lint
- name: Typecheck
run: pnpm run typecheck && pnpm run dev:typecheck
- name: Build
run: pnpm run build
- name: Publint
run: pnpm run publint

52
.github/workflows/release.yml vendored Normal file
View File

@ -0,0 +1,52 @@
name: Release
on:
push:
tags:
- "v**"
permissions:
id-token: write
contents: write
jobs:
release:
runs-on: ubuntu-latest
environment: npm-publish
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- name: Install dependencies
run: pnpm install
- name: Extract release notes
run: pnpm releaselog --format=notes ${{ github.ref_name }} > RELEASE_NOTES.md
- name: Create GitHub release
uses: softprops/action-gh-release@v2
with:
body_path: RELEASE_NOTES.md
prerelease: ${{ contains(github.ref_name, '-') }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build
run: pnpm run build
- name: Get dist tag
id: tag
run: echo "tag=$(pnpm exec jiti scripts/dist-tag.ts '${{ github.ref_name }}')" >> $GITHUB_OUTPUT
- name: Publish to npm
run: npm i -g npm && pnpm publish --access public --tag ${{ steps.tag.outputs.tag }} --no-git-checks

2
.gitignore vendored
View File

@ -22,4 +22,4 @@ pnpm-debug.log*
*.sln
*.sw?
/demo
/demo/dist

1
.npmrc
View File

@ -1 +0,0 @@
strict-peer-dependencies = false

3
.prettierignore Normal file
View File

@ -0,0 +1,3 @@
pnpm-lock.yaml
demo/data/*.json
src/style.css

View File

@ -1,4 +0,0 @@
{
"trailingComma": "none",
"arrowParens": "avoid"
}

View File

@ -1,4 +0,0 @@
{
"installDependencies": true,
"startCommand": "pnpm serve"
}

View File

@ -1,481 +1,504 @@
## 8.0.0-beta.1
### Breaking changes
- Updated peer dependency for `echarts` to `^6.0.0`.
- Updated peer dependency for `vue` to `^3.3.0`.
- Dropped support for browsers without native `class` support.
- Removed `vue-echarts/csp` entry. Use `vue-echarts` instead. Only manually include `vue-echarts/style.css` if you are **both** enforcing a strict CSP that prevents inline `<style>` injection and targeting browsers that don't support the [`CSSStyleSheet()` constructor](https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet/CSSStyleSheet#browser_compatibility).
### New features
- ECharts 6 support.
- Added slots for tooltip and data view.
- Added support for getter in provide/inject.
### Chore
- Built with tsdown.
- Switched Demo from Webpack to Rolldown-Vite.
- Use ESLint flat config.
## 7.0.3
* Fixed type for `autoresize` (again).
- Fixed type for `autoresize` (again).
## 7.0.2
* Fixed style injection regression (#805).
- Fixed style injection regression (#805).
## 7.0.1
* Fixed type for `autoresize`.
- Fixed type for `autoresize`.
## 7.0.0
> Other prerelease changes:
> * [7.0.0-beta.0](#700-beta0)
>
> - [7.0.0-beta.0](#700-beta0)
* Fixed types for events.
- Fixed types for events.
## 7.0.0-beta.0
* Upgraded to ESM.
* Removed the `postinstall` script.
* API remains the same.
- Upgraded to ESM.
- Removed the `postinstall` script.
- API remains the same.
### Breaking changes
* Peer deps for `echarts` is upgraded to `^5.5.1`.
* Dropped support for browsers without `ResizeObserver`. Can work with [resize-observer-polyfill](https://www.npmjs.com/package/resize-observer-polyfill).
* Dropped support for Vue < 2.7.
* Dropped CJS outputs.
* Dropped extra wrapper element so that you should no longer set `padding` on the component root.
* For strict CSP usage, `vue-echarts/csp` should now be used as the entry point and `vue-echarts/csp/style.css` should be included manually.
- Peer deps for `echarts` is upgraded to `^5.5.1`.
- Dropped support for browsers without `ResizeObserver`. Can work with [resize-observer-polyfill](https://www.npmjs.com/package/resize-observer-polyfill).
- Dropped support for Vue < 2.7.
- Dropped CJS outputs.
- Dropped extra wrapper element so that you should no longer set `padding` on the component root.
- For strict CSP usage, `vue-echarts/csp` should now be used as the entry point and `vue-echarts/csp/style.css` should be included manually.
## 6.7.3
* Fixed that `padding` on the component root doesn't work.
- Fixed that `padding` on the component root doesn't work.
## 6.7.2
* Fixed that charts inside `<keep-alive>` failed to display after activation.
- Fixed that charts inside `<keep-alive>` failed to display after activation.
## 6.7.1
* Fixed that native events won't actually trigger.
- Fixed that native events won't actually trigger.
## 6.7.0
* Added supports for native DOM events binding with the `native:` prefix.
- Added supports for native DOM events binding with the `native:` prefix.
## 6.6.10
* Fixed that `autoresize` doesn't work when reducing the height or the root element.
- Fixed that `autoresize` doesn't work when reducing the height or the root element.
## 6.6.9
* Fixed that the chart may not be the same size as the component root element ([#761](https://github.com/ecomfe/vue-echarts/issues/761)).
- Fixed that the chart may not be the same size as the component root element ([#761](https://github.com/ecomfe/vue-echarts/issues/761)).
## 6.6.8
* Fixed the postinstall script to patch the correct `types` entry for Vue 2.7.
- Fixed the postinstall script to patch the correct `types` entry for Vue 2.7.
## 6.6.7
* Added missing type file for Vue 2.7.
- Added missing type file for Vue 2.7.
## 6.6.6
* Fixed types for Vue < 2.7.
- Fixed types for Vue < 2.7.
## 6.6.5
* Fixed type for `option` regressed in v6.6.2.
- Fixed type for `option` regressed in v6.6.2.
## 6.6.4
* Fixed style regression introduced by v6.6.3.
- Fixed style regression introduced by v6.6.3.
## 6.6.3
* Fixed inner wrapper styles.
- Fixed inner wrapper styles.
## 6.6.2
* Fixed that tooltips may affected by internal styling by VueECharts.
- Fixed that tooltips may affected by internal styling by VueECharts.
## 6.6.1
* Make `padding` work out-of-the-box.
- Make `padding` work out-of-the-box.
## 6.6.0
* Added support for `autoresize` accepting an options object to specify custom throttle delay or resize callback.
- Added support for `autoresize` accepting an options object to specify custom throttle delay or resize callback.
## 6.5.5
* Removed the custom element registration enhancement for strict CSP builds so that they won't contain `new Function`.
- Removed the custom element registration enhancement for strict CSP builds so that they won't contain `new Function`.
## 6.5.4
* Cleaned up the `console.log` call sneaked in by mistake.
- Cleaned up the `console.log` call sneaked in by mistake.
## 6.5.3
* Fixed default behavior for `notMerge` option (#691).
- Fixed default behavior for `notMerge` option (#691).
## 6.5.2
* Added `dist/csp/*` to support strict CSP with extracted CSS file.
- Added `dist/csp/*` to support strict CSP with extracted CSS file.
## 6.5.1
* Fixed types for mouse events.
- Fixed types for mouse events.
## 6.5.0
* Use more precise typings for all event params.
* Updated peer deps for `echarts` to `^5.4.1`.
- Use more precise typings for all event params.
- Updated peer deps for `echarts` to `^5.4.1`.
## 6.4.1
* Improve typings for mouse event params.
- Improve typings for mouse event params.
## 6.4.0
* Delay the disposal of the ECharts instance to the moment the element is disconnected from the DOM if possible (#433).
- Delay the disposal of the ECharts instance to the moment the element is disconnected from the DOM if possible (#433).
## 6.3.3
* Make autoresize work for grid layout by default (#675).
- Make autoresize work for grid layout by default (#675).
## 6.3.2
* Added basic types for events (only event names).
- Added basic types for events (only event names).
## 6.3.1
* Revert the style change to prevent tooltips from being clipped.
- Revert the style change to prevent tooltips from being clipped.
## 6.3.0
* Injected values can now be wrapped in an object so that they can be reactive in Vue 2.
- Injected values can now be wrapped in an object so that they can be reactive in Vue 2.
## 6.2.4
* Fixed that attributes were not outputted onto the chart root element for Vue 2 (#670).
- Fixed that attributes were not outputted onto the chart root element for Vue 2 (#670).
## 6.2.3
* Fixed the problem that `v-on` stops working after upgrading to `vue@2.7.x`.
- Fixed the problem that `v-on` stops working after upgrading to `vue@2.7.x`.
## 6.2.2
* Improve types for `update-options`.
- Improve types for `update-options`.
## 6.2.1
* Improved types for provide/inject API.
- Improved types for provide/inject API.
## 6.2.0
* Added support for Vue 2.7+.
- Added support for Vue 2.7+.
## 6.1.0
* Added support for `.once` event modifier.
- Added support for `.once` event modifier.
## 6.0.3
* Improved typings for Vue 2 version.
- Improved typings for Vue 2 version.
## 6.0.2
* Make `notMerge` option still respect `update-options`.
* The default behavior of `notMerge` now revert to checking if there is a reference change for the `option` prop.
- Make `notMerge` option still respect `update-options`.
- The default behavior of `notMerge` now revert to checking if there is a reference change for the `option` prop.
## 6.0.1
* Update should always be `notMerge: true`.
* Update dependency version for vue-demi.
- Update should always be `notMerge: true`.
- Update dependency version for vue-demi.
## 6.0.0
* Update dependency versions.
- Update dependency versions.
## 6.0.0-rc.6
* Revert the change of `updateOptions.lazyUpdate`. It defaults to `false` again.
* Fixed the occasional error caused by the internal implementation of ECharts.
* Removed unexpected `console.log` call.
- Revert the change of `updateOptions.lazyUpdate`. It defaults to `false` again.
- Fixed the occasional error caused by the internal implementation of ECharts.
- Removed unexpected `console.log` call.
## 6.0.0-rc.5
* Changed `updateOptions.lazyUpdate` to `true` by default. ([#533](https://github.com/ecomfe/vue-echarts/issues/533#issuecomment-809883909))
* Only perform an additional `resize` call after init within a task. ([#533](https://github.com/ecomfe/vue-echarts/issues/533#issuecomment-809883909))
* The `.chart` getter API now works for Vue 2. (#542)
- Changed `updateOptions.lazyUpdate` to `true` by default. ([#533](https://github.com/ecomfe/vue-echarts/issues/533#issuecomment-809883909))
- Only perform an additional `resize` call after init within a task. ([#533](https://github.com/ecomfe/vue-echarts/issues/533#issuecomment-809883909))
- The `.chart` getter API now works for Vue 2. (#542)
## 6.0.0-rc.4
* Fix type error for `Vue2` reference.
- Fix type error for `Vue2` reference.
## 6.0.0-rc.3
* Add missing types file for Vue 2.
- Add missing types file for Vue 2.
## 6.0.0-rc.2
* Fix postinstall script.
- Fix postinstall script.
## 6.0.0-rc.1
* Move inital resize timing earlier into microtasks so that minimize visual layout shift.
* Add a postinstall script to bail out type check for Vue 2 environment.
- Move inital resize timing earlier into microtasks so that minimize visual layout shift.
- Add a postinstall script to bail out type check for Vue 2 environment.
## 6.0.0-beta.7
* Ensure charts fit to container after the next UI render. (#518)
- Ensure charts fit to container after the next UI render. (#518)
## 6.0.0-beta.6
* Ensure VCA is always installed.
- Ensure VCA is always installed.
## 6.0.0-beta.5
* Remove deps for `mergeProps` as it's not yet implemented in `@vue/composition-api`. (#519)
- Remove deps for `mergeProps` as it's not yet implemented in `@vue/composition-api`. (#519)
## 6.0.0-beta.4
* Suppress native events and only handles chart events. (#516)
- Suppress native events and only handles chart events. (#516)
## 6.0.0-beta.3
* Update `vue-demi` version to fix type error.
- Update `vue-demi` version to fix type error.
## 6.0.0-beta.2
* Fix injection keys for UMD bundle.
* Add `vue-demi` to UMD bundle.
- Fix injection keys for UMD bundle.
- Add `vue-demi` to UMD bundle.
## 6.0.0-beta.1
* Use a custom element for the root element to make default style less specific.
- Use a custom element for the root element to make default style less specific.
## 6.0.0-alpha.5
* Fix event support for Vue 2.
- Fix event support for Vue 2.
## 6.0.0-alpha.4
* Add missing injection key exports.
- Add missing injection key exports.
## 6.0.0-alpha.3
* Add missing dependencies for `vue-demi` and `resize-detector`.
- Add missing dependencies for `vue-demi` and `resize-detector`.
## 6.0.0-alpha.2
* Fix bundling for UMD build.
- Fix bundling for UMD build.
## 6.0.0-alpha.1
### Breaking changes
* Update peer dependency for `echarts` to `^5.0.2`.
* Update peer dependency for `vue` to `^2.6.11 || ^3.0.0`.
* Now `@vue/composition-api` is required to be installed to use Vue-ECharts with Vue 2.
* `options` is renamed to **`option`** to align with ECharts itself.
* Updating `option` will respect **`update-options`** configs instead of checking reference change.
* `watch-shallow` is removed. Use **`manual-update`** for performance critical scenarios.
* `mergeOptions` is renamed to **`setOption`** to align with ECharts itself.
* `showLoading` and `hideLoading` is removed. Use the **`loading` and `loading-options`** props instead.
* `appendData` is removed. (Due to ECharts 5's breaking change.)
* All static methods are removed from `vue-echarts`. Use those methods from `echarts` directly.
* Computed getters (`width`, `height`, `isDisposed` and `computedOptions`) are removed. Use the **`getWidth`, `getHeight`, `isDisposed` and `getOption`** methods instead.
* Now the root element of the component have **`100%×100%`** size by default, instead of `600×400`.
- Update peer dependency for `echarts` to `^5.0.2`.
- Update peer dependency for `vue` to `^2.6.11 || ^3.0.0`.
- Now `@vue/composition-api` is required to be installed to use Vue-ECharts with Vue 2.
- `options` is renamed to **`option`** to align with ECharts itself.
- Updating `option` will respect **`update-options`** configs instead of checking reference change.
- `watch-shallow` is removed. Use **`manual-update`** for performance critical scenarios.
- `mergeOptions` is renamed to **`setOption`** to align with ECharts itself.
- `showLoading` and `hideLoading` is removed. Use the **`loading` and `loading-options`** props instead.
- `appendData` is removed. (Due to ECharts 5's breaking change.)
- All static methods are removed from `vue-echarts`. Use those methods from `echarts` directly.
- Computed getters (`width`, `height`, `isDisposed` and `computedOptions`) are removed. Use the **`getWidth`, `getHeight`, `isDisposed` and `getOption`** methods instead.
- Now the root element of the component have **`100%×100%`** size by default, instead of `600×400`.
### New features
* ECharts 5 support.
* Vue 3 support.
* TypeScript support.
* Add new `update-options` prop and support providing default from context.
* Add new `loading` prop and support providing default from context.
* Add new `loading-options` prop and support providing default from context.
* Support providing default from context for the `theme` prop.
- ECharts 5 support.
- Vue 3 support.
- TypeScript support.
- Add new `update-options` prop and support providing default from context.
- Add new `loading` prop and support providing default from context.
- Add new `loading-options` prop and support providing default from context.
- Support providing default from context for the `theme` prop.
## 5.0.0-beta.0
* Update peer dependency for `vue` to `^2.4.0`. **BREAKING**
- Update peer dependency for `vue` to `^2.4.0`. **BREAKING**
## 4.1.0
* Fix the problem that `mergeOptions` didn't use the correct options if the instance is inited on-the-fly.
* Expose ZRender events via `zr:` prefixed events.
* Update to `echarts@4.5.0` (only affects the bundled version).
- Fix the problem that `mergeOptions` didn't use the correct options if the instance is inited on-the-fly.
- Expose ZRender events via `zr:` prefixed events.
- Update to `echarts@4.5.0` (only affects the bundled version).
## 4.0.4
* Update to `echarts@4.3.0` (only affects the bundled version).
- Update to `echarts@4.3.0` (only affects the bundled version).
## 4.0.3
* Update to `resize-detector@0.1.10`.
- Update to `resize-detector@0.1.10`.
## 4.0.2
* Make `manual-update` truely responsive.
- Make `manual-update` truely responsive.
## 4.0.1
* Fix `legendscroll` event.
- Fix `legendscroll` event.
## 4.0.0
* Release 4.0.0.
- Release 4.0.0.
## 4.0.0-beta.1
* Fix autoresize.
- Fix autoresize.
## 4.0.0-beta.0
* Move `echarts` into `peerDependencies`. **BREAKING**
* Rename `auto-resize` to `autoresize`. **BREAKING**
* Point `module` entry to the source version. **BREAKING**
* Switch to Vue CLI 3 for demo.
- Move `echarts` into `peerDependencies`. **BREAKING**
- Rename `auto-resize` to `autoresize`. **BREAKING**
- Point `module` entry to the source version. **BREAKING**
- Switch to Vue CLI 3 for demo.
## 3.1.2
* Fix the problem that `setOption` is always called with `notMerge: true`.
- Fix the problem that `setOption` is always called with `notMerge: true`.
## 3.1.1
* Fix the problem that `options` are not watched as expected.
- Fix the problem that `options` are not watched as expected.
## 3.1.0
* Add `manual-update` prop to handle performance critical scenarios.
* Deprecate `watch-shallow` prop as it was actually not working as expected.
* Fix the computed getters by using `Object.defineProperties` directly instead of Vue's `computed` as it no longer works as expected after Vue 2.0.
* Remove `chart` from `data` to gain a performance boost.
- Add `manual-update` prop to handle performance critical scenarios.
- Deprecate `watch-shallow` prop as it was actually not working as expected.
- Fix the computed getters by using `Object.defineProperties` directly instead of Vue's `computed` as it no longer works as expected after Vue 2.0.
- Remove `chart` from `data` to gain a performance boost.
## 3.0.9
* Update to `resize-detector@0.1.7` to better handle initial resize callback.
- Update to `resize-detector@0.1.7` to better handle initial resize callback.
## 3.0.8
* Add new events and API to adapt the latest version of ECharts.
- Add new events and API to adapt the latest version of ECharts.
## 3.0.7
* Only apply optimization introduce in last version for charts resize from `0` area.
- Only apply optimization introduce in last version for charts resize from `0` area.
## 3.0.6
* Optimize `auto-resize` for initially hidden (`display: none`) charts.
- Optimize `auto-resize` for initially hidden (`display: none`) charts.
## 3.0.5
* Update to `resize-detector@0.1.5`.
- Update to `resize-detector@0.1.5`.
## 3.0.4
* Fix misused `MutationObserver` (#200).
- Fix misused `MutationObserver` (#200).
## 3.0.3
* Update to `resize-detector@0.1.2`.
- Update to `resize-detector@0.1.2`.
## 3.0.2
* Update ECharts to `4.0.2`.
- Update ECharts to `4.0.2`.
## 3.0.1
* Fix npm distribution.
- Fix npm distribution.
## 3.0.0
* Added support for ECharts 4.
* `auto-resize` now listens to element size change instead of window.
* Remove deprecated `chart` prefixed events.
- Added support for ECharts 4.
- `auto-resize` now listens to element size change instead of window.
- Remove deprecated `chart` prefixed events.
## 2.6.0
* Added `watchShallow` prop to manually disable deep watch on `options` to optimize performance for charts with large amout of data.
* Made all props reactive.
* Updated ECharts dependency to `^3.8.5`.
- Added `watchShallow` prop to manually disable deep watch on `options` to optimize performance for charts with large amout of data.
- Made all props reactive.
- Updated ECharts dependency to `^3.8.5`.
## 2.5.1
* Updated ECharts dependency to `3.8.2`+ to fix module breaking change introduced in `3.8.0`.
- Updated ECharts dependency to `3.8.2`+ to fix module breaking change introduced in `3.8.0`.
## 2.5.0
* Fixed collision with Vue's internal methods by removing `_` prefix.
* `mergeOptions` now accept same arguments as ECharts' `setOption` method.
* Updated ECharts dependency to 3.7.2+.
- Fixed collision with Vue's internal methods by removing `_` prefix.
- `mergeOptions` now accept same arguments as ECharts' `setOption` method.
- Updated ECharts dependency to 3.7.2+.
## 2.4.1
* Made `theme` reactive.
* Added `focusnodeadjacency` & `unfocusnodeadjacency` events.
* Fixed the problem that charts won't refresh after `keep-alive` components are activated.
- Made `theme` reactive.
- Added `focusnodeadjacency` & `unfocusnodeadjacency` events.
- Fixed the problem that charts won't refresh after `keep-alive` components are activated.
## 2.4.0
* Add `computedOptions`.
- Add `computedOptions`.
## 2.3.9
* Replace publish npm scripts with shell commands to prevent failure upon npm install.
- Replace publish npm scripts with shell commands to prevent failure upon npm install.
## 2.3.8
* Fixed the problem that styles are missing for precompiled version.
- Fixed the problem that styles are missing for precompiled version.
## 2.3.7
* Switch back to `Vue.util.warn`.
* Switch build tool to rollup.
- Switch back to `Vue.util.warn`.
- Switch build tool to rollup.
## 2.3.6
* Hot fix for last version. Use `console.warn` temporarily.
- Hot fix for last version. Use `console.warn` temporarily.
## 2.3.5
* Mark Vue as an external dependency in webpack config.
- Mark Vue as an external dependency in webpack config.
## 2.3.4
* Use `Vue.util.warn` directly.
- Use `Vue.util.warn` directly.
## 2.3.3
* Fix NPM package.
- Fix NPM package.
## 2.3.2
* Fix the implementation of `disconnect`.
- Fix the implementation of `disconnect`.
## 2.3.1
* Correctly dispose ECharts instance before component is destroyed.
* Fix the problem that `group` is not properly initialized.
- Correctly dispose ECharts instance before component is destroyed.
- Fix the problem that `group` is not properly initialized.
## 2.3.0
* As native events are now not listened by `v-on` in Vue.js 2.0, change mouse events name to original ones (keeping emitting `chart*` events for now).
* Fix getter for `width` / `height` / `isDisposed`.
* `options` is now optional to initialize the component and the chart will be initialized automatically when `options` is set.
- As native events are now not listened by `v-on` in Vue.js 2.0, change mouse events name to original ones (keeping emitting `chart*` events for now).
- Fix getter for `width` / `height` / `isDisposed`.
- `options` is now optional to initialize the component and the chart will be initialized automatically when `options` is set.
## 2.2.0
* Add `auto-resize`.
* Refined demo.
- Add `auto-resize`.
- Refined demo.
## 2.1.0
* Fix `disconnect`.
* When importing `ECharts.vue`, only ECharts core will be imported instead of the whole ECharts bundle.
- Fix `disconnect`.
- When importing `ECharts.vue`, only ECharts core will be imported instead of the whole ECharts bundle.
## 2.0.0
* Update Vue dependency to `2.0.1`.
* Add support for new methods & events for ECharts.
* Fix missing arguments for some APIs.
- Update Vue dependency to `2.0.1`.
- Add support for new methods & events for ECharts.
- Fix missing arguments for some APIs.
## 0.1.2
* Update ECharts version.
* Remove unnecessary files from NPM package.
- Update ECharts version.
- Remove unnecessary files from NPM package.
## 0.1.1
* Fix usage in README.
- Fix usage in README.
## 0.1.0
* First version.
- First version.

317
README.md
View File

@ -6,20 +6,20 @@
---
> Still using v6? Read v6 docs [here →](https://github.com/ecomfe/vue-echarts/tree/6.x)
> Still using Vue 2? Read v7 docs [here →](https://github.com/ecomfe/vue-echarts/tree/7.x)
## Installation & Usage
### npm
```sh
npm add echarts vue-echarts
npm install echarts vue-echarts
```
#### Example
<details>
<summary>Vue 3 <a href="https://stackblitz.com/edit/vue-echarts-vue-3?file=src%2FApp.vue">Demo →</a></summary>
<summary>Vue 3 <a href="https://stackblitz.com/edit/vue-echarts-8?file=src%2FApp.vue">Demo →</a></summary>
```vue
<template>
@ -33,7 +33,7 @@ import { PieChart } from "echarts/charts";
import {
TitleComponent,
TooltipComponent,
LegendComponent
LegendComponent,
} from "echarts/components";
import VChart, { THEME_KEY } from "vue-echarts";
import { ref, provide } from "vue";
@ -43,7 +43,7 @@ use([
PieChart,
TitleComponent,
TooltipComponent,
LegendComponent
LegendComponent,
]);
provide(THEME_KEY, "dark");
@ -51,16 +51,16 @@ provide(THEME_KEY, "dark");
const option = ref({
title: {
text: "Traffic Sources",
left: "center"
left: "center",
},
tooltip: {
trigger: "item",
formatter: "{a} <br/>{b} : {c} ({d}%)"
formatter: "{a} <br/>{b} : {c} ({d}%)",
},
legend: {
orient: "vertical",
left: "left",
data: ["Direct", "Email", "Ad Networks", "Video Ads", "Search Engines"]
data: ["Direct", "Email", "Ad Networks", "Video Ads", "Search Engines"],
},
series: [
{
@ -73,17 +73,17 @@ const option = ref({
{ value: 310, name: "Email" },
{ value: 234, name: "Ad Networks" },
{ value: 135, name: "Video Ads" },
{ value: 1548, name: "Search Engines" }
{ value: 1548, name: "Search Engines" },
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: "rgba(0, 0, 0, 0.5)"
}
}
}
]
shadowColor: "rgba(0, 0, 0, 0.5)",
},
},
},
],
});
</script>
@ -96,100 +96,6 @@ const option = ref({
</details>
<details>
<summary>Vue 2 <a href="https://stackblitz.com/edit/vue-echarts-vue-2?file=src%2FApp.vue">Demo →</a></summary>
```vue
<template>
<v-chart class="chart" :option="option" />
</template>
<script>
import { use } from "echarts/core";
import { CanvasRenderer } from "echarts/renderers";
import { PieChart } from "echarts/charts";
import {
TitleComponent,
TooltipComponent,
LegendComponent
} from "echarts/components";
import VChart, { THEME_KEY } from "vue-echarts";
use([
CanvasRenderer,
PieChart,
TitleComponent,
TooltipComponent,
LegendComponent
]);
export default {
name: "HelloWorld",
components: {
VChart
},
provide: {
[THEME_KEY]: "dark"
},
data() {
return {
option: {
title: {
text: "Traffic Sources",
left: "center"
},
tooltip: {
trigger: "item",
formatter: "{a} <br/>{b} : {c} ({d}%)"
},
legend: {
orient: "vertical",
left: "left",
data: [
"Direct",
"Email",
"Ad Networks",
"Video Ads",
"Search Engines"
]
},
series: [
{
name: "Traffic Sources",
type: "pie",
radius: "55%",
center: ["50%", "60%"],
data: [
{ value: 335, name: "Direct" },
{ value: 310, name: "Email" },
{ value: 234, name: "Ad Networks" },
{ value: 135, name: "Video Ads" },
{ value: 1548, name: "Search Engines" }
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: "rgba(0, 0, 0, 0.5)"
}
}
}
]
}
};
}
};
</script>
<style scoped>
.chart {
height: 400px;
}
</style>
```
</details>
> [!IMPORTANT]
> We encourage manually importing components and charts from ECharts for smaller bundle size. We've built an [import code generator](https://vue-echarts.dev/#codegen) to help you with that. You can just paste in your `option` code and we'll generate the precise import code for you.
>
@ -208,14 +114,16 @@ import "echarts";
Drop `<script>` inside your HTML file and access the component via `window.VueECharts`.
<details>
<summary>Vue 3 <a href="https://stackblitz.com/edit/vue-echarts-vue-3-global?file=index.html">Demo →</a></summary>
<summary>Vue 3 <a href="https://stackblitz.com/edit/vue-echarts-8-global?file=index.html">Demo →</a></summary>
<!-- vue3Scripts:start -->
```html
<script src="https://cdn.jsdelivr.net/npm/vue@3.4.33"></script>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.5.1"></script>
<script src="https://cdn.jsdelivr.net/npm/vue-echarts@7.0.3"></script>
<script src="https://cdn.jsdelivr.net/npm/echarts@6.0.0"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@3.5.18"></script>
<script src="https://cdn.jsdelivr.net/npm/vue-echarts@8.0.0-beta.1"></script>
```
<!-- vue3Scripts:end -->
```js
@ -227,25 +135,7 @@ app.component('v-chart', VueECharts)
</details>
<details>
<summary>Vue 2 <a href="https://stackblitz.com/edit/vue-echarts-vue-2-global?file=index.html">Demo →</a></summary>
<!-- vue2Scripts:start -->
```html
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.16"></script>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.5.1"></script>
<script src="https://cdn.jsdelivr.net/npm/vue-echarts@7.0.3"></script>
```
<!-- vue2Scripts:end -->
```js
// register globally (or you can do it locally)
Vue.component("v-chart", VueECharts);
```
</details>
See more examples [here](https://github.com/ecomfe/vue-echarts/tree/main/src/demo).
See more examples [here](https://github.com/ecomfe/vue-echarts/tree/main/demo).
### Props
@ -265,7 +155,8 @@ See more examples [here](https://github.com/ecomfe/vue-echarts/tree/main/src/dem
ECharts' universal interface. Modifying this prop will trigger ECharts' `setOption` method. Read more [here →](https://echarts.apache.org/en/option.html)
> 💡 When `update-options` is not specified, `notMerge: false` will be specified by default when the `setOption` method is called if the `option` object is modified directly and the reference remains unchanged; otherwise, if a new reference is bound to `option`, ` notMerge: true` will be specified.
> [!TIP]
> When `update-options` is not specified, `notMerge: false` will be specified by default when the `setOption` method is called if the `option` object is modified directly and the reference remains unchanged; otherwise, if a new reference is bound to `option`, `notMerge: true` will be specified.
- `update-options: object`
@ -305,8 +196,7 @@ You can bind events with Vue's `v-on` directive.
</template>
```
> **Note**
>
> [!NOTE]
> Only the `.once` event modifier is supported as other modifiers are tightly coupled with the DOM event system.
Vue-ECharts support the following events:
@ -359,7 +249,7 @@ See supported events [here →](https://echarts.apache.org/en/api.html#events)
#### Native DOM Events
As Vue-ECharts binds events to the ECharts instance by default, there is some caveat when using native DOM events. You need to prefix the event name with `native:` to bind native DOM events (or you can use the `.native` modifier in Vue 2, which is dropped in Vue 3).
As Vue-ECharts binds events to the ECharts instance by default, there is some caveat when using native DOM events. You need to prefix the event name with `native:` to bind native DOM events.
```vue
<template>
@ -369,61 +259,56 @@ As Vue-ECharts binds events to the ECharts instance by default, there is some ca
### Provide / Inject
Vue-ECharts provides provide/inject API for `theme`, `init-options`, `update-options` and `loading-options` to help configuring contextual options. eg. for `init-options` you can use the provide API like this:
Vue-ECharts provides provide/inject API for `theme`, `init-options`, `update-options` and `loading-options` to help configuring contextual options. eg. for `theme` you can use the provide API like this:
<details>
<summary>Vue 3</summary>
<summary>Composition API</summary>
```js
import { THEME_KEY } from 'vue-echarts'
import { provide } from 'vue'
import { THEME_KEY } from "vue-echarts";
import { provide } from "vue";
// composition API
provide(THEME_KEY, 'dark')
provide(THEME_KEY, "dark");
// options API
{
provide: {
[THEME_KEY]: 'dark'
}
}
// or provide a ref
const theme = ref("dark");
provide(THEME_KEY, theme);
// getter is also supported
provide(THEME_KEY, () => theme.value);
```
</details>
<details>
<summary>Vue 2</summary>
<summary>Options API</summary>
```js
import { THEME_KEY } from 'vue-echarts'
import { computed } from 'vue'
// in component options
export default {
{
provide: {
[THEME_KEY]: 'dark'
}
}
```
}
> **Note**
>
> You need to provide an object for Vue 2 if you want to change it dynamically.
>
> ```js
> // in component options
> {
> data () {
> return {
> theme: { value: 'dark' }
> }
> },
> provide () {
> return {
> [THEME_KEY]: this.theme
> }
> }
> }
> ```
// Or make injections reactive
export default {
data() {
return {
theme: 'dark'
}
},
provide() {
return {
[THEME_KEY]: computed(() => this.theme)
}
}
}
```
</details>
@ -445,7 +330,80 @@ import { THEME_KEY } from 'vue-echarts'
- `dispose` [](https://echarts.apache.org/en/api.html#echartsInstance.dispose)
> [!NOTE]
> [`showLoading`](https://echarts.apache.org/en/api.html#echartsInstance.showLoading) and [`hideLoading`](https://echarts.apache.org/en/api.html#echartsInstance.hideLoading) are not exposed. Use the **`loading`** and **`loading-options`** props.
> The following ECharts instance methods aren't exposed because their functionality is already provided by component [props](#props):
>
> - [`showLoading`](https://echarts.apache.org/en/api.html#echartsInstance.showLoading) / [`hideLoading`](https://echarts.apache.org/en/api.html#echartsInstance.hideLoading): use the `loading` and `loading-options` props instead.
> - `setTheme`: use the `theme` prop instead.
### Slots
Vue-ECharts allows you to define ECharts option's [`tooltip.formatter`](https://echarts.apache.org/en/option.html#tooltip.formatter) and [`toolbox.feature.dataView.optionToContent`](https://echarts.apache.org/en/option.html#toolbox.feature.dataView.optionToContent) callbacks via Vue slots instead of defining them in your `option` object. This simplifies custom HTMLElement rendering using familiar Vue templating.
**Slot Naming Convention**
- Slot names begin with `tooltip`/`dataView`, followed by hyphen-separated path segments to the target.
- Each segment corresponds to an `option` property name or an array index (for arrays, use the numeric index).
- The constructed slot name maps directly to the nested callback it overrides.
**Example mappings**:
- `tooltip``option.tooltip.formatter`
- `tooltip-baseOption``option.baseOption.tooltip.formatter`
- `tooltip-xAxis-1``option.xAxis[1].tooltip.formatter`
- `tooltip-series-2-data-4``option.series[2].data[4].tooltip.formatter`
- `dataView``option.toolbox.feature.dataView.optionToContent`
- `dataView-media-1-option``option.media[1].option.toolbox.feature.dataView.optionToContent`
The slot props correspond to the first parameter of the callback function.
<details>
<summary>Usage</summary>
```vue
<template>
<v-chart :option="chartOptions">
<!-- Global `tooltip.formatter` -->
<template #tooltip="params">
<div v-for="(param, i) in params" :key="i">
<span v-html="param.marker" />
<span>{{ param.seriesName }}</span>
<span>{{ param.value[0] }}</span>
</div>
</template>
<!-- Tooltip on xAxis -->
<template #tooltip-xAxis="params">
<div>X-Axis : {{ params.value }}</div>
</template>
<!-- Data View Content -->
<template #dataView="option">
<table>
<thead>
<tr>
<th v-for="(t, i) in option.dataset[0].source[0]" :key="i">
{{ t }}
</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, i) in option.dataset[0].source.slice(1)" :key="i">
<th>{{ row[0] }}</th>
<td v-for="(v, i) in row.slice(1)" :key="i">{{ v }}</td>
</tr>
</tbody>
</table>
</template>
</v-chart>
</template>
```
[Example →](https://vue-echarts.dev/#line)
</details>
> [!NOTE]
> Slots take precedence over the corresponding callback defined in `props.option`.
### Static Methods
@ -453,20 +411,29 @@ Static methods can be accessed from [`echarts` itself](https://echarts.apache.or
## CSP: `style-src` or `style-src-elem`
If you are applying a CSP to prevent inline `<style>` injection, you need to use `vue-echarts/csp` instead of `vue-echarts` and include `vue-echarts/csp/style.css` manually.
If you are **both** enforcing a strict CSP that prevents inline `<style>` injection and targeting browsers that don't support the [CSSStyleSheet() constructor](https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet/CSSStyleSheet#browser_compatibility), you need to manually include `vue-echarts/style.css`.
## Migration to v7
## Migration to v8
Read the breaking changes document in the [release log](https://github.com/ecomfe/vue-echarts/releases/tag/v7.0.0-beta.0) and the migration shoud be straightforward.
> [!NOTE]
> Please make sure to read the [upgrade guide](https://echarts.apache.org/handbook/en/basics/release-note/v6-upgrade-guide/) for ECharts 6 as well.
The following breaking changes are introduced in `vue-echarts@8`:
- **Vue 2 support is dropped:** If you still need to stay on Vue 2, use [`vue-echarts@7`](https://github.com/ecomfe/vue-echarts/tree/7.x).
- **Browser compatibility changes:** We no longer provide compatibility for browsers without native [`class`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes#browser_compatibility) support. If you need to support legacy browsers, you must transpile the code to ES5 yourself.
- **CSP entry point removed:** The entry point `vue-echarts/csp` is removed. Use `vue-echarts` instead. You only need to manually include `vue-echarts/style.css` if you are **both** enforcing a strict CSP that prevents inline `<style>` injection and targeting browsers that don't support the [`CSSStyleSheet()` constructor](https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet/CSSStyleSheet#browser_compatibility).
## Local development
```sh
pnpm i
pnpm serve
pnpm dev
```
Open `http://localhost:8080` to see the demo.
Open `http://localhost:5173` to see the demo.
## Notice

View File

@ -6,20 +6,20 @@
---
> 还在使用 v6?可以继续阅读老版本的文档。[前往 →](https://github.com/ecomfe/vue-echarts/blob/6.x/README.zh-Hans.md)
> 还在使用 Vue 2?可以继续阅读老版本的文档。[前往 →](https://github.com/ecomfe/vue-echarts/blob/7.x/README.zh-Hans.md)
## 安装 & 使用
### npm
```sh
npm add echarts vue-echarts
npm install echarts vue-echarts
```
#### 示例
<details>
<summary>Vue 3 <a href="https://stackblitz.com/edit/vue-echarts-vue-3?file=src%2FApp.vue">Demo →</a></summary>
<summary>Vue 3 <a href="https://stackblitz.com/edit/vue-echarts-8?file=src%2FApp.vue">Demo →</a></summary>
```vue
<template>
@ -33,7 +33,7 @@ import { PieChart } from "echarts/charts";
import {
TitleComponent,
TooltipComponent,
LegendComponent
LegendComponent,
} from "echarts/components";
import VChart, { THEME_KEY } from "vue-echarts";
import { ref, provide } from "vue";
@ -43,7 +43,7 @@ use([
PieChart,
TitleComponent,
TooltipComponent,
LegendComponent
LegendComponent,
]);
provide(THEME_KEY, "dark");
@ -51,16 +51,16 @@ provide(THEME_KEY, "dark");
const option = ref({
title: {
text: "Traffic Sources",
left: "center"
left: "center",
},
tooltip: {
trigger: "item",
formatter: "{a} <br/>{b} : {c} ({d}%)"
formatter: "{a} <br/>{b} : {c} ({d}%)",
},
legend: {
orient: "vertical",
left: "left",
data: ["Direct", "Email", "Ad Networks", "Video Ads", "Search Engines"]
data: ["Direct", "Email", "Ad Networks", "Video Ads", "Search Engines"],
},
series: [
{
@ -73,17 +73,17 @@ const option = ref({
{ value: 310, name: "Email" },
{ value: 234, name: "Ad Networks" },
{ value: 135, name: "Video Ads" },
{ value: 1548, name: "Search Engines" }
{ value: 1548, name: "Search Engines" },
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: "rgba(0, 0, 0, 0.5)"
}
}
}
]
shadowColor: "rgba(0, 0, 0, 0.5)",
},
},
},
],
});
</script>
@ -96,100 +96,6 @@ const option = ref({
</details>
<details>
<summary>Vue 2 <a href="https://stackblitz.com/edit/vue-echarts-vue-2?file=src%2FApp.vue">Demo →</a></summary>
```vue
<template>
<v-chart class="chart" :option="option" />
</template>
<script>
import { use } from "echarts/core";
import { CanvasRenderer } from "echarts/renderers";
import { PieChart } from "echarts/charts";
import {
TitleComponent,
TooltipComponent,
LegendComponent
} from "echarts/components";
import VChart, { THEME_KEY } from "vue-echarts";
use([
CanvasRenderer,
PieChart,
TitleComponent,
TooltipComponent,
LegendComponent
]);
export default {
name: "HelloWorld",
components: {
VChart
},
provide: {
[THEME_KEY]: "dark"
},
data() {
return {
option: {
title: {
text: "Traffic Sources",
left: "center"
},
tooltip: {
trigger: "item",
formatter: "{a} <br/>{b} : {c} ({d}%)"
},
legend: {
orient: "vertical",
left: "left",
data: [
"Direct",
"Email",
"Ad Networks",
"Video Ads",
"Search Engines"
]
},
series: [
{
name: "Traffic Sources",
type: "pie",
radius: "55%",
center: ["50%", "60%"],
data: [
{ value: 335, name: "Direct" },
{ value: 310, name: "Email" },
{ value: 234, name: "Ad Networks" },
{ value: 135, name: "Video Ads" },
{ value: 1548, name: "Search Engines" }
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: "rgba(0, 0, 0, 0.5)"
}
}
}
]
}
};
}
};
</script>
<style scoped>
.chart {
height: 400px;
}
</style>
```
</details>
> [!IMPORTANT]
> 我们鼓励手动从 ECharts 中引入组件和图表,以减小打包体积。我们已经为此构建了一个[导入代码生成器](https://vue-echarts.dev/#codegen)。你只需要把`option` 代码粘贴进去,就可以得到精确的导入代码。
>
@ -208,14 +114,16 @@ import "echarts";
用如下方式在 HTML 中插入 `<script>` 标签,并且通过 `window.VueECharts` 来访问组件接口:
<details>
<summary>Vue 3 <a href="https://stackblitz.com/edit/vue-echarts-vue-3-global?file=index.html">Demo →</a></summary>
<summary>Vue 3 <a href="https://stackblitz.com/edit/vue-echarts-8-global?file=index.html">Demo →</a></summary>
<!-- vue3Scripts:start -->
```html
<script src="https://cdn.jsdelivr.net/npm/vue@3.4.33"></script>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.5.1"></script>
<script src="https://cdn.jsdelivr.net/npm/vue-echarts@7.0.3"></script>
<script src="https://cdn.jsdelivr.net/npm/echarts@6.0.0"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@3.5.18"></script>
<script src="https://cdn.jsdelivr.net/npm/vue-echarts@8.0.0-beta.1"></script>
```
<!-- vue3Scripts:end -->
```js
@ -227,25 +135,7 @@ app.component('v-chart', VueECharts)
</details>
<details>
<summary>Vue 2 <a href="https://stackblitz.com/edit/vue-echarts-vue-2-global?file=index.html">Demo →</a></summary>
<!-- vue2Scripts:start -->
```html
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.16"></script>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.5.1"></script>
<script src="https://cdn.jsdelivr.net/npm/vue-echarts@7.0.3"></script>
```
<!-- vue2Scripts:end -->
```js
// 全局注册组件(也可以使用局部注册)
Vue.component("v-chart", VueECharts);
```
</details>
可以在[这里](https://github.com/ecomfe/vue-echarts/tree/main/src/demo)查看更多例子。
可以在[这里](https://github.com/ecomfe/vue-echarts/tree/main/demo)查看更多例子。
### Prop
@ -265,7 +155,8 @@ Vue.component("v-chart", VueECharts);
ECharts 的万能接口。修改这个 prop 会触发 ECharts 实例的 `setOption` 方法。查看[详情 →](https://echarts.apache.org/zh/option.html)
> 💡 在没有指定 `update-options` 时,如果直接修改 `option` 对象而引用保持不变,`setOption` 方法调用时将默认指定 `notMerge: false`;否则,如果为 `option` 绑定一个新的引用,将指定 `notMerge: true`。
> [!TIP]
> 在没有指定 `update-options` 时,如果直接修改 `option` 对象而引用保持不变,`setOption` 方法调用时将默认指定 `notMerge: false`;否则,如果为 `option` 绑定一个新的引用,将指定 `notMerge: true`。
- `update-options: object`
@ -305,8 +196,7 @@ Vue.component("v-chart", VueECharts);
</template>
```
> **Note**
>
> [!NOTE]
> 仅支持 `.once` 修饰符,因为其它修饰符都与 DOM 事件机制强耦合。
Vue-ECharts 支持如下事件:
@ -359,7 +249,7 @@ Vue-ECharts 支持如下事件:
#### 原生 DOM 事件
由于 Vue-ECharts 默认将事件绑定到 ECharts 实例,因此在使用原生 DOM 事件时需要做一些特殊处理。你需要在事件名称前加上 `native:` 前缀来绑定原生 DOM 事件(在 Vue 2 中也可以使用 `.native` 修饰符,但这在 Vue 3 中已被废弃)
由于 Vue-ECharts 默认将事件绑定到 ECharts 实例,因此在使用原生 DOM 事件时需要做一些特殊处理。你需要在事件名称前加上 `native:` 前缀来绑定原生 DOM 事件。
```vue
<template>
@ -369,61 +259,56 @@ Vue-ECharts 支持如下事件:
### Provide / Inject
Vue-ECharts 为 `theme``init-options``update-options``loading-options` 提供了 provide/inject API以通过上下文配置选项。例如可以通过如下方式来使用 provide API 为 `init-options` 提供上下文配置:
Vue-ECharts 为 `theme``init-options``update-options``loading-options` 提供了 provide/inject API以通过上下文配置选项。例如可以通过如下方式来使用 provide API 为 `theme` 提供上下文配置:
<details>
<summary>Vue 3</summary>
<summary>组合式 API</summary>
```js
import { THEME_KEY } from 'vue-echarts'
import { provide } from 'vue'
import { THEME_KEY } from "vue-echarts";
import { provide } from "vue";
// 组合式 API
provide(THEME_KEY, 'dark')
provide(THEME_KEY, "dark");
// 选项式 API
{
provide: {
[THEME_KEY]: 'dark'
}
}
// 或者 provide 一个 ref
const theme = ref("dark");
provide(THEME_KEY, theme);
// 也支持 getter
provide(THEME_KEY, () => theme.value);
```
</details>
<details>
<summary>Vue 2</summary>
<summary>选项式 API</summary>
```js
import { THEME_KEY } from 'vue-echarts'
import { computed } from 'vue'
// 组件选项中
export default {
{
provide: {
[THEME_KEY]: 'dark'
}
}
```
}
> **Note**
>
> 在 Vue 2 中,如果你想动态地改变这些选项,那么你需要提供一个对象。
>
> ```js
> // 组件选项中
> {
> data () {
> return {
> theme: { value: 'dark' }
> }
> },
> provide () {
> return {
> [THEME_KEY]: this.theme
> }
> }
> }
> ```
// 或者让注入项具有响应性
export default {
data() {
return {
theme: 'dark'
}
},
provide() {
return {
[THEME_KEY]: computed(() => this.theme)
}
}
}
```
</details>
@ -445,30 +330,110 @@ import { THEME_KEY } from 'vue-echarts'
- `dispose` [](https://echarts.apache.org/zh/api.html#echartsInstance.dispose)
> [!NOTE]
> [`showLoading`](https://echarts.apache.org/zh/api.html#echartsInstance.showLoading) 和 [`hideLoading`](https://echarts.apache.org/zh/api.html#echartsInstance.hideLoading) 未被暴露,请使用 **`loading`** 和 **`loading-options`** prop。
> 如下 ECharts 实例方法没有被暴露,因为它们的功能已经通过组件 [props](#props) 提供了:
>
> - [`showLoading`](https://echarts.apache.org/zh/api.html#echartsInstance.showLoading) / [`hideLoading`](https://echarts.apache.org/zh/api.html#echartsInstance.hideLoading):请使用 `loading` 和 `loading-options` prop。
> - `setTheme`:请使用 `theme` prop。
### 插槽Slots
Vue-ECharts 允许你通过 Vue 插槽来定义 ECharts 配置中的 [`tooltip.formatter`](https://echarts.apache.org/zh/option.html#tooltip.formatter) 和 [`toolbox.feature.dataView.optionToContent`](https://echarts.apache.org/zh/option.html#toolbox.feature.dataView.optionToContent) 回调,而无需在 `option` 对象中定义它们。你可以使用熟悉的 Vue 模板语法来编写自定义提示框或数据视图中的内容。
**插槽命名约定**
- 插槽名称以 `tooltip`/`dataView` 开头,后面跟随用连字符分隔的路径片段,用于定位目标。
- 每个路径片段对应 `option` 对象的属性名或数组索引(数组索引使用数字形式)。
- 拼接后的插槽名称直接映射到要覆盖的嵌套回调函数。
**示例映射**
- `tooltip``option.tooltip.formatter`
- `tooltip-baseOption``option.baseOption.tooltip.formatter`
- `tooltip-xAxis-1``option.xAxis[1].tooltip.formatter`
- `tooltip-series-2-data-4``option.series[2].data[4].tooltip.formatter`
- `dataView``option.toolbox.feature.dataView.optionToContent`
- `dataView-media-1-option``option.media[1].option.toolbox.feature.dataView.optionToContent`
插槽的 props 对象对应回调函数的第一个参数。
<details>
<summary>用法示例</summary>
```vue
<template>
<v-chart :option="chartOptions">
<!-- 全局 `tooltip.formatter` -->
<template #tooltip="params">
<div v-for="(param, i) in params" :key="i">
<span v-html="param.marker" />
<span>{{ param.seriesName }}</span>
<span>{{ param.value[0] }}</span>
</div>
</template>
<!-- x轴 tooltip -->
<template #tooltip-xAxis="params">
<div>X轴: {{ params.value }}</div>
</template>
<!-- 数据视图内容 -->
<template #dataView="option">
<table>
<thead>
<tr>
<th v-for="(t, i) in option.dataset[0].source[0]" :key="i">
{{ t }}
</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, i) in option.dataset[0].source.slice(1)" :key="i">
<th>{{ row[0] }}</th>
<td v-for="(v, i) in row.slice(1)" :key="i">{{ v }}</td>
</tr>
</tbody>
</table>
</template>
</v-chart>
</template>
```
[示例 →](https://vue-echarts.dev/#line)
</details>
> [!NOTE]
> 插槽会优先于 `props.option` 中对应的回调函数。
### 静态方法
静态方法请直接通过 [`echarts` 本身](https://echarts.apache.org/zh/api.html#echarts)进行调用。
## CSP: `style-src` 或 `style-src-elem`
如果你正在应用 CSP 来防止内联 `<style>` 注入,则需要使用 `vue-echarts/csp` 代替 `vue-echarts`,并手动引入 `vue-echarts/csp/style.css`
如果你执行严格的 CSP 策略来防止内联 `<style>` 注入,**并且**需要兼容不支持 [CSSStyleSheet() 构造函数](https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet/CSSStyleSheet#browser_compatibility) 的浏览器,则需要手动引入 `vue-echarts/style.css`
## 迁移到 v7
## 迁移到 v8
请阅读[发布日志](https://github.com/ecomfe/vue-echarts/releases/tag/v7.0.0-beta.0)中的变更记录,之后迁移过程应该会相对简单。
> [!NOTE]
> 请确保同时查阅 [ECharts 6 的升级指南](https://echarts.apache.org/handbook/zh/basics/release-note/v6-upgrade-guide/)。
`vue-echarts@8` 引入了以下破坏性变更:
- **Vue 2 支持已移除:** 如果你仍需要继续使用 Vue 2请使用 [`vue-echarts@7`](https://github.com/ecomfe/vue-echarts/tree/7.x)。
- **浏览器兼容性变更:** 我们不再为不支持原生 [`class`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes#browser_compatibility) 的浏览器提供兼容性支持。如果你需要支持旧版浏览器,必须自行将代码转译为 ES5。
- **CSP 入口点已移除:** 入口点 `vue-echarts/csp` 已被移除。请使用 `vue-echarts` 替代。如果你执行严格的 CSP 策略来防止内联 `<style>` 注入,**并且**需要兼容不支持 [`CSSStyleSheet()` 构造函数](https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet/CSSStyleSheet#browser_compatibility) 的浏览器,则需要手动引入 `vue-echarts/style.css`
## 本地开发
```sh
pnpm i
pnpm serve
pnpm dev
```
打开 `http://localhost:8080` 来查看 demo。
打开 `http://localhost:5173` 来查看 demo。
## 声明

View File

@ -1,3 +0,0 @@
{
"presets": ["@vue/cli-plugin-babel/preset"]
}

View File

@ -4,10 +4,8 @@ import {
computed,
watch,
onBeforeUnmount,
defineProps,
defineEmits,
onMounted,
nextTick
nextTick,
} from "vue";
import { useLocalStorage } from "@vueuse/core";
import "highlight.js/styles/github.css";
@ -16,7 +14,7 @@ import javascript from "highlight.js/lib/languages/javascript";
import typescript from "highlight.js/lib/languages/typescript";
import hljsVuePlugin from "@highlightjs/vue-plugin";
import { initialize, transform } from "esbuild-wasm";
import wasmURL from "esbuild-wasm/esbuild.wasm";
import wasmURL from "esbuild-wasm/esbuild.wasm?url";
import { track } from "@vercel/analytics";
import { getImportsFromOption } from "./utils/codegen";
@ -31,7 +29,7 @@ const codegenOptions = useLocalStorage("ve.codegenOptions", {
multiline: false,
maxLen: 80,
semi: false,
includeType: false
includeType: false,
});
const props = defineProps({ open: Boolean, renderer: String });
@ -40,6 +38,10 @@ const emit = defineEmits(["update:open"]);
const dialog = ref(null);
let clickFrom = null;
function onMousedown(e) {
clickFrom = e.target;
}
function closeFromOutside() {
if (dialog.value?.contains(clickFrom)) {
return;
@ -55,7 +57,7 @@ const renderer = ref(props.renderer);
const source = ref(null);
watch(
() => props.open,
async val => {
async (val) => {
if (val) {
renderer.value = props.renderer;
}
@ -66,7 +68,7 @@ watch(
return;
}
source.value?.focus();
}
},
);
const copied = ref(false);
@ -76,7 +78,11 @@ const transformedCode = ref("");
const transformErrors = ref([]);
onMounted(async () => {
// prevent multiple initializations during HMR
if (!window.__esbuildInitialized) {
await initialize({ wasmURL });
window.__esbuildInitialized = true;
}
initializing.value = false;
@ -121,7 +127,7 @@ onMounted(async () => {
source.value?.focus();
});
watch(optionCode, async val => {
watch(optionCode, async (val) => {
try {
transformedCode.value = await transform(`(${val})`, { loader: "ts" });
transformErrors.value = [];
@ -162,7 +168,7 @@ const importCode = computed(() => {
try {
return getImportsFromOption(eval(transformedCode.value.code), {
renderer: renderer.value,
...codegenOptions.value
...codegenOptions.value,
});
} catch (e) {
return `/* Invalid ECharts option */
@ -211,7 +217,7 @@ onBeforeUnmount(() => {
<aside
class="modal"
:class="{ open: props.open }"
@mousedown="clickFrom = $event.target"
@mousedown="onMousedown"
@click="closeFromOutside"
@keydown.esc="close"
>

View File

@ -3,27 +3,28 @@ import { provide, computed, ref, watch } from "vue";
import { useUrlSearchParams } from "@vueuse/core";
import { use } from "echarts/core";
import { CanvasRenderer, SVGRenderer } from "echarts/renderers";
import { INIT_OPTIONS_KEY } from "../ECharts";
import { INIT_OPTIONS_KEY } from "../src/ECharts";
import { track } from "@vercel/analytics";
import LogoChart from "./examples/LogoChart";
import BarChart from "./examples/BarChart";
import PieChart from "./examples/PieChart";
import PolarChart from "./examples/PolarChart";
import ScatterChart from "./examples/ScatterChart";
import GeoChart from "./examples/GeoChart";
import RadarChart from "./examples/RadarChart";
import ConnectChart from "./examples/ConnectChart";
import GlChart from "./examples/GlChart";
import ManualChart from "./examples/ManualChart";
import LogoChart from "./examples/LogoChart.vue";
import BarChart from "./examples/BarChart.vue";
import LineChart from "./examples/LineChart.vue";
import PieChart from "./examples/PieChart.vue";
import PolarChart from "./examples/PolarChart.vue";
import ScatterChart from "./examples/ScatterChart.vue";
import GeoChart from "./examples/GeoChart.vue";
import RadarChart from "./examples/RadarChart.vue";
import ConnectChart from "./examples/ConnectChart.vue";
import GlChart from "./examples/GlChart.vue";
import ManualChart from "./examples/ManualChart.vue";
import CodeGen from "./CodeGen";
import CodeGen from "./CodeGen.vue";
use([CanvasRenderer, SVGRenderer]);
const params = useUrlSearchParams();
const initOptions = computed(() => ({
renderer: params.renderer || "canvas"
renderer: params.renderer || "canvas",
}));
provide(INIT_OPTIONS_KEY, initOptions);
@ -39,7 +40,7 @@ function openCodegen() {
track("codegen", { from: "click" });
}
watch(codeOpen, open => {
watch(codeOpen, (open) => {
if (open) {
location.hash = "#codegen";
} else {
@ -74,6 +75,7 @@ watch(codeOpen, open => {
</p>
<bar-chart />
<line-chart />
<pie-chart />
<polar-chart />
<scatter-chart />
@ -94,7 +96,7 @@ watch(codeOpen, open => {
<aside class="renderer">
<button
:class="{
active: initOptions.renderer === 'canvas'
active: initOptions.renderer === 'canvas',
}"
@click="params.renderer = 'canvas'"
>
@ -102,7 +104,7 @@ watch(codeOpen, open => {
</button>
<button
:class="{
active: initOptions.renderer === 'svg'
active: initOptions.renderer === 'svg',
}"
@click="params.renderer = 'svg'"
>
@ -149,13 +151,6 @@ a {
}
}
h1,
h2,
h3 {
color: #2c3e50;
font-weight: 400;
}
h1 {
margin-bottom: 1em;
font-family: Inter, "Helvetica Neue", Arial, sans-serif;
@ -237,8 +232,9 @@ pre {
pre,
code,
textarea {
font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
"Liberation Mono", monospace;
font-family:
ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono",
monospace;
}
footer {
@ -257,6 +253,8 @@ footer {
h1,
h2,
h3 {
color: #2c3e50;
font-weight: 400;
a,
a:hover {
text-decoration: none;

View File

Before

Width:  |  Height:  |  Size: 1011 B

After

Width:  |  Height:  |  Size: 1008 B

View File

Before

Width:  |  Height:  |  Size: 901 KiB

After

Width:  |  Height:  |  Size: 901 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

@ -6,7 +6,7 @@ export default function getData() {
return {
textStyle: {
fontFamily: 'Inter, "Helvetica Neue", Arial, sans-serif',
fontWeight: 300
fontWeight: 300,
},
dataset: {
dimensions: ["Product", "2015", "2016", "2017"],
@ -15,32 +15,32 @@ export default function getData() {
Product: "Matcha Latte",
2015: random(),
2016: random(),
2017: random()
2017: random(),
},
{
Product: "Milk Tea",
2015: random(),
2016: random(),
2017: random()
2017: random(),
},
{
Product: "Cheese Cocoa",
2015: random(),
2016: random(),
2017: random()
2017: random(),
},
{
Product: "Walnut Brownie",
2015: random(),
2016: random(),
2017: random()
}
]
2017: random(),
},
],
},
xAxis: { type: "category" },
yAxis: {},
// Declare several bar series, each will be mapped
// to a column of dataset.source by default.
series: [{ type: "bar" }, { type: "bar" }, { type: "bar" }]
series: [{ type: "bar" }, { type: "bar" }, { type: "bar" }],
};
}

View File

@ -5,38 +5,38 @@ for (let i = 0; i < 16; i++) {
Math.random() * 5,
Math.random() * 4,
Math.random() * 12,
Math.round(Math.random() * (symbolCount - 1))
Math.round(Math.random() * (symbolCount - 1)),
]);
}
const c1 = {
textStyle: {
fontFamily: 'Inter, "Helvetica Neue", Arial, sans-serif',
fontWeight: 300
fontWeight: 300,
},
legend: {
top: "3%",
data: ["scatter"]
data: ["scatter"],
},
tooltip: {
formatter: "{c}"
formatter: "{c}",
},
grid: {
top: "30%",
right: "18%",
bottom: "20%"
bottom: "20%",
},
xAxis: {
type: "value",
splitLine: {
show: false
}
show: false,
},
},
yAxis: {
type: "value",
splitLine: {
show: false
}
show: false,
},
},
visualMap: [
{
@ -50,47 +50,47 @@ const c1 = {
max: 18,
precision: 0,
splitNumber: 0,
calculable: true
}
calculable: true,
},
],
series: [
{
name: "scatter",
type: "scatter",
symbolSize: 30,
data: data1
}
]
data: data1,
},
],
};
const c2 = {
textStyle: {
fontFamily: 'Inter, "Helvetica Neue", Arial, sans-serif',
fontWeight: 300
fontWeight: 300,
},
legend: {
top: "3%",
data: ["scatter"]
data: ["scatter"],
},
tooltip: {
formatter: "{c}"
formatter: "{c}",
},
grid: {
top: "30%",
right: "18%",
bottom: "20%"
bottom: "20%",
},
xAxis: {
type: "value",
splitLine: {
show: false
}
show: false,
},
},
yAxis: {
type: "value",
splitLine: {
show: false
}
show: false,
},
},
visualMap: [
{
@ -103,17 +103,17 @@ const c2 = {
max: 18,
precision: 0,
splitNumber: 0,
calculable: true
}
calculable: true,
},
],
series: [
{
name: "scatter",
type: "scatter",
symbolSize: 30,
data: data1
}
]
data: data1,
},
],
};
export default function getData() {

56
demo/data/line.js Normal file
View File

@ -0,0 +1,56 @@
export default function getData() {
return {
textStyle: {
fontFamily: 'Inter, "Helvetica Neue", Arial, sans-serif',
fontWeight: 300,
},
legend: { top: 20 },
tooltip: {
trigger: "axis",
},
dataset: {
source: [
["product", "2012", "2013", "2014", "2015", "2016", "2017"],
["Milk Tea", 56.5, 82.1, 88.7, 70.1, 53.4, 85.1],
["Matcha Latte", 51.1, 51.4, 55.1, 53.3, 73.8, 68.7],
["Cheese Cocoa", 40.1, 62.2, 69.5, 36.4, 45.2, 32.5],
["Walnut Brownie", 25.2, 37.1, 41.2, 18, 33.9, 49.1],
],
},
xAxis: {
type: "category",
triggerEvent: true,
tooltip: { show: true, formatter: "" },
},
yAxis: {
triggerEvent: true,
tooltip: { show: true, formatter: "" },
},
series: [
{
type: "line",
smooth: true,
seriesLayoutBy: "row",
emphasis: { focus: "series" },
},
{
type: "line",
smooth: true,
seriesLayoutBy: "row",
emphasis: { focus: "series" },
},
{
type: "line",
smooth: true,
seriesLayoutBy: "row",
emphasis: { focus: "series" },
},
{
type: "line",
smooth: true,
seriesLayoutBy: "row",
emphasis: { focus: "series" },
},
],
};
}

View File

@ -1,4 +1,4 @@
import logo from "../assets/Vue-ECharts.svg";
import logo from "../assets/Vue-ECharts.svg?raw";
const d = logo.match(/\bd="([^"]+)"/)[1];
@ -9,27 +9,25 @@ export default {
data: [0.7, 0.6, 0.55, 0.45],
amplitude: 6,
outline: {
show: false
show: false,
},
radius: "60%",
color: ["#4fc08d", "#44d64a", "#33c762", "#4acc80"],
backgroundStyle: {
color: "#fff",
borderColor: "#2c3e50",
borderWidth: 1
borderWidth: 1,
},
shape: `path://${d}`,
label: {
normal: {
formatter() {
return "";
}
}
},
},
itemStyle: {
shadowBlur: 12,
shadowColor: "rgba(0, 0, 0, 0.25)"
}
}
]
shadowColor: "rgba(0, 0, 0, 0.25)",
},
},
],
};

View File

@ -188,7 +188,7 @@ const data = [
{ name: "菏泽", value: 194 },
{ name: "合肥", value: 229 },
{ name: "武汉", value: 273 },
{ name: "大庆", value: 279 }
{ name: "大庆", value: 279 },
];
const geoCoordMap = {
海门: [121.15, 31.89],
@ -380,7 +380,7 @@ const geoCoordMap = {
菏泽: [115.480656, 35.23375],
合肥: [117.27, 31.86],
武汉: [114.31, 30.52],
大庆: [125.03, 46.58]
大庆: [125.03, 46.58],
};
function convertData(data) {
@ -390,7 +390,7 @@ function convertData(data) {
if (geoCoord) {
res.push({
name: data[i].name,
value: geoCoord.concat(data[i].value)
value: geoCoord.concat(data[i].value),
});
}
}
@ -401,7 +401,7 @@ export default function getData() {
return {
textStyle: {
fontFamily: 'Inter, "Helvetica Neue", Arial, sans-serif',
fontWeight: 300
fontWeight: 300,
},
backgroundColor: "#404a59",
title: {
@ -411,11 +411,11 @@ export default function getData() {
top: "5%",
left: "center",
textStyle: {
color: "#fff"
}
color: "#fff",
},
},
tooltip: {
trigger: "item"
trigger: "item",
},
legend: {
orient: "vertical",
@ -423,25 +423,25 @@ export default function getData() {
bottom: "5%",
data: ["PM2.5"],
textStyle: {
color: "#fff"
}
color: "#fff",
},
},
geo: {
map: "china",
emphasis: {
label: {
show: false
show: false,
},
itemStyle: {
areaColor: "#2a333d"
}
areaColor: "#2a333d",
},
},
itemStyle: {
areaColor: "#323c48",
borderColor: "#111"
borderColor: "#111",
},
top: "20%",
bottom: "7%"
bottom: "7%",
},
series: [
{
@ -449,46 +449,46 @@ export default function getData() {
type: "scatter",
coordinateSystem: "geo",
data: convertData(data),
symbolSize: val => val[2] / 10,
symbolSize: (val) => val[2] / 10,
tooltip: {
formatter: function (val) {
return val.name + ": " + val.value[2];
}
},
},
itemStyle: {
color: "#ddb926"
}
color: "#ddb926",
},
},
{
name: "Top 5",
type: "effectScatter",
coordinateSystem: "geo",
data: convertData(data.sort((a, b) => b.value - a.value).slice(0, 6)),
symbolSize: val => val[2] / 10,
symbolSize: (val) => val[2] / 10,
showEffectOn: "render",
rippleEffect: {
brushType: "stroke"
brushType: "stroke",
},
emphasis: {
scale: true
scale: true,
},
tooltip: {
formatter: function (val) {
return val.name + ": " + val.value[2];
}
},
},
label: {
formatter: "{b}",
position: "right",
show: true
show: true,
},
itemStyle: {
color: "#f4e925",
shadowBlur: 10,
shadowColor: "#333"
shadowColor: "#333",
},
zlevel: 1
}
]
zlevel: 1,
},
],
};
}

View File

@ -2,22 +2,22 @@ export default function getData() {
return {
textStyle: {
fontFamily: 'Inter, "Helvetica Neue", Arial, sans-serif',
fontWeight: 300
fontWeight: 300,
},
title: {
text: "Traffic Sources",
top: "5%",
left: "center"
left: "center",
},
tooltip: {
trigger: "item",
formatter: "{a} <br/>{b} : {c} ({d}%)"
formatter: "{a} <br/>{b} : {c} ({d}%)",
},
legend: {
orient: "vertical",
top: "5%",
left: "5%",
data: ["Direct", "Email", "Ad Networks", "Video Ads", "Search Engines"]
data: ["Direct", "Email", "Ad Networks", "Video Ads", "Search Engines"],
},
series: [
{
@ -30,16 +30,16 @@ export default function getData() {
{ value: 310, name: "Email" },
{ value: 234, name: "Ad Networks" },
{ value: 135, name: "Video Ads" },
{ value: 1548, name: "Search Engines" }
{ value: 1548, name: "Search Engines" },
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: "rgba(0, 0, 0, 0.5)"
}
}
}
]
shadowColor: "rgba(0, 0, 0, 0.5)",
},
},
},
],
};
}

View File

@ -10,33 +10,33 @@ export default function getData() {
return {
textStyle: {
fontFamily: 'Inter, "Helvetica Neue", Arial, sans-serif',
fontWeight: 300
fontWeight: 300,
},
title: {
text: "Dual Numeric Axis",
top: "5%",
left: "5%"
left: "5%",
},
legend: {
data: ["line"],
top: "6%"
top: "6%",
},
polar: {
radius: "65%",
center: ["50%", "56%"]
center: ["50%", "56%"],
},
tooltip: {
trigger: "axis",
axisPointer: {
type: "cross"
}
type: "cross",
},
},
angleAxis: {
type: "value",
startAngle: 0
startAngle: 0,
},
radiusAxis: {
min: 0
min: 0,
},
series: [
{
@ -44,9 +44,9 @@ export default function getData() {
name: "line",
type: "line",
showSymbol: false,
data: data
}
data: data,
},
],
animationDuration: 2000
animationDuration: 2000,
};
}

View File

@ -8,7 +8,7 @@ export const useScoreStore = defineStore("store", () => {
{ name: "Speed", max: 20, value: 18 },
{ name: "Strength", max: 20, value: 16 },
{ name: "Endurance", max: 20, value: 16 },
{ name: "Agility", max: 20, value: 20 }
{ name: "Agility", max: 20, value: 20 },
]);
const metrics = computed(() => {
@ -20,27 +20,28 @@ export const useScoreStore = defineStore("store", () => {
title: {
text: "Player Ability",
top: "5%",
left: "5%"
left: "5%",
},
textStyle: {
fontFamily: 'Inter, "Helvetica Neue", Arial, sans-serif',
fontWeight: 300
fontWeight: 300,
},
radar: {
splitNumber: 4,
indicator: scores.value.map(({ name, max }, index) => {
if (index === activeIndex) {
return { name, max, color: "goldenrod" };
}
return { name, max };
})
}),
},
series: [
{
name: "Value",
type: "radar",
data: [{ value: scores.value.map(({ value }) => value) }]
}
]
data: [{ value: scores.value.map(({ value }) => value) }],
},
],
};
}
@ -64,6 +65,6 @@ export const useScoreStore = defineStore("store", () => {
getRadarData,
increase,
isMax,
isMin
isMin,
};
});

View File

@ -20,7 +20,7 @@ const data = [
[19349, 69.6, 147568552, "Russia", 1990],
[10670, 67.3, 53994605, "Turkey", 1990],
[26424, 75.7, 57110117, "United Kingdom", 1990],
[37062, 75.4, 252847810, "United States", 1990]
[37062, 75.4, 252847810, "United States", 1990],
],
[
[44056, 81.8, 23968973, "Australia", 2015],
@ -41,43 +41,43 @@ const data = [
[23038, 73.13, 143456918, "Russia", 2015],
[19360, 76.5, 78665830, "Turkey", 2015],
[38225, 81.4, 64715810, "United Kingdom", 2015],
[53354, 79.1, 321773631, "United States", 2015]
]
[53354, 79.1, 321773631, "United States", 2015],
],
];
export default function getData() {
return {
grid: {
top: "25%"
top: "25%",
},
textStyle: {
fontFamily: 'Inter, "Helvetica Neue", Arial, sans-serif',
fontWeight: 300
fontWeight: 300,
},
title: {
text: "Life Expectancy vs. GDP by country",
top: "5%",
left: "5%"
left: "5%",
},
legend: {
top: "6%",
right: "5%",
data: ["1990", "2015"]
data: ["1990", "2015"],
},
xAxis: {
splitLine: {
lineStyle: {
type: "dashed"
}
}
type: "dashed",
},
},
},
yAxis: {
splitLine: {
lineStyle: {
type: "dashed"
}
type: "dashed",
},
scale: true
},
scale: true,
},
series: [
{
@ -93,8 +93,8 @@ export default function getData() {
formatter({ data }) {
return data[3];
},
position: "top"
}
position: "top",
},
},
itemStyle: {
shadowBlur: 10,
@ -103,14 +103,14 @@ export default function getData() {
color: new graphic.RadialGradient(0.4, 0.3, 1, [
{
offset: 0,
color: "rgb(251, 118, 123)"
color: "rgb(251, 118, 123)",
},
{
offset: 1,
color: "rgb(204, 46, 72)"
}
])
}
color: "rgb(204, 46, 72)",
},
]),
},
},
{
name: "2015",
@ -125,8 +125,8 @@ export default function getData() {
formatter({ data }) {
return data[3];
},
position: "top"
}
position: "top",
},
},
itemStyle: {
shadowBlur: 10,
@ -135,15 +135,15 @@ export default function getData() {
color: new graphic.RadialGradient(0.4, 0.3, 1, [
{
offset: 0,
color: "rgb(129, 227, 238)"
color: "rgb(129, 227, 238)",
},
{
offset: 1,
color: "rgb(25, 183, 207)"
}
])
}
}
]
color: "rgb(25, 183, 207)",
},
]),
},
},
],
};
}

View File

@ -3,8 +3,8 @@ import { use, registerTheme } from "echarts/core";
import { BarChart } from "echarts/charts";
import { GridComponent, DatasetComponent } from "echarts/components";
import { shallowRef, onBeforeUnmount } from "vue";
import VChart from "../../ECharts";
import VExample from "./Example";
import VChart from "../../src/ECharts";
import VExample from "./Example.vue";
import getData from "../data/bar";
import theme from "../theme.json";
@ -16,7 +16,7 @@ const loading = shallowRef(false);
const loadingOptions = {
text: "Loading…",
color: "#4ea397",
maskColor: "rgba(255, 255, 255, 0.4)"
maskColor: "rgba(255, 255, 255, 0.4)",
};
const option = shallowRef(getData());

View File

@ -5,11 +5,11 @@ import {
GridComponent,
TitleComponent,
VisualMapComponent,
TooltipComponent
TooltipComponent,
} from "echarts/components";
import { shallowRef, watch } from "vue";
import VChart from "../../ECharts";
import VExample from "./Example";
import VChart from "../../src/ECharts";
import VExample from "./Example.vue";
import getData from "../data/connect";
use([
@ -17,7 +17,7 @@ use([
GridComponent,
TitleComponent,
VisualMapComponent,
TooltipComponent
TooltipComponent,
]);
const [c1, c2] = getData().map(shallowRef);
@ -25,14 +25,14 @@ const connected = shallowRef(true);
watch(
connected,
value => {
(value) => {
if (value) {
connect("radiance");
} else {
disconnect("radiance");
}
},
{ immediate: true }
{ immediate: true },
);
</script>

View File

@ -22,19 +22,17 @@
</template>
<script setup>
import { defineProps } from "vue";
defineProps({
id: {
type: String,
required: true
required: true,
},
title: {
type: String,
required: true
required: true,
},
desc: String,
split: Boolean
split: Boolean,
});
</script>
@ -45,7 +43,7 @@ defineProps({
width: fit-content;
margin: 2em auto;
.echarts {
> .echarts {
width: calc(60vw + 4em);
height: 360px;
max-width: 720px;

View File

@ -5,13 +5,13 @@ import {
GeoComponent,
TitleComponent,
LegendComponent,
TooltipComponent
TooltipComponent,
} from "echarts/components";
import { shallowRef } from "vue";
import VChart from "../../ECharts";
import VExample from "./Example";
import VChart from "../../src/ECharts";
import VExample from "./Example.vue";
import getData from "../data/map";
import chinaMap from "../china.json";
import chinaMap from "../data/china.json";
use([
ScatterChart,
@ -19,7 +19,7 @@ use([
GeoComponent,
TitleComponent,
LegendComponent,
TooltipComponent
TooltipComponent,
]);
registerMap("china", chinaMap);
@ -33,10 +33,10 @@ let img = null;
function convert() {
img = {
src: map.value.getDataURL({
pixelRatio: window.devicePixelRatio || 1
pixelRatio: window.devicePixelRatio || 1,
}),
width: map.value.getWidth(),
height: map.value.getHeight()
height: map.value.getHeight(),
};
open.value = true;
}

View File

@ -4,8 +4,8 @@ import { Bar3DChart } from "echarts-gl/charts";
import { VisualMapComponent } from "echarts/components";
import { GlobeComponent } from "echarts-gl/components";
import { shallowRef, onMounted } from "vue";
import VChart from "../../ECharts";
import VExample from "./Example";
import VChart from "../../src/ECharts";
import VExample from "./Example.vue";
import world from "../assets/world.jpg";
import starfield from "../assets/starfield.jpg";
@ -15,14 +15,14 @@ const option = shallowRef();
const loading = shallowRef(true);
const initOptions = {
renderer: "canvas"
renderer: "canvas",
};
const loadingOptions = {
text: "Loading...",
color: "#000",
textColor: "#fff",
maskColor: "transparent"
maskColor: "transparent",
};
onMounted(() => {
@ -30,8 +30,8 @@ onMounted(() => {
loading.value = false;
data = data
.filter(dataItem => dataItem[2] > 0)
.map(dataItem => [dataItem[0], dataItem[1], Math.sqrt(dataItem[2])]);
.filter((dataItem) => dataItem[2] > 0)
.map((dataItem) => [dataItem[0], dataItem[1], Math.sqrt(dataItem[2])]);
option.value = {
backgroundColor: "#000",
@ -42,12 +42,12 @@ onMounted(() => {
environment: starfield,
light: {
main: {
intensity: 2
}
intensity: 2,
},
},
viewControl: {
autoRotate: false
}
autoRotate: false,
},
},
visualMap: {
bottom: "3%",
@ -56,19 +56,19 @@ onMounted(() => {
calculable: true,
realtime: false,
inRange: {
colorLightness: [0.2, 0.9]
colorLightness: [0.2, 0.9],
},
textStyle: {
color: "#fff"
color: "#fff",
},
controller: {
inRange: {
color: "orange"
}
color: "orange",
},
},
outOfRange: {
colorAlpha: 0
}
colorAlpha: 0,
},
},
series: [
{
@ -79,10 +79,10 @@ onMounted(() => {
minHeight: 0.2,
silent: true,
itemStyle: {
color: "orange"
}
}
]
color: "orange",
},
},
],
};
});
});

108
demo/examples/LineChart.vue Normal file
View File

@ -0,0 +1,108 @@
<script setup>
import { use } from "echarts/core";
import { LineChart, PieChart } from "echarts/charts";
import {
GridComponent,
DatasetComponent,
LegendComponent,
TooltipComponent,
ToolboxComponent,
} from "echarts/components";
import { shallowRef } from "vue";
import VChart from "../../src/ECharts";
import VExample from "./Example.vue";
import getData from "../data/line";
use([
DatasetComponent,
GridComponent,
LegendComponent,
LineChart,
TooltipComponent,
ToolboxComponent,
PieChart,
]);
const option = shallowRef(getData());
const axis = shallowRef("xAxis");
function getPieOption(params) {
const option = {
dataset: { source: [params[0].dimensionNames, params[0].data] },
series: [
{
type: "pie",
radius: ["60%", "100%"],
seriesLayoutBy: "row",
itemStyle: {
borderRadius: 5,
borderColor: "#fff",
borderWidth: 2,
},
label: {
position: "center",
formatter: params[0].name,
fontFamily: 'Inter, "Helvetica Neue", Arial, sans-serif',
fontWeight: 300,
},
},
],
};
return option;
}
</script>
<template>
<v-example
id="line"
title="Line chart"
desc="(with tooltip and dataView slots)"
>
<v-chart :option="option" autoresize>
<template #tooltip="params">
<v-chart
:style="{ width: '100px', height: '100px' }"
:option="getPieOption(params)"
autoresize
/>
</template>
<template #[`tooltip-${axis}`]="params">
{{ axis === "xAxis" ? "Year" : "Value" }}:
<b>{{ params.name }}</b>
</template>
<template #dataView="option">
<table style="margin: 20px auto">
<thead>
<tr>
<th v-for="(t, i) in option.dataset[0].source[0]" :key="i">
{{ t }}
</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, i) in option.dataset[0].source.slice(1)" :key="i">
<th>{{ row[0] }}</th>
<td v-for="(v, i) in row.slice(1)" :key="i">{{ v }}</td>
</tr>
</tbody>
</table>
</template>
</v-chart>
<template #extra>
<p class="actions">
Custom tooltip on
<select v-model="axis">
<option value="xAxis">X Axis</option>
<option value="yAxis">Y Axis</option>
</select>
</p>
</template>
</v-example>
</template>
<style scoped>
th,
td {
padding: 4px 8px;
}
</style>

View File

@ -2,7 +2,7 @@
import { registerTheme } from "echarts/core";
import "echarts-liquidfill";
import VChart from "../../ECharts";
import VChart from "../../src/ECharts";
import theme from "../theme.json";
import logo from "../data/logo";

View File

@ -4,12 +4,12 @@ import { LinesChart } from "echarts/charts";
import {
GeoComponent,
TitleComponent,
TooltipComponent
TooltipComponent,
} from "echarts/components";
import { shallowRef } from "vue";
import VChart from "../../ECharts";
import VExample from "./Example";
import worldMap from "../world.json";
import VChart from "../../src/ECharts";
import VExample from "./Example.vue";
import worldMap from "../data/world.json";
use([LinesChart, GeoComponent, TitleComponent, TooltipComponent]);
registerMap("world", worldMap);
@ -23,7 +23,7 @@ const loadingOptions = {
color: "#c23531",
textColor: "rgba(255, 255, 255, 0.5)",
maskColor: "#003",
zlevel: 0
zlevel: 0,
};
function load() {
@ -36,21 +36,21 @@ function load() {
function getAirportCoord(idx) {
return [data.airports[idx][3], data.airports[idx][4]];
}
const routes = data.routes.map(airline => {
const routes = data.routes.map((airline) => {
return [getAirportCoord(airline[1]), getAirportCoord(airline[2])];
});
chart.value.setOption({
textStyle: {
fontFamily: 'Inter, "Helvetica Neue", Arial, sans-serif'
fontFamily: 'Inter, "Helvetica Neue", Arial, sans-serif',
},
title: {
text: "World Flights",
top: "5%",
left: "center",
textStyle: {
color: "#eee"
}
color: "#eee",
},
},
backgroundColor: "#003",
tooltip: {
@ -59,7 +59,7 @@ function load() {
return (
data.airports[route[1]][1] + " > " + data.airports[route[2]][1]
);
}
},
},
geo: {
map: "world",
@ -70,24 +70,22 @@ function load() {
silent: true,
itemStyle: {
borderColor: "#003",
color: "#005"
}
color: "#005",
},
},
series: [
{
type: "lines",
coordinateSystem: "geo",
data: routes,
large: true,
largeThreshold: 100,
lineStyle: {
opacity: 0.05,
width: 0.5,
curveness: 0.3
curveness: 0.3,
},
blendMode: "lighter"
}
]
blendMode: "lighter",
},
],
});
});
}

View File

@ -5,11 +5,11 @@ import {
PolarComponent,
TitleComponent,
LegendComponent,
TooltipComponent
TooltipComponent,
} from "echarts/components";
import { shallowRef, onMounted } from "vue";
import VChart from "../../ECharts";
import VExample from "./Example";
import { shallowRef, onMounted, onUnmounted } from "vue";
import VChart from "../../src/ECharts";
import VExample from "./Example.vue";
import getData from "../data/pie";
use([
@ -17,7 +17,7 @@ use([
PolarComponent,
TitleComponent,
LegendComponent,
TooltipComponent
TooltipComponent,
]);
const option = shallowRef(getData());
@ -27,10 +27,10 @@ let timer = null;
onMounted(() => {
startActions();
});
return () => {
onUnmounted(() => {
stopActions();
};
});
function startActions() {
@ -54,18 +54,18 @@ function startActions() {
pie.value.dispatchAction({
type: "downplay",
seriesIndex: 0,
dataIndex
dataIndex,
});
dataIndex = (dataIndex + 1) % dataLen;
pie.value.dispatchAction({
type: "highlight",
seriesIndex: 0,
dataIndex
dataIndex,
});
pie.value.dispatchAction({
type: "showTip",
seriesIndex: 0,
dataIndex
dataIndex,
});
}, 1000);
}

View File

@ -5,11 +5,11 @@ import {
PolarComponent,
TitleComponent,
LegendComponent,
TooltipComponent
TooltipComponent,
} from "echarts/components";
import { computed, shallowRef } from "vue";
import VChart from "../../ECharts";
import VExample from "./Example";
import VChart from "../../src/ECharts";
import VExample from "./Example.vue";
import getData from "../data/polar";
use([
@ -17,7 +17,7 @@ use([
PolarComponent,
TitleComponent,
LegendComponent,
TooltipComponent
TooltipComponent,
]);
const option = shallowRef(getData());
@ -28,9 +28,9 @@ const loadingOptions = computed(() =>
? {
color: "#fff",
textColor: "#fff",
maskColor: "rgba(0, 0, 0, 0.7)"
maskColor: "rgba(0, 0, 0, 0.7)",
}
: null
: null,
);
const style = computed(() => {
return theme.value === "dark"

View File

@ -4,11 +4,11 @@ import { RadarChart } from "echarts/charts";
import {
PolarComponent,
TitleComponent,
TooltipComponent
TooltipComponent,
} from "echarts/components";
import { shallowRef } from "vue";
import VChart from "../../ECharts";
import VExample from "./Example";
import VChart from "../../src/ECharts";
import VExample from "./Example.vue";
import { useScoreStore } from "../data/radar";
use([RadarChart, PolarComponent, TitleComponent, TooltipComponent]);

View File

@ -4,11 +4,11 @@ import { ScatterChart } from "echarts/charts";
import {
GridComponent,
TitleComponent,
LegendComponent
LegendComponent,
} from "echarts/components";
import { shallowRef } from "vue";
import VChart from "../../ECharts";
import VExample from "./Example";
import VChart from "../../src/ECharts";
import VExample from "./Example.vue";
import getData from "../data/scatter";
use([ScatterChart, GridComponent, TitleComponent, LegendComponent]);

18
demo/index.html Normal file
View File

@ -0,0 +1,18 @@
<!doctype html>
<html lang="en-US">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<link rel="icon" href="/favicon.ico" />
<link
href="https://fonts.googleapis.com/css?family=Inter:300,500;display=swap"
rel="stylesheet"
/>
<title>Vue-ECharts: Vue.js component for Apache ECharts™.</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/main.ts"></script>
</body>
</html>

View File

@ -6,13 +6,13 @@ import Demo from "./Demo.vue";
const SAMPLE_RATE = 0.5;
inject({
beforeSend: event => {
beforeSend: (event) => {
if (Math.random() > SAMPLE_RATE) {
return null;
}
return event;
}
},
});
const pinia = createPinia();

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

6
demo/shims-vue.d.ts vendored Normal file
View File

@ -0,0 +1,6 @@
/* eslint-disable */
declare module "*.vue" {
import type { DefineComponent } from "vue";
const component: DefineComponent<{}, {}, any>;
export default component;
}

8
demo/tsconfig.json Normal file
View File

@ -0,0 +1,8 @@
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"include": ["./**/*", "./**/*.vue"],
"compilerOptions": {
"types": ["vite/client"],
"allowJs": true
}
}

View File

@ -8,6 +8,7 @@ const COMPONENTS_MAP = {
singleAxis: "SingleAxisComponent",
parallel: "ParallelComponent",
calendar: "CalendarComponent",
matrix: "MatrixComponent",
graphic: "GraphicComponent",
toolbox: "ToolboxComponent",
tooltip: "TooltipComponent",
@ -21,6 +22,7 @@ const COMPONENTS_MAP = {
legend: "LegendComponent",
dataZoom: "DataZoomComponent",
visualMap: "VisualMapComponent",
thumbnail: "ThumbnailComponent",
aria: "AriaComponent",
dataset: "DatasetComponent",
@ -28,7 +30,7 @@ const COMPONENTS_MAP = {
xAxis: "GridComponent",
yAxis: "GridComponent",
angleAxis: "PolarComponent",
radiusAxis: "PolarComponent"
radiusAxis: "PolarComponent",
};
const CHARTS_MAP = {
@ -41,6 +43,7 @@ const CHARTS_MAP = {
tree: "TreeChart",
treemap: "TreemapChart",
graph: "GraphChart",
chord: "ChordChart",
gauge: "GaugeChart",
funnel: "FunnelChart",
parallel: "ParallelChart",
@ -53,7 +56,7 @@ const CHARTS_MAP = {
pictorialBar: "PictorialBarChart",
themeRiver: "ThemeRiverChart",
sunburst: "SunburstChart",
custom: "CustomChart"
custom: "CustomChart",
};
const COMPONENTS_GL_MAP = {
@ -66,7 +69,7 @@ const COMPONENTS_GL_MAP = {
// Dependencies
xAxis3D: "Grid3DComponent",
yAxis3D: "Grid3DComponent",
zAxis3D: "Grid3DComponent"
zAxis3D: "Grid3DComponent",
};
const CHARTS_GL_MAP = {
@ -81,17 +84,23 @@ const CHARTS_GL_MAP = {
scatterGL: "ScatterGLChart",
graphGL: "GraphGLChart",
flowGL: "FlowGLChart",
linesGL: "LinesGLChart"
linesGL: "LinesGLChart",
};
const FEATURES = ["UniversalTransition", "LabelLayout"];
const FEATURES = [
"UniversalTransition",
"LabelLayout",
"AxisBreak",
// "LegacyGridContainLabel",
"ScatterJitter",
];
const RENDERERS_MAP = {
canvas: "CanvasRenderer",
svg: "SVGRenderer"
svg: "SVGRenderer",
};
const EXTENSIONS_MAP = {
bmap: "bmap/bmap"
bmap: "bmap/bmap",
// PENDING: There seem no examples that use dataTool
// dataTool: 'dataTool'
};
@ -103,7 +112,7 @@ const INJECTED_COMPONENTS = [
...MARKERS,
"grid",
"axisPointer",
"aria" // TODO aria
"aria", // TODO aria
];
// Component that was dependent.
@ -114,12 +123,12 @@ const DEPENDENT_COMPONENTS = [
"radiusAxis",
"xAxis3D",
"yAxis3D",
"zAxis3D"
"zAxis3D",
];
function createReverseMap(map) {
const reverseMap = {};
Object.keys(map).forEach(key => {
Object.keys(map).forEach((key) => {
// Exclude dependencies.
if (DEPENDENT_COMPONENTS.includes(key)) {
return;
@ -139,7 +148,7 @@ function collectDeps(option) {
let deps = [];
if (option.options) {
// TODO getOption() doesn't have baseOption and options.
option.options.forEach(opt => {
option.options.forEach((opt) => {
deps = deps.concat(collectDeps(opt));
});
@ -151,11 +160,11 @@ function collectDeps(option) {
return Array.from(new Set(deps));
}
Object.keys(option).forEach(key => {
Object.keys(option).forEach((key) => {
if (INJECTED_COMPONENTS.includes(key)) {
return;
}
const val = option[key];
let val = option[key];
if (Array.isArray(val) && !val.length) {
return;
@ -172,12 +181,12 @@ function collectDeps(option) {
}
});
let series = option.series;
if (!Array.isArray(series)) {
series = [series];
const series = Array.isArray(option.series) ? option.series : [option.series];
let hasScatterSeries = false;
series.forEach((seriesOpt) => {
if (seriesOpt.type === "scatter") {
hasScatterSeries = true;
}
series.forEach(seriesOpt => {
if (CHARTS_MAP[seriesOpt.type]) {
deps.push(CHARTS_MAP[seriesOpt.type]);
}
@ -191,7 +200,7 @@ function collectDeps(option) {
if (seriesOpt.coordinateSystem === "bmap") {
deps.push("bmap");
}
MARKERS.forEach(markerType => {
MARKERS.forEach((markerType) => {
if (seriesOpt[markerType]) {
deps.push(COMPONENTS_MAP[markerType]);
}
@ -204,9 +213,24 @@ function collectDeps(option) {
deps.push("UniversalTransition");
}
});
Object.keys(option).forEach((key) => {
if (key.endsWith("Axis")) {
const val = option[key];
for (const axisOption of Array.isArray(val) ? val : [val]) {
if (hasScatterSeries && +axisOption.jitter > 0) {
deps.push("ScatterJitter");
}
if (axisOption.breaks && axisOption.breaks.length > 0) {
deps.push("AxisBreak");
}
}
}
});
// Dataset transform
if (option.dataset && Array.isArray(option.dataset)) {
option.dataset.forEach(dataset => {
option.dataset.forEach((dataset) => {
if (dataset.transform) {
deps.push("TransformComponent");
}
@ -225,15 +249,15 @@ function buildMinimalBundleCode(
quote = "'",
multiline = false,
indent = " ",
maxLen = 80
}
maxLen = 80,
},
) {
const options = {
semi,
quote,
multiline,
indent,
maxLen
maxLen,
};
const chartsImports = [];
@ -244,7 +268,7 @@ function buildMinimalBundleCode(
const renderersImports = [];
const extensionImports = [];
deps.forEach(dep => {
deps.forEach((dep) => {
if (dep.endsWith("Renderer")) {
renderersImports.push(dep);
} else if (CHARTS_MAP_REVERSE[dep]) {
@ -278,12 +302,12 @@ function buildMinimalBundleCode(
...componentsGLImports,
...chartsGLImports,
...renderersImports,
...featuresImports
...featuresImports,
];
const ECOptionTypeCode = typeItems(
allImports.filter(a => a.endsWith("Option")),
options
allImports.filter((a) => a.endsWith("Option")),
options,
);
const importSources = [
@ -292,33 +316,33 @@ function buildMinimalBundleCode(
[featuresImports, "echarts/features"],
[renderersImports, "echarts/renderers"],
[chartsGLImports, "echarts-gl/charts"],
[componentsGLImports, "echarts-gl/components"]
].filter(a => a[0].length > 0);
[componentsGLImports, "echarts-gl/components"],
].filter((a) => a[0].length > 0);
const importStatements = importSources.map(([imports, mod]) =>
importItems(
imports.filter(a => !a.endsWith("Option")),
imports.filter((a) => !a.endsWith("Option")),
mod,
options
)
options,
),
);
const semiStr = semi ? ";" : "";
getExtensionDeps(extensionImports, includeType).forEach(ext => {
getExtensionDeps(extensionImports, includeType).forEach((ext) => {
importStatements.push(`import ${quote}${ext}${quote}${semiStr}`);
});
if (includeType) {
importStatements.push(
`import type { ComposeOption } from ${quote}echarts/core${quote}${semiStr}`
`import type { ComposeOption } from ${quote}echarts/core${quote}${semiStr}`,
);
const importTypeStatements = importSources
.map(([imports, mod]) =>
importItems(
imports.filter(a => a.endsWith("Option")),
imports.filter((a) => a.endsWith("Option")),
mod,
{ ...options, type: true }
)
{ ...options, type: true },
),
)
.filter(Boolean);
importStatements.push(...importTypeStatements);
@ -328,16 +352,18 @@ function buildMinimalBundleCode(
${importStatements.join("\n")}
${useItems(
allImports.filter(a => !a.endsWith("Option")),
options
allImports.filter((a) => !a.endsWith("Option")),
options,
)}
${includeType ? `\n${ECOptionTypeCode}` : ""}
`;
}
function getExtensionDeps(deps, ts) {
return deps
.filter(dep => EXTENSIONS_MAP[dep])
.map(dep => `echarts/extension${ts ? "-src" : ""}/${EXTENSIONS_MAP[dep]}`);
.filter((dep) => EXTENSIONS_MAP[dep])
.map(
(dep) => `echarts/extension${ts ? "-src" : ""}/${EXTENSIONS_MAP[dep]}`,
);
}
/** import */
@ -366,7 +392,7 @@ function importSingleLine(items, module, { type, semi, quote }) {
const semiStr = semi ? ";" : "";
return `import ${typeStr}{ ${items.join(
", "
", ",
)} } from ${quote}${module}${quote}${semiStr}`;
}
@ -379,7 +405,7 @@ function importMultiLine(items, module, { type, indent, semi, quote }) {
const semiStr = semi ? ";" : "";
return `import ${typeStr}{
${items.map(item => `${indent}${item}`).join(",\n")}
${items.map((item) => `${indent}${item}`).join(",\n")}
} from ${quote}${module}${quote}${semiStr}`;
}
@ -418,7 +444,7 @@ function useMultiLine(items, { indent, semi }) {
const semiStr = semi ? ";" : "";
return `use([
${items.map(item => `${indent}${item}`).join(`,\n`)}
${items.map((item) => `${indent}${item}`).join(`,\n`)}
])${semiStr}`;
}
@ -457,16 +483,16 @@ function typeMultiLine(items, { indent, semi }) {
const semiStr = semi ? ";" : "";
return `type EChartsOption = ComposeOption<
${items.map(item => `${indent}| ${item}`).join("\n")}
${items.map((item) => `${indent}| ${item}`).join("\n")}
>${semiStr}`;
}
export function getImportsFromOption(
option,
{ renderer = "canvas", ...options }
{ renderer = "canvas", ...options },
) {
return buildMinimalBundleCode(
[...collectDeps(option), RENDERERS_MAP[renderer]],
options
options,
);
}

30
eslint.config.ts Normal file
View File

@ -0,0 +1,30 @@
import {
defineConfigWithVueTs,
vueTsConfigs,
configureVueProject,
} from "@vue/eslint-config-typescript";
import pluginVue from "eslint-plugin-vue";
import skipFormatting from "@vue/eslint-config-prettier/skip-formatting";
// The inferred type of 'default' cannot be named without a reference to "@typescript-eslint/utils"
import type {} from "@typescript-eslint/utils";
// To allow more languages other than `ts` in `.vue` files
// More info at https://github.com/vuejs/eslint-config-typescript/#advanced-setup
configureVueProject({ scriptLangs: ["ts", "js"] });
export default defineConfigWithVueTs(
{
name: "app/files-to-lint",
files: ["**/*.{ts,mts,tsx,vue}"],
},
{ ignores: ["**/dist/**"] },
pluginVue.configs["flat/essential"],
vueTsConfigs.recommended,
skipFormatting,
{
rules: {
"vue/multi-word-component-names": "off",
"@typescript-eslint/no-explicit-any": "off",
},
},
);

View File

@ -1,20 +1,27 @@
{
"name": "vue-echarts",
"version": "7.0.3",
"version": "8.0.0-beta.1",
"description": "Vue.js component for Apache ECharts™.",
"license": "MIT",
"repository": "https://github.com/ecomfe/vue-echarts.git",
"repository": {
"type": "git",
"url": "git+https://github.com/ecomfe/vue-echarts.git"
},
"author": "GU Yiling <justice360@gmail.com>",
"scripts": {
"serve": "vue-cli-service serve",
"build": "pnpm run docs && rimraf dist && pnpm run build:rollup",
"build:rollup": "vue-demi-switch 3 && rollup -c rollup.config.js",
"lint": "vue-cli-service lint",
"dev": "vite",
"build": "tsdown",
"typecheck": "tsc",
"lint": "eslint . --fix",
"format": "prettier . --write",
"publint": "publint",
"build:demo": "vue-cli-service build",
"docs": "node ./scripts/docs.mjs",
"prepublishOnly": "pnpm run build && publint"
"dev:build": "vite build",
"dev:preview": "vite preview",
"dev:typecheck": "vue-tsc -p ./demo",
"docs": "jiti ./scripts/docs.ts",
"release": "pnpm run docs && bumpp --all"
},
"packageManager": "pnpm@10.14.0",
"type": "module",
"main": "dist/index.js",
"unpkg": "dist/index.min.js",
@ -22,69 +29,52 @@
"types": "dist/index.d.ts",
"exports": {
".": "./dist/index.js",
"./csp": "./dist/csp/index.js",
"./csp/style.css": "./dist/csp/style.css"
"./style.css": "./dist/style.css"
},
"files": [
"dist",
"scripts/postinstall.js"
"dist"
],
"dependencies": {
"vue-demi": "^0.13.11"
},
"peerDependencies": {
"@vue/runtime-core": "^3.0.0",
"echarts": "^5.5.1",
"vue": "^2.7.0 || ^3.1.1"
},
"peerDependenciesMeta": {
"@vue/runtime-core": {
"optional": true
}
"echarts": "^6.0.0",
"vue": "^3.3.0"
},
"devDependencies": {
"@babel/core": "^7.24.9",
"@highlightjs/vue-plugin": "^2.1.0",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-replace": "^5.0.7",
"@typescript-eslint/eslint-plugin": "^7.17.0",
"@typescript-eslint/parser": "^7.17.0",
"@vercel/analytics": "^1.3.1",
"@vue/cli-plugin-babel": "^5.0.8",
"@vue/cli-plugin-eslint": "^5.0.8",
"@vue/cli-plugin-typescript": "^5.0.8",
"@vue/cli-service": "^5.0.8",
"@vue/compiler-sfc": "^3.4.33",
"@vue/eslint-config-prettier": "^9.0.0",
"@vue/eslint-config-typescript": "^13.0.0",
"@vueuse/core": "^10.11.0",
"comment-mark": "^1.1.1",
"core-js": "^3.37.1",
"echarts": "^5.5.1",
"@types/node": "^22.17.0",
"@typescript-eslint/utils": "^8.39.0",
"@vercel/analytics": "^1.5.0",
"@vitejs/plugin-vue": "^6.0.1",
"@vue/eslint-config-prettier": "^10.2.0",
"@vue/eslint-config-typescript": "^14.6.0",
"@vue/tsconfig": "^0.7.0",
"@vueuse/core": "^13.6.0",
"bumpp": "^10.2.2",
"comment-mark": "^2.0.1",
"echarts": "^6.0.0",
"echarts-gl": "^2.0.9",
"echarts-liquidfill": "^3.1.0",
"esbuild-wasm": "^0.23.0",
"eslint": "^8.57.0",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-vue": "^9.27.0",
"highlight.js": "^11.10.0",
"pinia": "^2.1.7",
"postcss": "^8.4.39",
"postcss-loader": "^8.1.1",
"postcss-nested": "^6.2.0",
"prettier": "^3.3.3",
"publint": "^0.2.9",
"raw-loader": "^4.0.2",
"resize-detector": "^0.3.0",
"rimraf": "^6.0.1",
"rollup": "^4.19.0",
"rollup-plugin-dts": "^6.1.0",
"rollup-plugin-esbuild": "^6.1.1",
"rollup-plugin-import-css": "^3.5.1",
"tslib": "^2.6.3",
"typescript": "5.5.4",
"vue": "^3.4.33",
"vue2": "npm:vue@^2.7.16",
"webpack": "^5.93.0"
"esbuild-wasm": "^0.25.8",
"eslint": "^9.32.0",
"eslint-plugin-vue": "^10.4.0",
"highlight.js": "^11.11.1",
"jiti": "^2.5.1",
"pinia": "^3.0.3",
"postcss-nested": "^7.0.2",
"prettier": "^3.6.2",
"publint": "^0.3.12",
"releaselog": "^6.0.3",
"tsdown": "^0.13.3",
"typescript": "^5.9.2",
"unplugin-raw": "^0.5.1",
"vite": "npm:rolldown-vite@^7.1.0",
"vue": "^3.5.18",
"vue-tsc": "^3.0.5"
},
"pnpm": {
"peerDependencyRules": {
"allowedVersions": {
"echarts": "6"
}
}
}
}

10066
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

2
pnpm-workspace.yaml Normal file
View File

@ -0,0 +1,2 @@
onlyBuiltDependencies:
- esbuild

View File

@ -1,18 +0,0 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<link href="https://fonts.googleapis.com/css?family=Inter:300,500;display=swap" rel="stylesheet">
<title>Vue-ECharts: Vue.js component for Apache ECharts™.</title>
</head>
<body>
<noscript>
<strong>We're sorry but Vue-ECharts doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

View File

@ -1,90 +0,0 @@
import replace from "@rollup/plugin-replace";
import esbuild from "rollup-plugin-esbuild";
import { dts } from "rollup-plugin-dts";
import css from "rollup-plugin-import-css";
import { injectVueDemi } from "./scripts/rollup.mjs";
/**
* Modifies the Rollup options for a build to support strict CSP
* @param {import('rollup').RollupOptions} options the original options
* @param {boolean} csp whether to support strict CSP
* @returns {import('rollup').RollupOptions} the modified options
*/
function configBuild(options, csp) {
const result = { ...options };
const { plugins, output } = result;
result.plugins = [
...(csp ? [replace({ __CSP__: `${csp}`, preventAssignment: true })] : []),
...plugins,
csp ? css({ output: "style.css" }) : css({ inject: true })
];
// modify output file names
if (csp && output) {
result.output = (Array.isArray(output) ? output : [output]).map(output => {
return {
...output,
file: output.file.replace(/^dist\//, "dist/csp/"),
assetFileNames: "[name][extname]"
};
});
}
return result;
}
/** @type {import('rollup').RollupOptions[]} */
const builds = [
{
input: "src/index.ts",
plugins: [esbuild()],
external: ["vue-demi", /^echarts/],
output: [
{
file: "dist/index.js",
format: "esm",
sourcemap: true
}
]
},
{
input: "src/global.ts",
plugins: [esbuild({ minify: true })],
external: ["vue-demi", /^echarts/],
output: [
{
file: "dist/index.min.js", // for unpkg/jsdelivr
format: "umd",
name: "VueECharts",
exports: "default",
sourcemap: true,
globals: {
"vue-demi": "VueDemi",
echarts: "echarts",
"echarts/core": "echarts"
},
plugins: [injectVueDemi]
}
]
}
];
export default [
...builds.map(options => configBuild(options, false)),
...builds.map(options => configBuild(options, true)),
{
input: "src/index.d.ts",
plugins: [dts()],
output: [
{
file: "dist/index.d.ts",
format: "esm"
},
{
file: "dist/csp/index.d.ts",
format: "esm"
}
]
}
];

16
scripts/dist-tag.ts Normal file
View File

@ -0,0 +1,16 @@
/** Resolve npm dist-tag from a semver string.
* Usage: jiti scripts/dist-tag.ts 1.2.3-beta.0 # → beta
* jiti scripts/dist-tag.ts # picks version from package.json
*/
import { readFileSync } from "node:fs";
import { join } from "node:path";
// Prefer CLI arg, otherwise read package.json
const version: string =
process.argv[2] ??
JSON.parse(readFileSync(join(process.cwd(), "package.json"), "utf8")).version;
// Capture first recognised prerelease label
const tag = version.match(/-(alpha|beta|rc)\b/i)?.[1].toLowerCase() ?? "latest";
console.log(tag);

View File

@ -1,57 +0,0 @@
import { readFileSync, writeFileSync } from "node:fs";
import commentMark from "comment-mark";
import { getPackageMeta, resolvePath } from "./utils.mjs";
const { name, version } = getPackageMeta();
const CDN_PREFIX = "https://cdn.jsdelivr.net/npm/";
const DEP_VERSIONS = {
"vue@3": "3.4.33",
"vue@2": "2.7.16",
echarts: "5.5.1",
[name]: version
};
const markConfig = {
vue3Scripts: ["vue@3", "echarts", name],
vue2Scripts: ["vue@2", "echarts", name]
};
function getScripts(version) {
const deps = markConfig[`vue${version}Scripts`];
return deps
.map(dep => {
const [, name] = dep.match(/^(.+?)(?:@.+)?$/) || [];
return `<script src="${CDN_PREFIX}${name}@${DEP_VERSIONS[dep]}"></script>`;
})
.join("\n");
}
function getCodeBlock(code) {
return "```html\n" + code + "\n```";
}
const scripts = {
2: getScripts(2),
3: getScripts(3)
};
const README_FILES = ["README.md", "README.zh-Hans.md"].map(name =>
resolvePath(import.meta.url, "..", name)
);
README_FILES.forEach(file => {
const content = readFileSync(file, "utf8");
writeFileSync(
file,
commentMark(content, {
vue2Scripts: getCodeBlock(scripts[2]),
vue3Scripts: getCodeBlock(scripts[3])
}),
"utf8"
);
});
console.log("README files updated.");

51
scripts/docs.ts Normal file
View File

@ -0,0 +1,51 @@
import { readFileSync, writeFileSync } from "node:fs";
import { commentMark } from "comment-mark";
import { getPackageVersions, resolvePath } from "./utils";
const { name, version, devDependencies } = getPackageVersions([
"echarts",
"vue",
]);
const CDN_PREFIX = "https://cdn.jsdelivr.net/npm/";
const DEP_VERSIONS = {
...Object.fromEntries(
Object.entries(devDependencies).map(([name, { version }]) => [
name,
version,
]),
),
[name]: version,
};
function getScripts() {
return Object.entries(DEP_VERSIONS)
.map(([dep, version]) => {
const [, name] = dep.match(/^(.+?)(?:@.+)?$/) || [];
return `<script src="${CDN_PREFIX}${name}@${version}"></script>`;
})
.join("\n");
}
function getCodeBlock(code: string) {
return "\n```html\n" + code + "\n```\n";
}
const README_FILES = ["README.md", "README.zh-Hans.md"].map((name) =>
resolvePath(import.meta.url, "..", name),
);
README_FILES.forEach((file) => {
const content = readFileSync(file, "utf8");
writeFileSync(
file,
commentMark(content, {
vue3Scripts: getCodeBlock(getScripts()),
}),
"utf8",
);
});
console.log("README files updated.");

View File

@ -1,17 +0,0 @@
import { readFileSync } from "node:fs";
import { createRequire } from "node:module";
const require = createRequire(import.meta.url);
const VUE_DEMI_IIFE = readFileSync(
require.resolve("vue-demi/lib/index.iife.js"),
"utf8"
);
/** @type {import('rollup').Plugin} */
export const injectVueDemi = {
name: "inject-vue-demi",
banner() {
return `${VUE_DEMI_IIFE};\n;`;
}
};

View File

@ -1,13 +0,0 @@
import { readFileSync } from "node:fs";
import { resolve, dirname } from "node:path";
import { fileURLToPath } from "node:url";
export function resolvePath(url, ...parts) {
return resolve(dirname(fileURLToPath(url)), ...parts);
}
export function getPackageMeta() {
return JSON.parse(
readFileSync(resolvePath(import.meta.url, "../package.json"), "utf8")
);
}

20
scripts/utils.ts Normal file
View File

@ -0,0 +1,20 @@
import { execSync } from "node:child_process";
import { resolve, dirname } from "node:path";
import { fileURLToPath } from "node:url";
export function resolvePath(url: string, ...parts: string[]) {
return resolve(dirname(fileURLToPath(url)), ...parts);
}
type PackageVersions = {
name: string;
version: string;
devDependencies: Record<string, { version: string }>;
};
export function getPackageVersions(devDeps?: string[]): PackageVersions {
const stdOut = execSync(`pnpm ls ${devDeps?.join(" ") || ""} --json`, {
encoding: "utf-8",
});
return JSON.parse(stdOut)[0];
}

6
shims-vue.d.ts vendored
View File

@ -1,6 +0,0 @@
/* eslint-disable */
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}

View File

@ -1,5 +1,3 @@
/* eslint-disable vue/multi-word-component-names */
/* eslint-disable @typescript-eslint/no-explicit-any */
import {
defineComponent,
shallowRef,
@ -12,9 +10,8 @@ import {
h,
nextTick,
watchEffect,
getCurrentInstance,
Vue2
} from "vue-demi";
toValue,
} from "vue";
import { init as initChart } from "echarts/core";
import {
@ -22,15 +19,17 @@ import {
useAutoresize,
autoresizeProps,
useLoading,
loadingProps
loadingProps,
useSlotOption,
} from "./composables";
import { isOn, omitOn, unwrapInjected } from "./utils";
import type { PublicMethods, SlotsTypes } from "./composables";
import { isOn, omitOn } from "./utils";
import { register, TAG_NAME } from "./wc";
import type { PropType, InjectionKey } from "vue-demi";
import type { PropType, InjectionKey } from "vue";
import type {
EChartsType,
EventTarget,
SetOptionType,
Option,
Theme,
ThemeInjection,
@ -38,45 +37,38 @@ import type {
InitOptionsInjection,
UpdateOptions,
UpdateOptionsInjection,
Emits
Emits,
} from "./types";
import type { EChartsElement } from "./wc";
import "./style.css";
import "./style.ts";
const __CSP__ = false;
const wcRegistered = __CSP__ ? false : register();
const wcRegistered = register();
if (Vue2) {
Vue2.config.ignoredElements.push(TAG_NAME);
}
export const THEME_KEY = "ecTheme" as unknown as InjectionKey<ThemeInjection>;
export const INIT_OPTIONS_KEY =
"ecInitOptions" as unknown as InjectionKey<InitOptionsInjection>;
export const UPDATE_OPTIONS_KEY =
"ecUpdateOptions" as unknown as InjectionKey<UpdateOptionsInjection>;
export const THEME_KEY: InjectionKey<ThemeInjection> = Symbol();
export const INIT_OPTIONS_KEY: InjectionKey<InitOptionsInjection> = Symbol();
export const UPDATE_OPTIONS_KEY: InjectionKey<UpdateOptionsInjection> =
Symbol();
export { LOADING_OPTIONS_KEY } from "./composables";
const NATIVE_EVENT_RE = /(^&?~?!?)native:/;
export default defineComponent({
name: "echarts",
props: {
option: Object as PropType<Option>,
theme: {
type: [Object, String] as PropType<Theme>
type: [Object, String] as PropType<Theme>,
},
initOptions: Object as PropType<InitOptions>,
updateOptions: Object as PropType<UpdateOptions>,
group: String,
manualUpdate: Boolean,
...autoresizeProps,
...loadingProps
...loadingProps,
},
emits: {} as unknown as Emits,
slots: Object as SlotsTypes,
inheritAttrs: false,
setup(props, { attrs }) {
setup(props, { attrs, expose, slots }) {
const root = shallowRef<EChartsElement>();
const chart = shallowRef<EChartsType>();
const manualOption = shallowRef<Option>();
@ -87,74 +79,66 @@ export default defineComponent({
const { autoresize, manualUpdate, loading, loadingOptions } = toRefs(props);
const realOption = computed(
() => manualOption.value || props.option || null
() => manualOption.value || props.option || null,
);
const realTheme = computed(
() => props.theme || unwrapInjected(defaultTheme, {})
() => props.theme || toValue(defaultTheme) || {},
);
const realInitOptions = computed(
() => props.initOptions || unwrapInjected(defaultInitOptions, {})
() => props.initOptions || toValue(defaultInitOptions) || {},
);
const realUpdateOptions = computed(
() => props.updateOptions || unwrapInjected(defaultUpdateOptions, {})
() => props.updateOptions || toValue(defaultUpdateOptions) || {},
);
const nonEventAttrs = computed(() => omitOn(attrs));
const nativeListeners: Record<string, unknown> = {};
// @ts-expect-error listeners for Vue 2 compatibility
const listeners = getCurrentInstance().proxy.$listeners;
const realListeners: Record<string, any> = {};
const listeners: Map<{ event: string; once?: boolean; zr?: boolean }, any> =
new Map();
if (!listeners) {
// This is for Vue 3.
// We are converting all `on<Event>` props to event listeners compatible with Vue 2
// and collect them into `realListeners` so that we can bind them to the chart instance
// later in the same way.
const { teleportedSlots, patchOption } = useSlotOption(slots, () => {
if (!manualUpdate.value && props.option && chart.value) {
chart.value.setOption(
patchOption(props.option),
realUpdateOptions.value,
);
}
});
// We are converting all `on<Event>` props and collect them into `listeners` so that
// we can bind them to the chart instance later.
// For `onNative:<event>` props, we just strip the `Native:` part and collect them into
// `nativeListeners` so that we can bind them to the root element directly.
Object.keys(attrs)
.filter(key => isOn(key))
.forEach(key => {
// onClick -> c + lick
// onZr:click -> z + r:click
let event = key.charAt(2).toLowerCase() + key.slice(3);
.filter((key) => isOn(key))
.forEach((key) => {
// Collect native DOM events
if (event.indexOf("native:") === 0) {
// native:click -> onClick
const nativeKey = `on${event.charAt(7).toUpperCase()}${event.slice(
8
)}`;
if (key.indexOf("Native:") === 2) {
// onNative:click -> onClick
const nativeKey = `on${key.charAt(9).toUpperCase()}${key.slice(10)}`;
nativeListeners[nativeKey] = attrs[key];
return;
}
// clickOnce -> ~click
// zr:clickOnce -> ~zr:click
if (event.substring(event.length - 4) === "Once") {
event = `~${event.substring(0, event.length - 4)}`;
// onClick -> c + lick
// onZr:click -> z + r:click
let event = key.charAt(2).toLowerCase() + key.slice(3);
let zr: boolean | undefined;
if (event.indexOf("zr:") === 0) {
zr = true;
event = event.substring(3);
}
realListeners[event] = attrs[key];
});
} else {
// This is for Vue 2.
// We just need to distinguish normal events and `native:<event>` events and
// collect them into `realListeners` and `nativeListeners` respectively.
// For `native:<event>` events, we just strip the `native:` part and collect them
// into `nativeListeners` so that we can bind them to the root element directly.
// native:click -> click
// ~native:click -> ~click
// &~!native:click -> &~!click
Object.keys(listeners).forEach(key => {
if (NATIVE_EVENT_RE.test(key)) {
nativeListeners[key.replace(NATIVE_EVENT_RE, "$1")] = listeners[key];
} else {
realListeners[key] = listeners[key];
let once: boolean | undefined;
if (event.substring(event.length - 4) === "Once") {
once = true;
event = event.substring(0, event.length - 4);
}
listeners.set({ event, zr, once }, attrs[key]);
});
}
function init(option?: Option) {
if (!root.value) {
@ -164,35 +148,21 @@ export default defineComponent({
const instance = (chart.value = initChart(
root.value,
realTheme.value,
realInitOptions.value
realInitOptions.value,
));
if (props.group) {
instance.group = props.group;
}
Object.keys(realListeners).forEach(key => {
let handler = realListeners[key];
listeners.forEach((handler, { zr, once, event }) => {
if (!handler) {
return;
}
let event = key.toLowerCase();
if (event.charAt(0) === "~") {
event = event.substring(1);
handler.__once__ = true;
}
let target: EventTarget = instance;
if (event.indexOf("zr:") === 0) {
target = instance.getZr();
event = event.substring(3);
}
if (handler.__once__) {
delete handler.__once__;
const target = zr ? instance.getZr() : instance;
if (once) {
const raw = handler;
handler = (...args: any[]) => {
@ -216,7 +186,7 @@ export default defineComponent({
function commit() {
const opt = option || realOption.value;
if (opt) {
instance.setOption(opt, realUpdateOptions.value);
instance.setOption(patchOption(opt), realUpdateOptions.value);
}
}
@ -231,8 +201,14 @@ export default defineComponent({
commit();
}
}
const setOption: SetOptionType = (
option,
notMerge,
lazyUpdate?: boolean,
) => {
const updateOptions =
typeof notMerge === "boolean" ? { notMerge, lazyUpdate } : notMerge;
function setOption(option: Option, updateOptions?: UpdateOptions) {
if (props.manualUpdate) {
manualOption.value = option;
}
@ -240,9 +216,9 @@ export default defineComponent({
if (!chart.value) {
init(option);
} else {
chart.value.setOption(option, updateOptions || {});
}
chart.value.setOption(patchOption(option), updateOptions);
}
};
function cleanup() {
if (chart.value) {
@ -254,7 +230,7 @@ export default defineComponent({
let unwatchOption: (() => void) | null = null;
watch(
manualUpdate,
manualUpdate => {
(manualUpdate) => {
if (typeof unwatchOption === "function") {
unwatchOption();
unwatchOption = null;
@ -270,32 +246,42 @@ export default defineComponent({
if (!chart.value) {
init();
} else {
chart.value.setOption(option, {
chart.value.setOption(patchOption(option), {
// mutating `option` will lead to `notMerge: false` and
// replacing it with new reference will lead to `notMerge: true`
notMerge: option !== oldOption,
...realUpdateOptions.value
...realUpdateOptions.value,
});
}
},
{ deep: true }
{ deep: true },
);
}
},
{
immediate: true
}
immediate: true,
},
);
watch(
[realTheme, realInitOptions],
realInitOptions,
() => {
cleanup();
init();
},
{
deep: true
}
deep: true,
},
);
watch(
realTheme,
(theme) => {
chart.value?.setTheme(theme);
},
{
deep: true,
},
);
watchEffect(() => {
@ -326,25 +312,27 @@ export default defineComponent({
}
});
return {
chart,
root,
const exposed = {
setOption,
nonEventAttrs,
nativeListeners,
...publicApi
root,
chart,
};
expose({ ...exposed, ...publicApi });
// While `expose()` exposes methods and properties to the parent component
// via template refs at runtime, it doesn't contribute to TypeScript types.
// This type casting ensures TypeScript correctly types the exposed members
// that will be available when using this component.
return (() =>
h(
TAG_NAME,
{
...nonEventAttrs.value,
...nativeListeners,
ref: root,
class: ["echarts", nonEventAttrs.value.class],
},
teleportedSlots(),
)) as unknown as typeof exposed & PublicMethods;
},
render() {
// Vue 3 and Vue 2 have different vnode props format:
// See https://v3-migration.vuejs.org/breaking-changes/render-function-api.html#vnode-props-format
const attrs = (
Vue2
? { attrs: this.nonEventAttrs, on: this.nativeListeners }
: { ...this.nonEventAttrs, ...this.nativeListeners }
) as any;
attrs.ref = "root";
attrs.class = attrs.class ? ["echarts"].concat(attrs.class) : "echarts";
return h(TAG_NAME, attrs);
}
});

View File

@ -1,5 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { Ref } from "vue-demi";
import type { Ref } from "vue";
import type { EChartsType } from "../types";
const METHOD_NAMES = [
@ -17,18 +16,18 @@ const METHOD_NAMES = [
"appendData",
"clear",
"isDisposed",
"dispose"
"dispose",
] as const;
type MethodName = (typeof METHOD_NAMES)[number];
type PublicMethods = Pick<EChartsType, MethodName>;
export type PublicMethods = Pick<EChartsType, MethodName>;
export function usePublicAPI(
chart: Ref<EChartsType | undefined>
chart: Ref<EChartsType | undefined>,
): PublicMethods {
function makePublicMethod<T extends MethodName>(
name: T
name: T,
): (...args: Parameters<EChartsType[T]>) => ReturnType<EChartsType[T]> {
return (...args) => {
if (!chart.value) {
@ -40,7 +39,7 @@ export function usePublicAPI(
function makePublicMethods(): PublicMethods {
const methods = Object.create(null);
METHOD_NAMES.forEach(name => {
METHOD_NAMES.forEach((name) => {
methods[name] = makePublicMethod(name);
});

View File

@ -1,13 +1,13 @@
import { watch } from "vue-demi";
import { watch } from "vue";
import { throttle } from "echarts/core";
import type { Ref, PropType } from "vue-demi";
import type { Ref, PropType } from "vue";
import type { EChartsType, AutoResize } from "../types";
export function useAutoresize(
chart: Ref<EChartsType | undefined>,
autoresize: Ref<AutoResize | undefined>,
root: Ref<HTMLElement | undefined>
root: Ref<HTMLElement | undefined>,
): void {
watch(
[root, chart, autoresize],
@ -57,10 +57,10 @@ export function useAutoresize(
ro = null;
}
});
}
},
);
}
export const autoresizeProps = {
autoresize: [Boolean, Object] as PropType<AutoResize>
autoresize: [Boolean, Object] as PropType<AutoResize>,
};

View File

@ -1,3 +1,4 @@
export * from "./api";
export * from "./autoresize";
export * from "./loading";
export * from "./slot";

View File

@ -1,23 +1,24 @@
import { unwrapInjected } from "../utils";
import { inject, computed, watchEffect } from "vue-demi";
import { inject, computed, watchEffect, toValue } from "vue";
import type { Ref, InjectionKey, PropType } from "vue-demi";
import type { EChartsType, LoadingOptions } from "../types";
import type { Ref, InjectionKey, PropType } from "vue";
import type {
EChartsType,
LoadingOptions,
LoadingOptionsInjection,
} from "../types";
export const LOADING_OPTIONS_KEY =
"ecLoadingOptions" as unknown as InjectionKey<
LoadingOptions | Ref<LoadingOptions>
>;
export const LOADING_OPTIONS_KEY: InjectionKey<LoadingOptionsInjection> =
Symbol();
export function useLoading(
chart: Ref<EChartsType | undefined>,
loading: Ref<boolean>,
loadingOptions: Ref<LoadingOptions | undefined>
loading: Ref<boolean | undefined>,
loadingOptions: Ref<LoadingOptions | undefined>,
): void {
const defaultLoadingOptions = inject(LOADING_OPTIONS_KEY, {});
const realLoadingOptions = computed(() => ({
...unwrapInjected(defaultLoadingOptions, {}),
...loadingOptions?.value
...toValue(defaultLoadingOptions),
...loadingOptions?.value,
}));
watchEffect(() => {
@ -36,5 +37,5 @@ export function useLoading(
export const loadingProps = {
loading: Boolean,
loadingOptions: Object as PropType<LoadingOptions>
loadingOptions: Object as PropType<LoadingOptions>,
};

146
src/composables/slot.ts Normal file
View File

@ -0,0 +1,146 @@
import {
h,
Teleport,
onUpdated,
onUnmounted,
onMounted,
shallowRef,
shallowReactive,
warn,
} from "vue";
import type { Slots, SlotsType } from "vue";
import type { Option } from "../types";
import { isValidArrayIndex, isSameSet } from "../utils";
import type { TooltipComponentFormatterCallbackParams } from "echarts";
const SLOT_OPTION_PATHS = {
tooltip: ["tooltip", "formatter"],
dataView: ["toolbox", "feature", "dataView", "optionToContent"],
} as const;
type SlotPrefix = keyof typeof SLOT_OPTION_PATHS;
type SlotName = SlotPrefix | `${SlotPrefix}-${string}`;
type SlotRecord<T> = Partial<Record<SlotName, T>>;
const SLOT_PREFIXES = Object.keys(SLOT_OPTION_PATHS) as SlotPrefix[];
function isValidSlotName(key: string): key is SlotName {
return SLOT_PREFIXES.some(
(slotPrefix) => key === slotPrefix || key.startsWith(slotPrefix + "-"),
);
}
export function useSlotOption(slots: Slots, onSlotsChange: () => void) {
const detachedRoot =
typeof window !== "undefined" ? document.createElement("div") : undefined;
const containers = shallowReactive<SlotRecord<HTMLElement>>({});
const initialized = shallowReactive<SlotRecord<boolean>>({});
const params = shallowReactive<SlotRecord<unknown>>({});
const isMounted = shallowRef(false);
// Teleport the slots to a detached root
const teleportedSlots = () => {
// Make slots client-side only to avoid SSR hydration mismatch
return isMounted.value
? h(
Teleport,
{ to: detachedRoot },
Object.entries(slots)
.filter(([key]) => isValidSlotName(key))
.map(([key, slot]) => {
const slotName = key as SlotName;
const slotContent = initialized[slotName]
? slot?.(params[slotName])
: undefined;
return h(
"div",
{
ref: (el) => (containers[slotName] = el as HTMLElement),
style: { display: "contents" },
},
slotContent,
);
}),
)
: undefined;
};
// Shallow-clone the option along the path and override the target callback
function patchOption(src: Option): Option {
const root = { ...src };
Object.keys(slots)
.filter((key) => {
const isValidSlot = isValidSlotName(key);
if (!isValidSlot) {
warn(`Invalid vue-echarts slot name: ${key}`);
}
return isValidSlot;
})
.forEach((key) => {
const path = key.split("-");
const prefix = path.shift() as SlotPrefix;
path.push(...SLOT_OPTION_PATHS[prefix]);
let cur: any = root;
for (let i = 0; i < path.length - 1; i++) {
const seg = path[i];
const next = cur[seg];
// Shallow-clone the link; create empty shell if missing
cur[seg] = next
? Array.isArray(next)
? [...next]
: { ...next }
: isValidArrayIndex(seg)
? []
: {};
cur = cur[seg];
}
cur[path[path.length - 1]] = (p: unknown) => {
initialized[key] = true;
params[key] = p;
return containers[key];
};
});
return root;
}
// `slots` is not reactive, so we need to watch it manually
let slotNames: SlotName[] = [];
onUpdated(() => {
const newSlotNames = Object.keys(slots).filter(isValidSlotName);
if (!isSameSet(newSlotNames, slotNames)) {
// Clean up states for removed slots
slotNames.forEach((key) => {
if (!newSlotNames.includes(key)) {
delete params[key];
delete initialized[key];
delete containers[key];
}
});
slotNames = newSlotNames;
onSlotsChange();
}
});
onMounted(() => {
isMounted.value = true;
});
onUnmounted(() => {
detachedRoot?.remove();
});
return {
teleportedSlots,
patchOption,
};
}
export type SlotsTypes = SlotsType<
Record<
"tooltip" | `tooltip-${string}`,
TooltipComponentFormatterCallbackParams
> &
Record<"dataView" | `dataView-${string}`, Option>
>;

View File

@ -1,6 +0,0 @@
{
"files": [
"./Demo.vue",
"./examples/RadarChart.vue"
],
}

View File

@ -3,5 +3,5 @@ import ECharts, * as exported from "./index";
export default {
...ECharts,
...exported
...exported,
};

75
src/index.d.ts vendored
View File

@ -1,75 +0,0 @@
/* eslint-disable @typescript-eslint/ban-types */
import type { Ref, DefineComponent, InjectionKey } from "vue-demi";
import type {
Option,
Theme,
InitOptions,
UpdateOptions,
LoadingOptions,
AutoResize,
EChartsType,
Emits,
ThemeInjection,
InitOptionsInjection,
UpdateOptionsInjection,
LoadingOptionsInjection
} from "./types";
declare const THEME_KEY: InjectionKey<ThemeInjection>;
declare const INIT_OPTIONS_KEY: InjectionKey<InitOptionsInjection>;
declare const UPDATE_OPTIONS_KEY: InjectionKey<UpdateOptionsInjection>;
declare const LOADING_OPTIONS_KEY: InjectionKey<LoadingOptionsInjection>;
declare type ChartProps = {
theme?: Theme;
initOptions?: InitOptions;
updateOptions?: UpdateOptions;
loadingOptions?: LoadingOptions;
option?: Option;
autoresize?: AutoResize;
loading?: boolean;
group?: string;
manualUpdate?: boolean;
};
// convert Emits to Props
// click => onClick
declare type ChartEventProps = {
[key in keyof Emits as key extends string
? `on${Capitalize<key>}`
: never]?: Emits[key];
};
type MethodNames =
| "getWidth"
| "getHeight"
| "getDom"
| "getOption"
| "resize"
| "dispatchAction"
| "convertToPixel"
| "convertFromPixel"
| "containPixel"
| "getDataURL"
| "getConnectedDataURL"
| "appendData"
| "clear"
| "isDisposed"
| "dispose"
| "setOption";
declare type ChartMethods = Pick<EChartsType, MethodNames>;
declare const Chart: DefineComponent<
ChartProps & ChartEventProps,
{
root: Ref<HTMLElement | undefined>;
chart: Ref<EChartsType | undefined>;
},
{},
{},
ChartMethods
>;
export default Chart;
export { THEME_KEY, INIT_OPTIONS_KEY, UPDATE_OPTIONS_KEY, LOADING_OPTIONS_KEY };

View File

@ -1 +1 @@
x-vue-echarts{display:block;width:100%;height:100%;min-width:0}
x-vue-echarts{display:block;width:100%;height:100%;min-width:0;}

16
src/style.ts Normal file
View File

@ -0,0 +1,16 @@
import cssRules from "./style.css?raw";
if (typeof document !== "undefined") {
if (
Array.isArray(document.adoptedStyleSheets) &&
"replaceSync" in CSSStyleSheet.prototype
) {
const sheet = new CSSStyleSheet();
sheet.replaceSync(cssRules);
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
} else {
const styleEl = document.createElement("style");
styleEl.textContent = cssRules;
document.head.appendChild(styleEl);
}
}

View File

@ -1,26 +1,22 @@
import { init } from "echarts/core";
import type { SetOptionOpts, ECElementEvent, ElementEvent } from "echarts/core";
import type { Ref } from "vue-demi";
import type { MaybeRefOrGetter } from "vue";
export type Injection<T> = T | null | Ref<T | null> | { value: T | null };
export type Injection<T> = MaybeRefOrGetter<T | null>;
type InitType = typeof init;
export type InitParameters = Parameters<InitType>;
export type Theme = NonNullable<InitParameters[1]>;
export type ThemeInjection = Injection<Theme>;
export type InitOptions = NonNullable<InitParameters[2]>;
export type InitOptionsInjection = Injection<InitOptions>;
export type UpdateOptions = SetOptionOpts;
export type UpdateOptionsInjection = Injection<UpdateOptions>;
export type EChartsType = ReturnType<InitType>;
type ZRenderType = ReturnType<EChartsType["getZr"]>;
export type EventTarget = EChartsType | ZRenderType;
type SetOptionType = EChartsType["setOption"];
export type SetOptionType = EChartsType["setOption"];
export type Option = Parameters<SetOptionType>[0];
export type AutoResize =

View File

@ -1,11 +1,4 @@
import { unref, isRef } from "vue-demi";
import type { Injection } from "./types";
type Attrs = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[key: string]: any;
};
type Attrs = Record<string, any>;
// Copied from
// https://github.com/vuejs/vue-next/blob/5a7a1b8293822219283d6e267496bec02234b0bc/packages/shared/src/index.ts#L40-L41
@ -23,15 +16,25 @@ export function omitOn(attrs: Attrs): Attrs {
return result;
}
export function unwrapInjected<T, V>(
injection: Injection<T>,
defaultValue: V
): T | V {
const value = isRef(injection) ? unref(injection) : injection;
if (value && typeof value === "object" && "value" in value) {
return value.value || defaultValue;
export function isValidArrayIndex(key: string): boolean {
const num = Number(key);
return (
Number.isInteger(num) &&
num >= 0 &&
num < Math.pow(2, 32) - 1 &&
String(num) === key
);
}
return value || defaultValue;
export function isSameSet<T>(a: T[], b: T[]): boolean {
const setA = new Set(a);
const setB = new Set(b);
if (setA.size !== setB.size) return false;
for (const val of setA) {
if (!setB.has(val)) return false;
}
return true;
}

View File

@ -19,19 +19,20 @@ export function register(): boolean {
}
try {
// Class definitions cannot be transpiled to ES5
// so we are doing a little trick here to ensure
// we are using native classes. As we use this as
// a progressive enhancement, it will be fine even
// if the browser doesn't support native classes.
const reg = new Function(
"tag",
// Use esbuild repl to keep build process simple
// https://esbuild.github.io/try/#dAAwLjIzLjAALS1taW5pZnkAY2xhc3MgRUNoYXJ0c0VsZW1lbnQgZXh0ZW5kcyBIVE1MRWxlbWVudCB7CiAgX19kaXNwb3NlID0gbnVsbDsKCiAgZGlzY29ubmVjdGVkQ2FsbGJhY2soKSB7CiAgICBpZiAodGhpcy5fX2Rpc3Bvc2UpIHsKICAgICAgdGhpcy5fX2Rpc3Bvc2UoKTsKICAgICAgdGhpcy5fX2Rpc3Bvc2UgPSBudWxsOwogICAgfQogIH0KfQoKaWYgKGN1c3RvbUVsZW1lbnRzLmdldCh0YWcpID09IG51bGwpIHsKICBjdXN0b21FbGVtZW50cy5kZWZpbmUodGFnLCBFQ2hhcnRzRWxlbWVudCk7Cn0K
"class EChartsElement extends HTMLElement{__dispose=null;disconnectedCallback(){this.__dispose&&(this.__dispose(),this.__dispose=null)}}customElements.get(tag)==null&&customElements.define(tag,EChartsElement);"
);
reg(TAG_NAME);
} catch (e) {
class ECElement extends HTMLElement implements EChartsElement {
__dispose: (() => void) | null = null;
disconnectedCallback() {
if (this.__dispose) {
this.__dispose();
this.__dispose = null;
}
}
}
if (customElements.get(TAG_NAME) == null) {
customElements.define(TAG_NAME, ECElement);
}
} catch {
return (registered = false);
}

View File

@ -1,27 +1,25 @@
{
"allowJs": true,
"compilerOptions": {
"target": "ES5",
"target": "ESNext",
"module": "ESNext",
"strict": true,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"moduleResolution": "bundler",
"removeComments": true,
"skipLibCheck": false,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"noEmit": true,
"baseUrl": ".",
"types": ["vite/client"],
"lib": ["ESNext", "DOM", "DOM.Iterable", "ScriptHost"]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"shims-vue.d.ts",
"src/demo/**/*.ts",
"src/demo/**/*.vue"
],
"exclude": ["node_modules"]
"include": ["src/**/*.ts", "src/**/*.tsx"],
"exclude": ["node_modules"],
"references": [
{
"path": "./tsconfig.node.json"
}
]
}

13
tsconfig.node.json Normal file
View File

@ -0,0 +1,13 @@
{
"include": ["*.config.*", "scripts/**/*"],
"compilerOptions": {
"composite": true,
"lib": ["ESNext"],
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "Bundler",
"types": ["node"]
}
}

32
tsdown.config.ts Normal file
View File

@ -0,0 +1,32 @@
import { defineConfig } from "tsdown";
import raw from "unplugin-raw/rollup";
export default defineConfig([
{
entry: "src/index.ts",
platform: "browser",
sourcemap: true,
copy: ["src/style.css"],
plugins: [raw()],
},
{
entry: "src/global.ts",
outputOptions: {
file: "dist/index.min.js", // for unpkg/jsdelivr
dir: undefined,
format: "umd",
name: "VueECharts",
exports: "default",
globals: {
vue: "Vue",
echarts: "echarts",
"echarts/core": "echarts",
},
},
platform: "browser",
sourcemap: true,
minify: true,
dts: false,
plugins: [raw()],
},
]);

5
vercel.json Normal file
View File

@ -0,0 +1,5 @@
{
"$schema": "https://openapi.vercel.sh/vercel.json",
"buildCommand": "pnpm run dev:build",
"outputDirectory": "demo/dist"
}

17
vite.config.ts Normal file
View File

@ -0,0 +1,17 @@
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import postcssNested from "postcss-nested";
// https://vite.dev/config/
export default defineConfig({
plugins: [vue()],
root: "./demo",
server: {
host: true,
},
css: {
postcss: {
plugins: [postcssNested()],
},
},
});

View File

@ -1,41 +0,0 @@
import nested from "postcss-nested";
export default {
outputDir: "demo",
css: {
loaderOptions: {
postcss: {
postcssOptions: {
plugins: [nested()]
}
}
}
},
chainWebpack: config => {
config.entry("app").clear().add("./src/demo/main.ts");
config.module
.rule("svg")
.clear()
.test(/\.svg$/)
.type("asset/source");
config.module
.rule("wasm")
.test(/\.wasm$/)
.type("asset/resource")
.set("generator", {
filename: "[name].[hash:8][ext]"
});
config.plugin("define").tap(([options]) => [
{
...options,
__CSP__: "false"
}
]);
},
devServer: {
allowedHosts: "all"
}
};