Compare commits
82 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 36c3cb97a9 | |||
| 55c68b48b7 | |||
| c232e71c47 | |||
| e568005bb2 | |||
| 8ed975e09b | |||
| 570a26c262 | |||
| df640ebce6 | |||
| 30e7934aab | |||
| 6155bbb409 | |||
| fa42af0723 | |||
| 8b7ef5e6e1 | |||
| 522dd7cc5c | |||
| 077bd3ec40 | |||
| 473fed37a2 | |||
| 7ae6892fe6 | |||
| 71c106ae29 | |||
| 381489da2f | |||
| 2fb0dc2233 | |||
| b6c84aab7e | |||
| c6a1228c9d | |||
| 9067505a3a | |||
| d0357c8f31 | |||
| cde7c33a32 | |||
| 963c204da4 | |||
| bee810ead3 | |||
| 1301014626 | |||
| b7852ab643 | |||
| 1378f6e397 | |||
| ec2c15c153 | |||
| beec26367f | |||
| b366b31afb | |||
| 4f3a6ac0a3 | |||
| e967bfc1fe | |||
| a9c54e1dc5 | |||
| d941e35a61 | |||
| 3c063ed21e | |||
| c0b0d2d0be | |||
| 2d310a3891 | |||
| 9fe76efa93 | |||
| 24ed18e439 | |||
| 6b98318504 | |||
| 27c79b9012 | |||
| 861674352b | |||
| fc53aee8a7 | |||
| da0109b07a | |||
| 7e5c06225c | |||
| 3c7ff95331 | |||
| d774558f3e | |||
| 5d23006866 | |||
| 443eaa0367 | |||
| 16f3f4b419 | |||
| e651d32334 | |||
| a8e30b137a | |||
| 09808a47d2 | |||
| 898195c770 | |||
| 07e0d067d5 | |||
| 185940aecb | |||
| def674bc6c | |||
| feb4f03587 | |||
| 5cdff0b4f9 | |||
| 29ff9bc52a | |||
| 54d196c9d5 | |||
| 34defec669 | |||
| e189abd9c2 | |||
| f07855df08 | |||
| ae3102a86c | |||
| 48a2507ef8 | |||
| 311d588850 | |||
| 2f1910ec87 | |||
| 48a0664c2b | |||
| 05ec40a956 | |||
| dc9dccbf05 | |||
| 476efa4209 | |||
| 7bcf57e77b | |||
| ce3ba29209 | |||
| cb95ece9c6 | |||
| 2a0db561b4 | |||
| 2b30334b82 | |||
| dbd6699a4f | |||
| 2130bd6444 | |||
| e7eb680d46 | |||
| b3a10f3654 |
@ -1,3 +0,0 @@
|
||||
> 1%
|
||||
last 2 versions
|
||||
not dead
|
||||
@ -1 +0,0 @@
|
||||
dist
|
||||
25
.eslintrc.js
@ -1,25 +0,0 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
node: true
|
||||
},
|
||||
extends: ["plugin:vue/vue3-essential", "eslint:recommended", "@vue/prettier"],
|
||||
parserOptions: {
|
||||
ecmaVersion: 2020,
|
||||
parser: "@typescript-eslint/parser"
|
||||
},
|
||||
rules: {
|
||||
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
|
||||
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
|
||||
"vue/multi-word-component-names": "off"
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ["*.ts"],
|
||||
extends: [
|
||||
"@vue/typescript/recommended",
|
||||
"@vue/prettier/@typescript-eslint"
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
37
.github/ISSUE_TEMPLATE.md
vendored
@ -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))**
|
||||
2
.github/ISSUE_TEMPLATE/bug-report.en-US.yml
vendored
@ -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
|
||||
|
||||
@ -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
@ -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
@ -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
@ -22,4 +22,4 @@ pnpm-debug.log*
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
/demo
|
||||
/demo/dist
|
||||
|
||||
3
.prettierignore
Normal file
@ -0,0 +1,3 @@
|
||||
pnpm-lock.yaml
|
||||
demo/data/*.json
|
||||
src/style.css
|
||||
@ -1,4 +0,0 @@
|
||||
{
|
||||
"trailingComma": "none",
|
||||
"arrowParens": "avoid"
|
||||
}
|
||||
@ -1,4 +0,0 @@
|
||||
{
|
||||
"installDependencies": true,
|
||||
"startCommand": "pnpm serve"
|
||||
}
|
||||
381
CHANGELOG.md
@ -1,399 +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).
|
||||
|
||||
## 7.0.2
|
||||
|
||||
- Fixed style injection regression (#805).
|
||||
|
||||
## 7.0.1
|
||||
|
||||
- Fixed type for `autoresize`.
|
||||
|
||||
## 7.0.0
|
||||
|
||||
> Other prerelease changes:
|
||||
>
|
||||
> - [7.0.0-beta.0](#700-beta0)
|
||||
|
||||
- Fixed types for events.
|
||||
|
||||
## 7.0.0-beta.0
|
||||
|
||||
- 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.
|
||||
|
||||
## 6.7.3
|
||||
|
||||
- Fixed that `padding` on the component root doesn't work.
|
||||
|
||||
## 6.7.2
|
||||
|
||||
- Fixed that charts inside `<keep-alive>` failed to display after activation.
|
||||
|
||||
## 6.7.1
|
||||
|
||||
- Fixed that native events won't actually trigger.
|
||||
|
||||
## 6.7.0
|
||||
|
||||
- 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.
|
||||
|
||||
## 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)).
|
||||
|
||||
## 6.6.8
|
||||
|
||||
- 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.
|
||||
|
||||
## 6.6.6
|
||||
|
||||
- Fixed types for Vue < 2.7.
|
||||
|
||||
## 6.6.5
|
||||
|
||||
- Fixed type for `option` regressed in v6.6.2.
|
||||
|
||||
## 6.6.4
|
||||
|
||||
- Fixed style regression introduced by v6.6.3.
|
||||
|
||||
## 6.6.3
|
||||
|
||||
- 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.
|
||||
|
||||
470
README.md
@ -1,50 +1,25 @@
|
||||
<h1 align="center">Vue-ECharts</h1>
|
||||
|
||||
<p align="center">Vue.js <sup>(v2/v3)</sup> component for Apache ECharts <sup>(v5)</sup>.</p>
|
||||
<p align="center"><a href="https://vue-echarts.dev/">View Demo →</a></p>
|
||||
<p align="center"><a href="https:///pr.new/ecomfe/vue-echarts"><img alt="Open in Codeflow" src="https://developer.stackblitz.com/img/open_in_codeflow.svg" height="28"/></a> <a href="https://codesandbox.io/p/github/ecomfe/vue-echarts"><img alt="Edit in CodeSandbox" src="https://assets.codesandbox.io/github/button-edit-lime.svg" height="28"/></a></p>
|
||||
|
||||
> [!IMPORTANT]
|
||||
> We have released an [import code generator](https://vue-echarts.dev/#codegen) that can generate precise import code by pasting the `option` code.
|
||||
>
|
||||
> 
|
||||
>
|
||||
> [Try it →](https://vue-echarts.dev/#codegen)
|
||||
<p align="center">Vue.js component for Apache ECharts™.</p>
|
||||
<p align="center"><a href="https://npmjs.com/package/vue-echarts"><img alt="npm version" src="https://img.shields.io/npm/v/vue-echarts"></a> <a href="https://vue-echarts.dev/"><img src="https://img.shields.io/badge/Demo%20%C2%BB-20c3aa" alt="View demo"></a> <a href="./README.zh-Hans.md"><img src="https://img.shields.io/badge/%E4%B8%AD%E6%96%87%E7%89%88%20%C2%BB-000" alt="前往中文版"></a></p>
|
||||
<p align="center"><a href="https:///pr.new/ecomfe/vue-echarts"><img alt="Open in Codeflow" src="https://developer.stackblitz.com/img/open_in_codeflow.svg" height="28"></a> <a href="https://codesandbox.io/p/github/ecomfe/vue-echarts"><img alt="Edit in CodeSandbox" src="https://assets.codesandbox.io/github/button-edit-lime.svg" height="28"></a></p>
|
||||
|
||||
---
|
||||
|
||||
<h2>💡 Heads up 💡 <a href="./README.zh-Hans.md"><img src="https://img.shields.io/badge/%F0%9F%87%A8%F0%9F%87%B3-%E4%B8%AD%E6%96%87%E7%89%88-white?labelColor=white" alt="前往中文版" align="right" height="24"/></a></h2>
|
||||
|
||||
If you are migrating from `vue-echarts` ≤ 5, you should read the _[Migration to v6](#migration-to-v6)_ section before you update to v6.
|
||||
|
||||
Not ready yet? Read documentation for older versions [here →](https://github.com/ecomfe/vue-echarts/tree/5.x)
|
||||
> Still using Vue 2? Read v7 docs [here →](https://github.com/ecomfe/vue-echarts/tree/7.x)
|
||||
|
||||
## Installation & Usage
|
||||
|
||||
### npm & ESM
|
||||
### npm
|
||||
|
||||
```bash
|
||||
```sh
|
||||
npm install echarts vue-echarts
|
||||
```
|
||||
|
||||
To make `vue-echarts` work for _Vue 2_ (<2.7.0), you need to have `@vue/composition-api` installed:
|
||||
|
||||
```sh
|
||||
npm i @vue/composition-api
|
||||
```
|
||||
|
||||
If you are using _NuxtJS_ on top of _Vue 2_, you'll also need `@nuxtjs/composition-api`:
|
||||
|
||||
```sh
|
||||
npm i @nuxtjs/composition-api
|
||||
```
|
||||
|
||||
And then add `'@nuxtjs/composition-api/module'` in the `buildModules` option in your `nuxt.config.js`.
|
||||
|
||||
#### 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>
|
||||
@ -58,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";
|
||||
@ -68,7 +43,7 @@ use([
|
||||
PieChart,
|
||||
TitleComponent,
|
||||
TooltipComponent,
|
||||
LegendComponent
|
||||
LegendComponent,
|
||||
]);
|
||||
|
||||
provide(THEME_KEY, "dark");
|
||||
@ -76,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: [
|
||||
{
|
||||
@ -98,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>
|
||||
|
||||
@ -121,103 +96,11 @@ 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.
|
||||
>
|
||||
> 
|
||||
>
|
||||
> [Try it →](https://vue-echarts.dev/#codegen)
|
||||
|
||||
But if you really want to import the whole ECharts bundle without having to import modules manually, just add this in your code:
|
||||
@ -226,19 +109,21 @@ But if you really want to import the whole ECharts bundle without having to impo
|
||||
import "echarts";
|
||||
```
|
||||
|
||||
### CDN & Global variable
|
||||
### CDN
|
||||
|
||||
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.3.7"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue-echarts@6.6.1"></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
|
||||
@ -250,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.15"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue-echarts@6.6.1"></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
|
||||
|
||||
@ -288,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`
|
||||
|
||||
@ -318,89 +186,6 @@ See more examples [here](https://github.com/ecomfe/vue-echarts/tree/main/src/dem
|
||||
|
||||
For performance critical scenarios (having a large dataset) we'd better bypass Vue's reactivity system for `option` prop. By specifying `manual-update` prop with `true` and not providing `option` prop, the dataset won't be watched any more. After doing so, you need to retrieve the component instance with `ref` and manually call `setOption` method to update the chart.
|
||||
|
||||
### 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:
|
||||
|
||||
<details>
|
||||
<summary>Vue 3</summary>
|
||||
|
||||
```js
|
||||
import { THEME_KEY } from 'vue-echarts'
|
||||
import { provide } from 'vue'
|
||||
|
||||
// composition API
|
||||
provide(THEME_KEY, 'dark')
|
||||
|
||||
// options API
|
||||
{
|
||||
provide: {
|
||||
[THEME_KEY]: 'dark'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Vue 2</summary>
|
||||
|
||||
```js
|
||||
import { THEME_KEY } from 'vue-echarts'
|
||||
|
||||
// in component options
|
||||
{
|
||||
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
|
||||
> }
|
||||
> }
|
||||
> }
|
||||
> ```
|
||||
|
||||
</details>
|
||||
|
||||
### Methods
|
||||
|
||||
- `setOption` [→](https://echarts.apache.org/en/api.html#echartsInstance.setOption)
|
||||
- `getWidth` [→](https://echarts.apache.org/en/api.html#echartsInstance.getWidth)
|
||||
- `getHeight` [→](https://echarts.apache.org/en/api.html#echartsInstance.getHeight)
|
||||
- `getDom` [→](https://echarts.apache.org/en/api.html#echartsInstance.getDom)
|
||||
- `getOption` [→](https://echarts.apache.org/en/api.html#echartsInstance.getOption)
|
||||
- `resize` [→](https://echarts.apache.org/en/api.html#echartsInstance.resize)
|
||||
- `dispatchAction` [→](https://echarts.apache.org/en/api.html#echartsInstance.dispatchAction)
|
||||
- `convertToPixel` [→](https://echarts.apache.org/en/api.html#echartsInstance.convertToPixel)
|
||||
- `convertFromPixel` [→](https://echarts.apache.org/en/api.html#echartsInstance.convertFromPixel)
|
||||
- `containPixel` [→](https://echarts.apache.org/en/api.html#echartsInstance.containPixel)
|
||||
- `showLoading` [→](https://echarts.apache.org/en/api.html#echartsInstance.showLoading)
|
||||
- `hideLoading` [→](https://echarts.apache.org/en/api.html#echartsInstance.hideLoading)
|
||||
- `getDataURL` [→](https://echarts.apache.org/en/api.html#echartsInstance.getDataURL)
|
||||
- `getConnectedDataURL` [→](https://echarts.apache.org/en/api.html#echartsInstance.getConnectedDataURL)
|
||||
- `clear` [→](https://echarts.apache.org/en/api.html#echartsInstance.clear)
|
||||
- `dispose` [→](https://echarts.apache.org/en/api.html#echartsInstance.dispose)
|
||||
|
||||
### Static Methods
|
||||
|
||||
Static methods can be accessed from [`echarts` itself](https://echarts.apache.org/en/api.html#echarts).
|
||||
|
||||
### Events
|
||||
|
||||
You can bind events with Vue's `v-on` directive.
|
||||
@ -411,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:
|
||||
@ -463,46 +247,194 @@ Vue-ECharts support the following events:
|
||||
|
||||
See supported events [here →](https://echarts.apache.org/en/api.html#events)
|
||||
|
||||
## CSP: `style-src` or `style-src-elem`
|
||||
#### Native DOM Events
|
||||
|
||||
If you are applying a CSP to prevent inline `<style>` injection, you need to use files from `dist/csp` directory and include `dist/csp/style.css` into your app manually.
|
||||
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.
|
||||
|
||||
## Migration to v6
|
||||
```vue
|
||||
<template>
|
||||
<v-chart @native:click="handleClick" />
|
||||
</template>
|
||||
```
|
||||
|
||||
> 💡 Please make sure to read the [migration guide](https://echarts.apache.org/en/tutorial.html#ECharts%205%20Upgrade%20Guide) for ECharts 5 as well.
|
||||
### Provide / Inject
|
||||
|
||||
The following breaking changes are introduced in `vue-echarts@6`:
|
||||
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:
|
||||
|
||||
### Vue 2 support
|
||||
<details>
|
||||
<summary>Composition API</summary>
|
||||
|
||||
- If you are using version prior to `vue@2.7.0`, `@vue/composition-api` is required to be installed to use Vue-ECharts with Vue 2.
|
||||
```js
|
||||
import { THEME_KEY } from "vue-echarts";
|
||||
import { provide } from "vue";
|
||||
|
||||
### Props
|
||||
provide(THEME_KEY, "dark");
|
||||
|
||||
- `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.
|
||||
// or provide a ref
|
||||
const theme = ref("dark");
|
||||
provide(THEME_KEY, theme);
|
||||
|
||||
// getter is also supported
|
||||
provide(THEME_KEY, () => theme.value);
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Options API</summary>
|
||||
|
||||
```js
|
||||
import { THEME_KEY } from 'vue-echarts'
|
||||
import { computed } from 'vue'
|
||||
|
||||
export default {
|
||||
{
|
||||
provide: {
|
||||
[THEME_KEY]: 'dark'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Or make injections reactive
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
theme: 'dark'
|
||||
}
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
[THEME_KEY]: computed(() => this.theme)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Methods
|
||||
|
||||
- `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.
|
||||
- `setOption` [→](https://echarts.apache.org/en/api.html#echartsInstance.setOption)
|
||||
- `getWidth` [→](https://echarts.apache.org/en/api.html#echartsInstance.getWidth)
|
||||
- `getHeight` [→](https://echarts.apache.org/en/api.html#echartsInstance.getHeight)
|
||||
- `getDom` [→](https://echarts.apache.org/en/api.html#echartsInstance.getDom)
|
||||
- `getOption` [→](https://echarts.apache.org/en/api.html#echartsInstance.getOption)
|
||||
- `resize` [→](https://echarts.apache.org/en/api.html#echartsInstance.resize)
|
||||
- `dispatchAction` [→](https://echarts.apache.org/en/api.html#echartsInstance.dispatchAction)
|
||||
- `convertToPixel` [→](https://echarts.apache.org/en/api.html#echartsInstance.convertToPixel)
|
||||
- `convertFromPixel` [→](https://echarts.apache.org/en/api.html#echartsInstance.convertFromPixel)
|
||||
- `containPixel` [→](https://echarts.apache.org/en/api.html#echartsInstance.containPixel)
|
||||
- `getDataURL` [→](https://echarts.apache.org/en/api.html#echartsInstance.getDataURL)
|
||||
- `getConnectedDataURL` [→](https://echarts.apache.org/en/api.html#echartsInstance.getConnectedDataURL)
|
||||
- `clear` [→](https://echarts.apache.org/en/api.html#echartsInstance.clear)
|
||||
- `dispose` [→](https://echarts.apache.org/en/api.html#echartsInstance.dispose)
|
||||
|
||||
### Computed getters
|
||||
> [!NOTE]
|
||||
> 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.
|
||||
|
||||
- Computed getters (`width`, `height`, `isDisposed` and `computedOptions`) are removed. Use the **`getWidth`, `getHeight`, `isDisposed` and `getOption`** methods instead.
|
||||
### Slots
|
||||
|
||||
### Styles
|
||||
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.
|
||||
|
||||
- Now the root element of the component have **`100%×100%`** size by default, instead of `600×400`.
|
||||
**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
|
||||
|
||||
Static methods can be accessed from [`echarts` itself](https://echarts.apache.org/en/api.html#echarts).
|
||||
|
||||
## CSP: `style-src` or `style-src-elem`
|
||||
|
||||
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 v8
|
||||
|
||||
> [!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
|
||||
|
||||
```bash
|
||||
```sh
|
||||
pnpm i
|
||||
pnpm serve
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
Open `http://localhost:8080` to see the demo.
|
||||
Open `http://localhost:5173` to see the demo.
|
||||
|
||||
## Notice
|
||||
|
||||
The Apache Software Foundation [Apache ECharts, ECharts](https://echarts.apache.org/), Apache, the Apache feather, and the Apache ECharts project logo are either registered trademarks or trademarks of the [Apache Software Foundation](https://www.apache.org/).
|
||||
|
||||
@ -1,51 +1,25 @@
|
||||
<h1 align="center">Vue-ECharts</h1>
|
||||
|
||||
<p align="center">Apache ECharts <sup>(v5)</sup> 的 Vue.js <sup>(v2/v3)</sup> 组件。</p>
|
||||
<p align="center"><a href="https://vue-echarts.dev/">查看 Demo →</a></p>
|
||||
<p align="center"><a href="https:///pr.new/ecomfe/vue-echarts"><img alt="Open in Codeflow" src="https://developer.stackblitz.com/img/open_in_codeflow.svg" height="28"/></a> <a href="https://codesandbox.io/p/github/ecomfe/vue-echarts"><img alt="Edit in CodeSandbox" src="https://assets.codesandbox.io/github/button-edit-lime.svg" height="28"/></a></p>
|
||||
|
||||
|
||||
> [!IMPORTANT]
|
||||
> 我们新发布了一个[导入代码生成器](https://vue-echarts.dev/#codegen),只需要把`option` 代码粘贴进去,就可以得到精确的导入代码。
|
||||
>
|
||||
> 
|
||||
>
|
||||
> [试一试 →](https://vue-echarts.dev/#codegen)
|
||||
<p align="center">Apache ECharts™ 的 Vue.js 组件。</p>
|
||||
<p align="center"><a href="https://npmjs.com/package/vue-echarts"><img alt="npm 版本" src="https://img.shields.io/npm/v/vue-echarts"></a> <a href="https://vue-echarts.dev/"><img src="https://img.shields.io/badge/%E6%BC%94%E7%A4%BA%20%C2%BB-20c3aa" alt="查看演示"></a> <a href="./README.zh-Hans.md"></p>
|
||||
<p align="center"><a href="https:///pr.new/ecomfe/vue-echarts"><img alt="Open in Codeflow" src="https://developer.stackblitz.com/img/open_in_codeflow.svg" height="28"></a> <a href="https://codesandbox.io/p/github/ecomfe/vue-echarts"><img alt="Edit in CodeSandbox" src="https://assets.codesandbox.io/github/button-edit-lime.svg" height="28"></a></p>
|
||||
|
||||
---
|
||||
|
||||
## 💡 注意 💡
|
||||
|
||||
若您准备从 `vue-echarts` ≤ 5 的版本迁移到新版本,请在升级 v6 前阅读 _[迁移到 v6](#迁移到-v6)_ 部分文档。
|
||||
|
||||
没准备好的话,可以继续阅读老版本的文档。[前往 →](https://github.com/ecomfe/vue-echarts/blob/5.x/README.zh_CN.md)
|
||||
> 还在使用 Vue 2?可以继续阅读老版本的文档。[前往 →](https://github.com/ecomfe/vue-echarts/blob/7.x/README.zh-Hans.md)
|
||||
|
||||
## 安装 & 使用
|
||||
|
||||
### npm & ESM
|
||||
### npm
|
||||
|
||||
```bash
|
||||
```sh
|
||||
npm install echarts vue-echarts
|
||||
```
|
||||
|
||||
要在 _Vue 2_(<2.7.0)下使用 `vue-echarts`,需要确保 `@vue/composition-api` 已经安装:
|
||||
|
||||
```sh
|
||||
npm i @vue/composition-api
|
||||
```
|
||||
|
||||
如果你在使用基于 _Vue 2_ 的 _NuxtJS_,那么还需要安装 `@nuxtjs/composition-api`:
|
||||
|
||||
```sh
|
||||
npm i @nuxtjs/composition-api
|
||||
```
|
||||
|
||||
然后在 `nuxt.config.js` 的 `buildModules` 选项中添加 `'@nuxtjs/composition-api/module'`。
|
||||
|
||||
#### 示例
|
||||
|
||||
<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>
|
||||
@ -59,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";
|
||||
@ -69,7 +43,7 @@ use([
|
||||
PieChart,
|
||||
TitleComponent,
|
||||
TooltipComponent,
|
||||
LegendComponent
|
||||
LegendComponent,
|
||||
]);
|
||||
|
||||
provide(THEME_KEY, "dark");
|
||||
@ -77,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: [
|
||||
{
|
||||
@ -99,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>
|
||||
|
||||
@ -122,103 +96,11 @@ 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` 代码粘贴进去,就可以得到精确的导入代码。
|
||||
>
|
||||
> 
|
||||
>
|
||||
> [试一试 →](https://vue-echarts.dev/#codegen)
|
||||
|
||||
但如果你实在需要全量引入 ECharts 从而无需手动引入模块,只需要在代码中添加:
|
||||
@ -227,19 +109,21 @@ export default {
|
||||
import "echarts";
|
||||
```
|
||||
|
||||
### CDN & 全局变量
|
||||
### CDN
|
||||
|
||||
用如下方式在 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.3.7"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue-echarts@6.6.1"></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
|
||||
@ -251,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.15"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue-echarts@6.6.1"></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
|
||||
|
||||
@ -289,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`
|
||||
|
||||
@ -329,8 +196,7 @@ Vue.component("v-chart", VueECharts);
|
||||
</template>
|
||||
```
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> [!NOTE]
|
||||
> 仅支持 `.once` 修饰符,因为其它修饰符都与 DOM 事件机制强耦合。
|
||||
|
||||
Vue-ECharts 支持如下事件:
|
||||
@ -381,64 +247,69 @@ Vue-ECharts 支持如下事件:
|
||||
|
||||
请参考支持的事件列表。[前往 →](https://echarts.apache.org/zh/api.html#events)
|
||||
|
||||
#### 原生 DOM 事件
|
||||
|
||||
由于 Vue-ECharts 默认将事件绑定到 ECharts 实例,因此在使用原生 DOM 事件时需要做一些特殊处理。你需要在事件名称前加上 `native:` 前缀来绑定原生 DOM 事件。
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<v-chart @native:click="handleClick" />
|
||||
</template>
|
||||
```
|
||||
|
||||
### 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'
|
||||
|
||||
// 组件选项中
|
||||
{
|
||||
provide: {
|
||||
[THEME_KEY]: 'dark'
|
||||
export default {
|
||||
{
|
||||
provide: {
|
||||
[THEME_KEY]: 'dark'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 或者让注入项具有响应性
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
theme: 'dark'
|
||||
}
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
[THEME_KEY]: computed(() => this.theme)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> 在 Vue 2 中,如果你想动态地改变这些选项,那么你需要提供一个对象。
|
||||
>
|
||||
> ```js
|
||||
> // 组件选项中
|
||||
> {
|
||||
> data () {
|
||||
> return {
|
||||
> theme: { value: 'dark' }
|
||||
> }
|
||||
> },
|
||||
> provide () {
|
||||
> return {
|
||||
> [THEME_KEY]: this.theme
|
||||
> }
|
||||
> }
|
||||
> }
|
||||
> ```
|
||||
|
||||
</details>
|
||||
|
||||
### 方法
|
||||
@ -453,57 +324,117 @@ import { THEME_KEY } from 'vue-echarts'
|
||||
- `convertToPixel` [→](https://echarts.apache.org/zh/api.html#echartsInstance.convertToPixel)
|
||||
- `convertFromPixel` [→](https://echarts.apache.org/zh/api.html#echartsInstance.convertFromPixel)
|
||||
- `containPixel` [→](https://echarts.apache.org/zh/api.html#echartsInstance.containPixel)
|
||||
- `showLoading` [→](https://echarts.apache.org/zh/api.html#echartsInstance.showLoading)
|
||||
- `hideLoading` [→](https://echarts.apache.org/zh/api.html#echartsInstance.hideLoading)
|
||||
- `getDataURL` [→](https://echarts.apache.org/zh/api.html#echartsInstance.getDataURL)
|
||||
- `getConnectedDataURL` [→](https://echarts.apache.org/zh/api.html#echartsInstance.getConnectedDataURL)
|
||||
- `clear` [→](https://echarts.apache.org/zh/api.html#echartsInstance.clear)
|
||||
- `dispose` [→](https://echarts.apache.org/zh/api.html#echartsInstance.dispose)
|
||||
|
||||
> [!NOTE]
|
||||
> 如下 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>` 注入,则需要使用 `dist/csp` 目录中的文件,并手动引入 `dist/csp/style.css`。
|
||||
如果你执行严格的 CSP 策略来防止内联 `<style>` 注入,**并且**需要兼容不支持 [CSSStyleSheet() 构造函数](https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet/CSSStyleSheet#browser_compatibility) 的浏览器,则需要手动引入 `vue-echarts/style.css`。
|
||||
|
||||
## 迁移到 v6
|
||||
## 迁移到 v8
|
||||
|
||||
> 💡 请确保同时查阅 ECharts 5 的[升级指南](https://echarts.apache.org/zh/tutorial.html#ECharts%205%20%E5%8D%87%E7%BA%A7%E6%8C%87%E5%8D%97)。
|
||||
> [!NOTE]
|
||||
> 请确保同时查阅 [ECharts 6 的升级指南](https://echarts.apache.org/handbook/zh/basics/release-note/v6-upgrade-guide/)。
|
||||
|
||||
`vue-echarts@6` 引入了如下破坏性变更:
|
||||
`vue-echarts@8` 引入了以下破坏性变更:
|
||||
|
||||
### Vue 2 支持
|
||||
- **Vue 2 支持已移除:** 如果你仍需要继续使用 Vue 2,请使用 [`vue-echarts@7`](https://github.com/ecomfe/vue-echarts/tree/7.x)。
|
||||
|
||||
- 要在 `vue@2.7.0` 之前的版本中使用 Vue-ECharts,必须安装 `@vue/composition-api`。
|
||||
- **浏览器兼容性变更:** 我们不再为不支持原生 [`class`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes#browser_compatibility) 的浏览器提供兼容性支持。如果你需要支持旧版浏览器,必须自行将代码转译为 ES5。
|
||||
|
||||
### Prop
|
||||
|
||||
- `options` 重命名为 **`option`**,以和 ECharts 本身保持一致。
|
||||
- 更新 `option` 将采用 **`update-options`** 中的配置,不再检查是否发生引用变化。
|
||||
- `watch-shallow` 被移除。在性能关键场景请使用 **`manual-update`**。
|
||||
|
||||
### 方法
|
||||
|
||||
- `mergeOptions` 重命名为 **`setOption`**,以和 ECharts 本身保持一致。
|
||||
- `showLoading` 与 `hideLoading` 被移除。请使用 **`loading` 与 `loading-options`** prop。
|
||||
- `appendData` 被移除。(由于 ECharts 5 引入的破坏性变更。)
|
||||
- 所有静态方法被从 `vue-echarts` 移除。可以直接使用 `echarts` 本身的这些方法。
|
||||
|
||||
### 计算 Getter
|
||||
|
||||
- 计算 getter(`width`、`height`、`isDisposed` 和 `computedOptions`)被移除。请分别使用 **`getWidth`、`getHeight`、`isDisposed` 和 `getOption`** 方法代替。
|
||||
|
||||
### 样式
|
||||
|
||||
- 现在组件根元素尺寸默认为 **`100%×100%`**,而非原来的 `600×400`。
|
||||
- **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`。
|
||||
|
||||
## 本地开发
|
||||
|
||||
```bash
|
||||
```sh
|
||||
pnpm i
|
||||
pnpm serve
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
打开 `http://localhost:8080` 来查看 demo。
|
||||
打开 `http://localhost:5173` 来查看 demo。
|
||||
|
||||
## 声明
|
||||
|
||||
The Apache Software Foundation [Apache ECharts, ECharts](https://echarts.apache.org/), Apache, the Apache feather, and the Apache ECharts project logo are either registered trademarks or trademarks of the [Apache Software Foundation](https://www.apache.org/).
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
presets: ["@vue/cli-plugin-babel/preset"]
|
||||
};
|
||||
@ -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,6 +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?url";
|
||||
import { track } from "@vercel/analytics";
|
||||
|
||||
import { getImportsFromOption } from "./utils/codegen";
|
||||
@ -30,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 });
|
||||
@ -39,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;
|
||||
@ -54,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;
|
||||
}
|
||||
@ -65,7 +68,7 @@ watch(
|
||||
return;
|
||||
}
|
||||
source.value?.focus();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const copied = ref(false);
|
||||
@ -75,9 +78,11 @@ const transformedCode = ref("");
|
||||
const transformErrors = ref([]);
|
||||
|
||||
onMounted(async () => {
|
||||
await initialize({
|
||||
wasmURL: "https://cdn.jsdelivr.net/npm/esbuild-wasm@0.19.2/esbuild.wasm"
|
||||
});
|
||||
// prevent multiple initializations during HMR
|
||||
if (!window.__esbuildInitialized) {
|
||||
await initialize({ wasmURL });
|
||||
window.__esbuildInitialized = true;
|
||||
}
|
||||
|
||||
initializing.value = false;
|
||||
|
||||
@ -122,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 = [];
|
||||
@ -163,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 */
|
||||
@ -212,7 +217,7 @@ onBeforeUnmount(() => {
|
||||
<aside
|
||||
class="modal"
|
||||
:class="{ open: props.open }"
|
||||
@mousedown="clickFrom = $event.target"
|
||||
@mousedown="onMousedown"
|
||||
@click="closeFromOutside"
|
||||
@keydown.esc="close"
|
||||
>
|
||||
@ -324,10 +329,6 @@ input[type="number"] {
|
||||
overflow: hidden;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 0 45px rgba(0, 0, 0, 0.2);
|
||||
|
||||
h2 {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.options {
|
||||
@ -418,7 +419,9 @@ input[type="number"] {
|
||||
transform: translate(-50%, 200%);
|
||||
border-radius: 4px;
|
||||
opacity: 0;
|
||||
transition: transform 0.2s, opacity 0.2s;
|
||||
transition:
|
||||
transform 0.2s,
|
||||
opacity 0.2s;
|
||||
}
|
||||
|
||||
.message.open {
|
||||
@ -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 {
|
||||
@ -56,13 +57,25 @@ watch(codeOpen, open => {
|
||||
<a href="https://github.com/ecomfe/vue-echarts">Vue-ECharts</a>
|
||||
</h1>
|
||||
<p class="desc">
|
||||
Vue.js component for Apache ECharts. (<a
|
||||
Vue.js component for Apache ECharts™. (<a
|
||||
href="https://github.com/ecomfe/vue-echarts#readme"
|
||||
>docs</a
|
||||
>)
|
||||
</p>
|
||||
|
||||
<h2 class="sep">Examples</h2>
|
||||
<p>
|
||||
<small
|
||||
>See
|
||||
<a href="https://echarts.apache.org/examples/en/index.html"
|
||||
>echarts.apache.org/examples</a
|
||||
>
|
||||
for all examples.</small
|
||||
>
|
||||
</p>
|
||||
|
||||
<bar-chart />
|
||||
<line-chart />
|
||||
<pie-chart />
|
||||
<polar-chart />
|
||||
<scatter-chart />
|
||||
@ -83,7 +96,7 @@ watch(codeOpen, open => {
|
||||
<aside class="renderer">
|
||||
<button
|
||||
:class="{
|
||||
active: initOptions.renderer === 'canvas'
|
||||
active: initOptions.renderer === 'canvas',
|
||||
}"
|
||||
@click="params.renderer = 'canvas'"
|
||||
>
|
||||
@ -91,7 +104,7 @@ watch(codeOpen, open => {
|
||||
</button>
|
||||
<button
|
||||
:class="{
|
||||
active: initOptions.renderer === 'svg'
|
||||
active: initOptions.renderer === 'svg',
|
||||
}"
|
||||
@click="params.renderer = 'svg'"
|
||||
>
|
||||
@ -100,9 +113,7 @@ watch(codeOpen, open => {
|
||||
</aside>
|
||||
|
||||
<aside class="codegen">
|
||||
<button @click="openCodegen">
|
||||
✨ <code>import</code> Codegen <span class="badge">beta</span>
|
||||
</button>
|
||||
<button @click="openCodegen">✨ <code>import</code> Codegen</button>
|
||||
</aside>
|
||||
|
||||
<code-gen v-model:open="codeOpen" :renderer="initOptions.renderer" />
|
||||
@ -132,6 +143,12 @@ body {
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
box-shadow: 0 1px 0 0 #42b983;
|
||||
transition: box-shadow 0.2s;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 2px 0 0 #42b983;
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
@ -139,13 +156,32 @@ h1 {
|
||||
font-family: Inter, "Helvetica Neue", Arial, sans-serif;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2 {
|
||||
color: #2c3e50;
|
||||
font-weight: 400;
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
h2 {
|
||||
.sep {
|
||||
margin-top: 6em;
|
||||
margin-bottom: 1.8em;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
font-size: 1.25em;
|
||||
color: #7f8c8d;
|
||||
opacity: 0.6;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
content: "";
|
||||
display: block;
|
||||
width: 48px;
|
||||
border-bottom: 1px dotted currentColor;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-top: 2em;
|
||||
padding-top: 1em;
|
||||
font-size: 1.2em;
|
||||
@ -196,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 {
|
||||
@ -208,15 +245,20 @@ footer {
|
||||
a {
|
||||
display: inline-block;
|
||||
margin: 0 5px;
|
||||
padding: 3px 0 6px;
|
||||
color: #7f8c8d;
|
||||
font-size: 2em;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3 {
|
||||
color: #2c3e50;
|
||||
font-weight: 400;
|
||||
a,
|
||||
a:hover {
|
||||
padding-bottom: 3px;
|
||||
border-bottom: 3px solid #42b983;
|
||||
text-decoration: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
@ -251,7 +293,7 @@ input {
|
||||
}
|
||||
|
||||
label {
|
||||
display: flex;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
@ -265,7 +307,7 @@ select {
|
||||
}
|
||||
|
||||
#logo {
|
||||
display: inline-block;
|
||||
display: inline-flex;
|
||||
width: 128px;
|
||||
height: 128px;
|
||||
pointer-events: none;
|
||||
@ -335,11 +377,13 @@ select {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.actions {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.renderer,
|
||||
@ -399,15 +443,6 @@ select {
|
||||
align-items: center;
|
||||
padding: 0 4px;
|
||||
gap: 4px;
|
||||
|
||||
.badge {
|
||||
display: block;
|
||||
padding: 2px 3px;
|
||||
font-size: 10px;
|
||||
background: #36485e54;
|
||||
color: #fff;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 465 750">
|
||||
<path fill="currentColor" d="M367.855,428.202c-3.674-1.385-7.452-1.966-11.146-1.794c0.659-2.922,0.844-5.85,0.58-8.719 c-0.937-10.407-7.663-19.864-18.063-23.834c-10.697-4.043-22.298-1.168-29.902,6.403c3.015,0.026,6.074,0.594,9.035,1.728 c13.626,5.151,20.465,20.379,15.32,34.004c-1.905,5.02-5.177,9.115-9.22,12.05c-6.951,4.992-16.19,6.536-24.777,3.271 c-13.625-5.137-20.471-20.371-15.32-34.004c0.673-1.768,1.523-3.423,2.526-4.992h-0.014c0,0,0,0,0,0.014 c4.386-6.853,8.145-14.279,11.146-22.187c23.294-61.505-7.689-130.278-69.215-153.579c-61.532-23.293-130.279,7.69-153.579,69.202 c-6.371,16.785-8.679,34.097-7.426,50.901c0.026,0.554,0.079,1.121,0.132,1.688c4.973,57.107,41.767,109.148,98.945,130.793 c58.162,22.008,121.303,6.529,162.839-34.465c7.103-6.893,17.826-9.444,27.679-5.719c11.858,4.491,18.565,16.6,16.719,28.643 c4.438-3.126,8.033-7.564,10.117-13.045C389.751,449.992,382.411,433.709,367.855,428.202z"/>
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 465 750">
|
||||
<path fill="currentColor" d="M367.855,428.202c-3.674-1.385-7.452-1.966-11.146-1.794c0.659-2.922,0.844-5.85,0.58-8.719 c-0.937-10.407-7.663-19.864-18.063-23.834c-10.697-4.043-22.298-1.168-29.902,6.403c3.015,0.026,6.074,0.594,9.035,1.728 c13.626,5.151,20.465,20.379,15.32,34.004c-1.905,5.02-5.177,9.115-9.22,12.05c-6.951,4.992-16.19,6.536-24.777,3.271 c-13.625-5.137-20.471-20.371-15.32-34.004c0.673-1.768,1.523-3.423,2.526-4.992h-0.014c0,0,0,0,0,0.014 c4.386-6.853,8.145-14.279,11.146-22.187c23.294-61.505-7.689-130.278-69.215-153.579c-61.532-23.293-130.279,7.69-153.579,69.202 c-6.371,16.785-8.679,34.097-7.426,50.901c0.026,0.554,0.079,1.121,0.132,1.688c4.973,57.107,41.767,109.148,98.945,130.793 c58.162,22.008,121.303,6.529,162.839-34.465c7.103-6.893,17.826-9.444,27.679-5.719c11.858,4.491,18.565,16.6,16.719,28.643 c4.438-3.126,8.033-7.564,10.117-13.045C389.751,449.992,382.411,433.709,367.855,428.202z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1011 B After Width: | Height: | Size: 1008 B |
|
Before Width: | Height: | Size: 901 KiB After Width: | Height: | Size: 901 KiB |
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.3 MiB |
@ -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" }],
|
||||
};
|
||||
}
|
||||
@ -5,42 +5,44 @@ 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: 20,
|
||||
data: ["scatter"]
|
||||
top: "3%",
|
||||
data: ["scatter"],
|
||||
},
|
||||
tooltip: {
|
||||
formatter: "{c}"
|
||||
formatter: "{c}",
|
||||
},
|
||||
grid: {
|
||||
top: "26%",
|
||||
bottom: "26%"
|
||||
top: "30%",
|
||||
right: "18%",
|
||||
bottom: "20%",
|
||||
},
|
||||
xAxis: {
|
||||
type: "value",
|
||||
splitLine: {
|
||||
show: false
|
||||
}
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
yAxis: {
|
||||
type: "value",
|
||||
splitLine: {
|
||||
show: false
|
||||
}
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
visualMap: [
|
||||
{
|
||||
realtime: false,
|
||||
left: "right",
|
||||
right: "2%",
|
||||
bottom: "3%",
|
||||
selectedMode: "multiple",
|
||||
dimension: 2,
|
||||
selected: [],
|
||||
@ -48,50 +50,52 @@ 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: 20,
|
||||
data: ["scatter"]
|
||||
top: "3%",
|
||||
data: ["scatter"],
|
||||
},
|
||||
tooltip: {
|
||||
formatter: "{c}"
|
||||
formatter: "{c}",
|
||||
},
|
||||
grid: {
|
||||
top: "26%",
|
||||
bottom: "26%"
|
||||
top: "30%",
|
||||
right: "18%",
|
||||
bottom: "20%",
|
||||
},
|
||||
xAxis: {
|
||||
type: "value",
|
||||
splitLine: {
|
||||
show: false
|
||||
}
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
yAxis: {
|
||||
type: "value",
|
||||
splitLine: {
|
||||
show: false
|
||||
}
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
visualMap: [
|
||||
{
|
||||
left: "right",
|
||||
right: "2%",
|
||||
bottom: "3%",
|
||||
selectedMode: "multiple",
|
||||
dimension: 2,
|
||||
selected: [],
|
||||
@ -99,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
@ -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" },
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
@ -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,23 +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 "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
formatter() {
|
||||
return "";
|
||||
},
|
||||
},
|
||||
itemStyle: {
|
||||
shadowBlur: 12,
|
||||
shadowColor: "rgba(0, 0, 0, 0.25)",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@ -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,91 +401,94 @@ export default function getData() {
|
||||
return {
|
||||
textStyle: {
|
||||
fontFamily: 'Inter, "Helvetica Neue", Arial, sans-serif',
|
||||
fontWeight: 300
|
||||
fontWeight: 300,
|
||||
},
|
||||
backgroundColor: "#404a59",
|
||||
title: {
|
||||
text: "Air quality of major cities in China",
|
||||
subtext: "data from PM25.in",
|
||||
sublink: "http://www.pm25.in",
|
||||
top: "5%",
|
||||
left: "center",
|
||||
textStyle: {
|
||||
color: "#fff"
|
||||
}
|
||||
color: "#fff",
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
trigger: "item"
|
||||
trigger: "item",
|
||||
},
|
||||
legend: {
|
||||
orient: "vertical",
|
||||
y: "bottom",
|
||||
x: "right",
|
||||
data: ["pm2.5"],
|
||||
right: "5%",
|
||||
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%",
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: "pm2.5",
|
||||
name: "PM2.5",
|
||||
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,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
@ -2,20 +2,22 @@ export default function getData() {
|
||||
return {
|
||||
textStyle: {
|
||||
fontFamily: 'Inter, "Helvetica Neue", Arial, sans-serif',
|
||||
fontWeight: 300
|
||||
fontWeight: 300,
|
||||
},
|
||||
title: {
|
||||
text: "Traffic Sources",
|
||||
left: "center"
|
||||
top: "5%",
|
||||
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"]
|
||||
top: "5%",
|
||||
left: "5%",
|
||||
data: ["Direct", "Email", "Ad Networks", "Video Ads", "Search Engines"],
|
||||
},
|
||||
series: [
|
||||
{
|
||||
@ -28,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)",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
@ -10,29 +10,33 @@ export default function getData() {
|
||||
return {
|
||||
textStyle: {
|
||||
fontFamily: 'Inter, "Helvetica Neue", Arial, sans-serif',
|
||||
fontWeight: 300
|
||||
fontWeight: 300,
|
||||
},
|
||||
title: {
|
||||
text: "Dual Numeric Axis"
|
||||
text: "Dual Numeric Axis",
|
||||
top: "5%",
|
||||
left: "5%",
|
||||
},
|
||||
legend: {
|
||||
data: ["line"]
|
||||
data: ["line"],
|
||||
top: "6%",
|
||||
},
|
||||
polar: {
|
||||
center: ["50%", "54%"]
|
||||
radius: "65%",
|
||||
center: ["50%", "56%"],
|
||||
},
|
||||
tooltip: {
|
||||
trigger: "axis",
|
||||
axisPointer: {
|
||||
type: "cross"
|
||||
}
|
||||
type: "cross",
|
||||
},
|
||||
},
|
||||
angleAxis: {
|
||||
type: "value",
|
||||
startAngle: 0
|
||||
startAngle: 0,
|
||||
},
|
||||
radiusAxis: {
|
||||
min: 0
|
||||
min: 0,
|
||||
},
|
||||
series: [
|
||||
{
|
||||
@ -40,9 +44,9 @@ export default function getData() {
|
||||
name: "line",
|
||||
type: "line",
|
||||
showSymbol: false,
|
||||
data: data
|
||||
}
|
||||
data: data,
|
||||
},
|
||||
],
|
||||
animationDuration: 2000
|
||||
animationDuration: 2000,
|
||||
};
|
||||
}
|
||||
@ -8,59 +8,40 @@ 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(() => {
|
||||
return scores.value.map(({ name }) => name);
|
||||
});
|
||||
|
||||
const scoreRadar = computed(() => {
|
||||
function getRadarData(activeIndex: number) {
|
||||
return {
|
||||
title: {
|
||||
text: "Player Ability"
|
||||
text: "Player Ability",
|
||||
top: "5%",
|
||||
left: "5%",
|
||||
},
|
||||
textStyle: {
|
||||
fontFamily: 'Inter, "Helvetica Neue", Arial, sans-serif',
|
||||
fontWeight: 300
|
||||
fontWeight: 300,
|
||||
},
|
||||
radar: {
|
||||
indicator: scores.value.map(({ name, max }) => {
|
||||
return { name, max };
|
||||
})
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: "Value",
|
||||
type: "radar",
|
||||
data: [{ value: scores.value.map(({ value }) => value) }]
|
||||
}
|
||||
]
|
||||
};
|
||||
});
|
||||
|
||||
function getRadarData(activeIndex: number) {
|
||||
return {
|
||||
animation: false,
|
||||
title: {
|
||||
text: "Player Ability"
|
||||
},
|
||||
tooltip: {},
|
||||
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) }],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
@ -81,10 +62,9 @@ export const useScoreStore = defineStore("store", () => {
|
||||
return {
|
||||
scores,
|
||||
metrics,
|
||||
scoreRadar,
|
||||
getRadarData,
|
||||
increase,
|
||||
isMax,
|
||||
isMin
|
||||
isMin,
|
||||
};
|
||||
});
|
||||
@ -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,37 +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%",
|
||||
},
|
||||
textStyle: {
|
||||
fontFamily: 'Inter, "Helvetica Neue", Arial, sans-serif',
|
||||
fontWeight: 300
|
||||
fontWeight: 300,
|
||||
},
|
||||
title: {
|
||||
text: "Life Expectancy vs. GDP by country"
|
||||
text: "Life Expectancy vs. GDP by country",
|
||||
top: "5%",
|
||||
left: "5%",
|
||||
},
|
||||
legend: {
|
||||
right: 10,
|
||||
data: ["1990", "2015"]
|
||||
top: "6%",
|
||||
right: "5%",
|
||||
data: ["1990", "2015"],
|
||||
},
|
||||
xAxis: {
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
type: "dashed"
|
||||
}
|
||||
}
|
||||
type: "dashed",
|
||||
},
|
||||
},
|
||||
},
|
||||
yAxis: {
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
type: "dashed"
|
||||
}
|
||||
type: "dashed",
|
||||
},
|
||||
},
|
||||
scale: true
|
||||
scale: true,
|
||||
},
|
||||
series: [
|
||||
{
|
||||
@ -87,8 +93,8 @@ export default function getData() {
|
||||
formatter({ data }) {
|
||||
return data[3];
|
||||
},
|
||||
position: "top"
|
||||
}
|
||||
position: "top",
|
||||
},
|
||||
},
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
@ -97,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",
|
||||
@ -119,8 +125,8 @@ export default function getData() {
|
||||
formatter({ data }) {
|
||||
return data[3];
|
||||
},
|
||||
position: "top"
|
||||
}
|
||||
position: "top",
|
||||
},
|
||||
},
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
@ -129,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)",
|
||||
},
|
||||
]),
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
@ -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());
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -46,10 +46,8 @@ watch(
|
||||
</template>
|
||||
<template #extra>
|
||||
<p class="actions">
|
||||
<label>
|
||||
<input type="checkbox" v-model="connected" />
|
||||
Connected
|
||||
</label>
|
||||
<input id="connected-check" type="checkbox" v-model="connected" />
|
||||
<label for="connected-check">Connected</label>
|
||||
</p>
|
||||
</template>
|
||||
</v-example>
|
||||
@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<h2 :id="id">
|
||||
<h3 :id="id">
|
||||
<a :href="`#${id}`">
|
||||
{{ title }}
|
||||
<small v-if="desc">{{ desc }}</small>
|
||||
</a>
|
||||
</h2>
|
||||
</h3>
|
||||
<section>
|
||||
<figure class="fig hero" v-if="!split">
|
||||
<slot />
|
||||
@ -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,11 +43,10 @@ defineProps({
|
||||
width: fit-content;
|
||||
margin: 2em auto;
|
||||
|
||||
.echarts {
|
||||
> .echarts {
|
||||
width: calc(60vw + 4em);
|
||||
height: 360px;
|
||||
max-width: 720px;
|
||||
padding: 1.5em 2em;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 0 45px rgba(0, 0, 0, 0.2);
|
||||
@ -75,7 +72,6 @@ defineProps({
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
height: 60vw;
|
||||
padding: 1em 0;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
@ -88,7 +84,6 @@ defineProps({
|
||||
.echarts {
|
||||
width: 28vw;
|
||||
min-width: 240px;
|
||||
padding: 1em 1.5em;
|
||||
height: 180px;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -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,31 +42,33 @@ onMounted(() => {
|
||||
environment: starfield,
|
||||
light: {
|
||||
main: {
|
||||
intensity: 2
|
||||
}
|
||||
intensity: 2,
|
||||
},
|
||||
},
|
||||
viewControl: {
|
||||
autoRotate: false
|
||||
}
|
||||
autoRotate: false,
|
||||
},
|
||||
},
|
||||
visualMap: {
|
||||
bottom: "3%",
|
||||
left: "3%",
|
||||
max: 40,
|
||||
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: [
|
||||
{
|
||||
@ -77,10 +79,10 @@ onMounted(() => {
|
||||
minHeight: 0.2,
|
||||
silent: true,
|
||||
itemStyle: {
|
||||
color: "orange"
|
||||
}
|
||||
}
|
||||
]
|
||||
color: "orange",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
});
|
||||
108
demo/examples/LineChart.vue
Normal 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>
|
||||
@ -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";
|
||||
|
||||
@ -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,20 +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: {
|
||||
@ -58,33 +59,33 @@ function load() {
|
||||
return (
|
||||
data.airports[route[1]][1] + " > " + data.airports[route[2]][1]
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
geo: {
|
||||
map: "world",
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: "15%",
|
||||
right: "5%",
|
||||
bottom: "5%",
|
||||
left: "5%",
|
||||
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",
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -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 () => {
|
||||
stopActions();
|
||||
};
|
||||
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);
|
||||
}
|
||||
@ -5,11 +5,11 @@ import {
|
||||
PolarComponent,
|
||||
TitleComponent,
|
||||
LegendComponent,
|
||||
TooltipComponent
|
||||
TooltipComponent,
|
||||
} from "echarts/components";
|
||||
import { shallowRef } from "vue";
|
||||
import VChart from "../../ECharts";
|
||||
import VExample from "./Example";
|
||||
import { computed, shallowRef } from "vue";
|
||||
import VChart from "../../src/ECharts";
|
||||
import VExample from "./Example.vue";
|
||||
import getData from "../data/polar";
|
||||
|
||||
use([
|
||||
@ -17,11 +17,28 @@ use([
|
||||
PolarComponent,
|
||||
TitleComponent,
|
||||
LegendComponent,
|
||||
TooltipComponent
|
||||
TooltipComponent,
|
||||
]);
|
||||
|
||||
const option = shallowRef(getData());
|
||||
const theme = shallowRef("dark");
|
||||
const loading = shallowRef(false);
|
||||
const loadingOptions = computed(() =>
|
||||
theme.value === "dark"
|
||||
? {
|
||||
color: "#fff",
|
||||
textColor: "#fff",
|
||||
maskColor: "rgba(0, 0, 0, 0.7)",
|
||||
}
|
||||
: null,
|
||||
);
|
||||
const style = computed(() => {
|
||||
return theme.value === "dark"
|
||||
? loading.value
|
||||
? "background-color: #05040d"
|
||||
: "background-color: #100c2a"
|
||||
: "";
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -29,8 +46,10 @@ const theme = shallowRef("dark");
|
||||
<v-chart
|
||||
:option="option"
|
||||
autoresize
|
||||
:loading="loading"
|
||||
:loading-options="loadingOptions"
|
||||
:theme="theme"
|
||||
:style="theme === 'dark' ? 'background-color: #100c2a' : ''"
|
||||
:style="style"
|
||||
/>
|
||||
<template #extra>
|
||||
<p class="actions">
|
||||
@ -39,6 +58,8 @@ const theme = shallowRef("dark");
|
||||
<option :value="null">Default</option>
|
||||
<option value="dark">Dark</option>
|
||||
</select>
|
||||
<input id="loading-check" type="checkbox" v-model="loading" />
|
||||
<label for="loading-check">Loading</label>
|
||||
</p>
|
||||
</template>
|
||||
</v-example>
|
||||
@ -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]);
|
||||
@ -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
@ -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>
|
||||
@ -3,7 +3,17 @@ import { createApp } from "vue";
|
||||
import { createPinia } from "pinia";
|
||||
import Demo from "./Demo.vue";
|
||||
|
||||
inject();
|
||||
const SAMPLE_RATE = 0.5;
|
||||
|
||||
inject({
|
||||
beforeSend: (event) => {
|
||||
if (Math.random() > SAMPLE_RATE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return event;
|
||||
},
|
||||
});
|
||||
|
||||
const pinia = createPinia();
|
||||
const app = createApp(Demo);
|
||||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
6
demo/shims-vue.d.ts
vendored
Normal 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
@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
||||
"include": ["./**/*", "./**/*.vue"],
|
||||
"compilerOptions": {
|
||||
"types": ["vite/client"],
|
||||
"allowJs": true
|
||||
}
|
||||
}
|
||||
@ -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];
|
||||
}
|
||||
|
||||
series.forEach(seriesOpt => {
|
||||
const series = Array.isArray(option.series) ? option.series : [option.series];
|
||||
let hasScatterSeries = false;
|
||||
series.forEach((seriesOpt) => {
|
||||
if (seriesOpt.type === "scatter") {
|
||||
hasScatterSeries = true;
|
||||
}
|
||||
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
@ -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",
|
||||
},
|
||||
},
|
||||
);
|
||||
138
package.json
@ -1,88 +1,80 @@
|
||||
{
|
||||
"name": "vue-echarts",
|
||||
"version": "6.6.2",
|
||||
"description": "Vue.js component for Apache ECharts.",
|
||||
"version": "8.0.0-beta.1",
|
||||
"description": "Vue.js component for Apache ECharts™.",
|
||||
"license": "MIT",
|
||||
"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:2 && pnpm run build:3 && vue-demi-switch 3",
|
||||
"build:2": "vue-demi-switch 2 vue2 && rollup -c rollup.vue2.config.js",
|
||||
"build:3": "vue-demi-switch 3 && rollup -c rollup.config.js",
|
||||
"lint": "vue-cli-service lint",
|
||||
"build:demo": "vue-cli-service build",
|
||||
"docs": "node ./scripts/docs.js",
|
||||
"postinstall": "node ./scripts/postinstall.js",
|
||||
"prepublishOnly": "pnpm run build"
|
||||
"dev": "vite",
|
||||
"build": "tsdown",
|
||||
"typecheck": "tsc",
|
||||
"lint": "eslint . --fix",
|
||||
"format": "prettier . --write",
|
||||
"publint": "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",
|
||||
"jsdelivr": "dist/index.min.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"exports": {
|
||||
".": "./dist/index.js",
|
||||
"./style.css": "./dist/style.css"
|
||||
},
|
||||
"main": "dist/index.cjs.min.js",
|
||||
"module": "dist/index.esm.min.js",
|
||||
"unpkg": "dist/index.umd.min.js",
|
||||
"files": [
|
||||
"dist",
|
||||
"scripts/postinstall.js"
|
||||
"dist"
|
||||
],
|
||||
"dependencies": {
|
||||
"resize-detector": "^0.3.0",
|
||||
"vue-demi": "^0.13.11"
|
||||
"peerDependencies": {
|
||||
"echarts": "^6.0.0",
|
||||
"vue": "^3.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.23.2",
|
||||
"@highlightjs/vue-plugin": "^2.1.0",
|
||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||
"@rollup/plugin-replace": "^5.0.5",
|
||||
"@rollup/plugin-terser": "^0.4.4",
|
||||
"@typescript-eslint/eslint-plugin": "^4.33.0",
|
||||
"@typescript-eslint/parser": "^4.33.0",
|
||||
"@vercel/analytics": "^1.1.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.3.7",
|
||||
"@vue/composition-api": "^1.7.2",
|
||||
"@vue/eslint-config-prettier": "^6.0.0",
|
||||
"@vue/eslint-config-typescript": "^10.0.0",
|
||||
"@vueuse/core": "^10.5.0",
|
||||
"comment-mark": "^1.1.1",
|
||||
"core-js": "^3.33.2",
|
||||
"echarts": "^5.4.3",
|
||||
"@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.19.2",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-plugin-prettier": "^3.4.1",
|
||||
"eslint-plugin-vue": "^8.7.1",
|
||||
"highlight.js": "^11.9.0",
|
||||
"pinia": "^2.1.7",
|
||||
"postcss": "^8.4.31",
|
||||
"postcss-loader": "^5.3.0",
|
||||
"postcss-nested": "^5.0.6",
|
||||
"prettier": "^2.8.8",
|
||||
"raw-loader": "^4.0.2",
|
||||
"resize-detector": "^0.3.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"rollup": "^2.79.1",
|
||||
"rollup-plugin-dts": "^4.2.3",
|
||||
"rollup-plugin-styles": "^4.0.0",
|
||||
"rollup-plugin-ts": "^2.0.7",
|
||||
"tslib": "^2.6.2",
|
||||
"typescript": "4.6.4",
|
||||
"vue": "^3.3.7",
|
||||
"vue2": "npm:vue@^2.7.15",
|
||||
"webpack": "^5.89.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"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vue/composition-api": "^1.0.5",
|
||||
"echarts": "^5.4.1",
|
||||
"vue": "^2.6.12 || ^3.1.1"
|
||||
},
|
||||
"jsdelivr": "dist/index.umd.min.js",
|
||||
"license": "MIT",
|
||||
"peerDependenciesMeta": {
|
||||
"@vue/composition-api": {
|
||||
"optional": true
|
||||
"pnpm": {
|
||||
"peerDependencyRules": {
|
||||
"allowedVersions": {
|
||||
"echarts": "6"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repository": "https://github.com/ecomfe/vue-echarts.git",
|
||||
"types": "dist/index.d.ts"
|
||||
}
|
||||
}
|
||||
|
||||
10369
pnpm-lock.yaml
generated
2
pnpm-workspace.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
onlyBuiltDependencies:
|
||||
- esbuild
|
||||
@ -1,18 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="">
|
||||
<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>
|
||||
141
rollup.config.js
@ -1,141 +0,0 @@
|
||||
import typescript from "rollup-plugin-ts";
|
||||
import terser from "@rollup/plugin-terser";
|
||||
import resolve from "@rollup/plugin-node-resolve";
|
||||
import replace from "@rollup/plugin-replace";
|
||||
import styles from "rollup-plugin-styles";
|
||||
import { injectVueDemi } from "./scripts/rollup";
|
||||
|
||||
/**
|
||||
* 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 ? styles({ mode: ["extract", "style.css"] }) : styles()
|
||||
];
|
||||
|
||||
// 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: [
|
||||
typescript({
|
||||
tsconfig: resolvedConfig => ({ ...resolvedConfig, declaration: true }),
|
||||
hook: {
|
||||
outputPath: (path, kind) =>
|
||||
kind === "declaration" ? "dist/index.d.ts" : path
|
||||
}
|
||||
})
|
||||
],
|
||||
external: ["vue-demi", "echarts/core", "resize-detector"],
|
||||
output: {
|
||||
file: "dist/index.esm.js",
|
||||
format: "esm",
|
||||
sourcemap: true
|
||||
}
|
||||
},
|
||||
{
|
||||
input: "src/index.ts",
|
||||
plugins: [typescript()],
|
||||
external: ["vue-demi", "echarts/core", "resize-detector"],
|
||||
output: [
|
||||
{
|
||||
file: "dist/index.esm.min.js",
|
||||
format: "esm",
|
||||
sourcemap: true,
|
||||
plugins: [
|
||||
terser({
|
||||
format: {
|
||||
comments: false
|
||||
}
|
||||
})
|
||||
]
|
||||
},
|
||||
{
|
||||
file: "dist/index.cjs.js",
|
||||
format: "cjs",
|
||||
exports: "named",
|
||||
sourcemap: true
|
||||
},
|
||||
{
|
||||
file: "dist/index.cjs.min.js",
|
||||
format: "cjs",
|
||||
exports: "named",
|
||||
sourcemap: true,
|
||||
plugins: [
|
||||
terser({
|
||||
format: {
|
||||
comments: false
|
||||
}
|
||||
})
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
input: "src/global.ts",
|
||||
plugins: [resolve(), typescript()],
|
||||
external: ["vue-demi", "echarts", "echarts/core"],
|
||||
output: [
|
||||
{
|
||||
file: "dist/index.umd.js",
|
||||
format: "umd",
|
||||
name: "VueECharts",
|
||||
exports: "default",
|
||||
sourcemap: true,
|
||||
globals: {
|
||||
"vue-demi": "VueDemi",
|
||||
echarts: "echarts",
|
||||
"echarts/core": "echarts"
|
||||
},
|
||||
plugins: [injectVueDemi]
|
||||
},
|
||||
{
|
||||
file: "dist/index.umd.min.js",
|
||||
format: "umd",
|
||||
name: "VueECharts",
|
||||
exports: "default",
|
||||
sourcemap: true,
|
||||
globals: {
|
||||
"vue-demi": "VueDemi",
|
||||
echarts: "echarts",
|
||||
"echarts/core": "echarts"
|
||||
},
|
||||
plugins: [
|
||||
injectVueDemi,
|
||||
terser({
|
||||
format: {
|
||||
comments: false
|
||||
}
|
||||
})
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
export default [
|
||||
...builds.map(options => configBuild(options, false)),
|
||||
...builds.map(options => configBuild(options, true))
|
||||
];
|
||||
@ -1,15 +0,0 @@
|
||||
import dts from "rollup-plugin-dts";
|
||||
|
||||
/** @type {import('rollup').RollupOptions[]} */
|
||||
const options = [
|
||||
{
|
||||
input: "src/index.vue2.d.ts",
|
||||
plugins: [dts()],
|
||||
output: {
|
||||
file: "dist/index.vue2.d.ts",
|
||||
format: "esm"
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
export default options;
|
||||
16
scripts/dist-tag.ts
Normal 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);
|
||||
@ -1,60 +0,0 @@
|
||||
const { readFileSync, writeFileSync } = require("fs");
|
||||
const { resolve } = require("path");
|
||||
const commentMark = require("comment-mark");
|
||||
const { name, version } = require("../package.json");
|
||||
|
||||
function resolvePath(...parts) {
|
||||
return resolve(__dirname, ...parts);
|
||||
}
|
||||
|
||||
const CDN_PREFIX = "https://cdn.jsdelivr.net/npm/";
|
||||
|
||||
const DEP_VERSIONS = {
|
||||
"vue@3": "3.3.7",
|
||||
"vue@2": "2.7.15",
|
||||
echarts: "5.4.3",
|
||||
[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("..", 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
@ -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.");
|
||||
@ -1,43 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
|
||||
const packageFile = path.resolve(__dirname, "../package.json");
|
||||
|
||||
const typesPaths = {
|
||||
3: "dist/index.d.ts",
|
||||
2: "dist/index.vue2.d.ts"
|
||||
};
|
||||
|
||||
function switchVersion(version) {
|
||||
const typesPath = typesPaths[version];
|
||||
const package = JSON.parse(fs.readFileSync(packageFile, "utf8"));
|
||||
if (typesPath !== package.types) {
|
||||
package.types = typesPath;
|
||||
fs.writeFileSync(packageFile, JSON.stringify(package, null, " "), "utf8");
|
||||
}
|
||||
console.log(`[vue-echarts] Switched to Vue ${version} environment.`);
|
||||
}
|
||||
|
||||
function loadVue() {
|
||||
try {
|
||||
return require("vue");
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const Vue = loadVue();
|
||||
|
||||
// Align the process with vue-demi
|
||||
if (!Vue || typeof Vue.version !== "string") {
|
||||
console.warn(
|
||||
'[vue-echarts] Vue is not found. Please run "npm install vue" to install.'
|
||||
);
|
||||
} else if (Vue.version.startsWith("3.")) {
|
||||
switchVersion(3);
|
||||
} else if (Vue.version.startsWith("2.")) {
|
||||
switchVersion(2);
|
||||
} else {
|
||||
console.warn(`[vue-echarts] Vue version v${Vue.version} is not supported.`);
|
||||
}
|
||||
@ -1,30 +0,0 @@
|
||||
import { readFileSync } from "fs";
|
||||
|
||||
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;`;
|
||||
}
|
||||
};
|
||||
|
||||
const EMPTY_FILE_ID = "__rollup_empty__";
|
||||
|
||||
/** @type {import('rollup').Plugin} */
|
||||
export const ingoreCss = {
|
||||
name: "ignore-css",
|
||||
resolveId(source) {
|
||||
if (source.endsWith(".css")) {
|
||||
return EMPTY_FILE_ID;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
load(id) {
|
||||
return id === EMPTY_FILE_ID ? "" : null;
|
||||
}
|
||||
};
|
||||
20
scripts/utils.ts
Normal 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
@ -1,6 +0,0 @@
|
||||
/* eslint-disable */
|
||||
declare module '*.vue' {
|
||||
import type { DefineComponent } from 'vue'
|
||||
const component: DefineComponent<{}, {}, any>
|
||||
export default component
|
||||
}
|
||||
246
src/ECharts.ts
@ -1,5 +1,3 @@
|
||||
/* eslint-disable vue/multi-word-component-names */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import {
|
||||
defineComponent,
|
||||
shallowRef,
|
||||
@ -12,15 +10,26 @@ import {
|
||||
h,
|
||||
nextTick,
|
||||
watchEffect,
|
||||
getCurrentInstance,
|
||||
Vue2,
|
||||
type PropType,
|
||||
type InjectionKey
|
||||
} from "vue-demi";
|
||||
toValue,
|
||||
} from "vue";
|
||||
import { init as initChart } from "echarts/core";
|
||||
|
||||
import {
|
||||
usePublicAPI,
|
||||
useAutoresize,
|
||||
autoresizeProps,
|
||||
useLoading,
|
||||
loadingProps,
|
||||
useSlotOption,
|
||||
} from "./composables";
|
||||
import type { PublicMethods, SlotsTypes } from "./composables";
|
||||
import { isOn, omitOn } from "./utils";
|
||||
import { register, TAG_NAME } from "./wc";
|
||||
|
||||
import type { PropType, InjectionKey } from "vue";
|
||||
import type {
|
||||
EChartsType,
|
||||
EventTarget,
|
||||
SetOptionType,
|
||||
Option,
|
||||
Theme,
|
||||
ThemeInjection,
|
||||
@ -28,31 +37,18 @@ import type {
|
||||
InitOptionsInjection,
|
||||
UpdateOptions,
|
||||
UpdateOptionsInjection,
|
||||
Emits
|
||||
Emits,
|
||||
} from "./types";
|
||||
import {
|
||||
usePublicAPI,
|
||||
useAutoresize,
|
||||
autoresizeProps,
|
||||
useLoading,
|
||||
loadingProps
|
||||
} from "./composables";
|
||||
import { omitOn, unwrapInjected } from "./utils";
|
||||
import { register, TAG_NAME, type EChartsElement } from "./wc";
|
||||
import "./style.css";
|
||||
import type { EChartsElement } from "./wc";
|
||||
|
||||
const __CSP__ = false;
|
||||
const wcRegistered = __CSP__ ? false : register();
|
||||
import "./style.ts";
|
||||
|
||||
if (Vue2) {
|
||||
Vue2.config.ignoredElements.push(TAG_NAME);
|
||||
}
|
||||
const wcRegistered = register();
|
||||
|
||||
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";
|
||||
|
||||
export default defineComponent({
|
||||
@ -60,20 +56,20 @@ export default defineComponent({
|
||||
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 inner = shallowRef<HTMLElement>();
|
||||
const chart = shallowRef<EChartsType>();
|
||||
const manualOption = shallowRef<Option>();
|
||||
const defaultTheme = inject(THEME_KEY, null);
|
||||
@ -83,80 +79,90 @@ 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 listeners: Map<{ event: string; once?: boolean; zr?: boolean }, any> =
|
||||
new Map();
|
||||
|
||||
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) => {
|
||||
// Collect native DOM events
|
||||
if (key.indexOf("Native:") === 2) {
|
||||
// onNative:click -> onClick
|
||||
const nativeKey = `on${key.charAt(9).toUpperCase()}${key.slice(10)}`;
|
||||
|
||||
nativeListeners[nativeKey] = attrs[key];
|
||||
return;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
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 (!inner.value) {
|
||||
if (!root.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const instance = (chart.value = initChart(
|
||||
inner.value,
|
||||
root.value,
|
||||
realTheme.value,
|
||||
realInitOptions.value
|
||||
realInitOptions.value,
|
||||
));
|
||||
|
||||
if (props.group) {
|
||||
instance.group = props.group;
|
||||
}
|
||||
|
||||
let realListeners = listeners;
|
||||
if (!realListeners) {
|
||||
realListeners = {};
|
||||
|
||||
Object.keys(attrs)
|
||||
.filter(key => key.indexOf("on") === 0 && key.length > 2)
|
||||
.forEach(key => {
|
||||
// onClick -> c + lick
|
||||
// onZr:click -> z + r:click
|
||||
let event = key.charAt(2).toLowerCase() + key.slice(3);
|
||||
|
||||
// clickOnce -> ~click
|
||||
// zr:clickOnce -> ~zr:click
|
||||
if (event.substring(event.length - 4) === "Once") {
|
||||
event = `~${event.substring(0, event.length - 4)}`;
|
||||
}
|
||||
|
||||
realListeners[event] = attrs[key];
|
||||
});
|
||||
}
|
||||
|
||||
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[]) => {
|
||||
@ -180,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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -195,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;
|
||||
}
|
||||
@ -204,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) {
|
||||
@ -218,7 +230,7 @@ export default defineComponent({
|
||||
let unwatchOption: (() => void) | null = null;
|
||||
watch(
|
||||
manualUpdate,
|
||||
manualUpdate => {
|
||||
(manualUpdate) => {
|
||||
if (typeof unwatchOption === "function") {
|
||||
unwatchOption();
|
||||
unwatchOption = null;
|
||||
@ -234,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(() => {
|
||||
@ -272,7 +294,7 @@ export default defineComponent({
|
||||
|
||||
useLoading(chart, loading, loadingOptions);
|
||||
|
||||
useAutoresize(chart, autoresize, inner);
|
||||
useAutoresize(chart, autoresize, root);
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
@ -290,23 +312,27 @@ export default defineComponent({
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
chart,
|
||||
root,
|
||||
inner,
|
||||
const exposed = {
|
||||
setOption,
|
||||
nonEventAttrs,
|
||||
...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 } : { ...this.nonEventAttrs }
|
||||
) as any;
|
||||
attrs.ref = "root";
|
||||
attrs.class = attrs.class ? ["echarts"].concat(attrs.class) : "echarts";
|
||||
return h(TAG_NAME, attrs, [h("div", { ref: "inner" })]);
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { Ref } from "vue-demi";
|
||||
import { EChartsType } from "../types";
|
||||
import type { Ref } from "vue";
|
||||
import type { EChartsType } from "../types";
|
||||
|
||||
const METHOD_NAMES = [
|
||||
"getWidth",
|
||||
@ -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);
|
||||
});
|
||||
|
||||
|
||||
@ -1,48 +1,66 @@
|
||||
import { watch, type Ref, type PropType } from "vue-demi";
|
||||
import { watch } from "vue";
|
||||
import { throttle } from "echarts/core";
|
||||
import {
|
||||
addListener,
|
||||
removeListener,
|
||||
type ResizeCallback
|
||||
} from "resize-detector";
|
||||
import { type EChartsType } from "../types";
|
||||
|
||||
type AutoresizeProp =
|
||||
| boolean
|
||||
| {
|
||||
throttle?: number;
|
||||
onResize?: () => void;
|
||||
};
|
||||
import type { Ref, PropType } from "vue";
|
||||
import type { EChartsType, AutoResize } from "../types";
|
||||
|
||||
export function useAutoresize(
|
||||
chart: Ref<EChartsType | undefined>,
|
||||
autoresize: Ref<AutoresizeProp | undefined>,
|
||||
root: Ref<HTMLElement | undefined>
|
||||
autoresize: Ref<AutoResize | undefined>,
|
||||
root: Ref<HTMLElement | undefined>,
|
||||
): void {
|
||||
let resizeListener: ResizeCallback | null = null;
|
||||
watch(
|
||||
[root, chart, autoresize],
|
||||
([root, chart, autoresize], _, onCleanup) => {
|
||||
let ro: ResizeObserver | null = null;
|
||||
|
||||
watch([root, chart, autoresize], ([root, chart, autoresize], _, cleanup) => {
|
||||
if (root && chart && autoresize) {
|
||||
const autoresizeOptions = autoresize === true ? {} : autoresize;
|
||||
const { throttle: wait = 100, onResize } = autoresizeOptions;
|
||||
if (root && chart && autoresize) {
|
||||
const { offsetWidth, offsetHeight } = root;
|
||||
const autoresizeOptions = autoresize === true ? {} : autoresize;
|
||||
const { throttle: wait = 100, onResize } = autoresizeOptions;
|
||||
|
||||
const callback = () => {
|
||||
chart.resize();
|
||||
onResize?.();
|
||||
};
|
||||
let initialResizeTriggered = false;
|
||||
|
||||
resizeListener = wait ? throttle(callback, wait) : callback;
|
||||
addListener(root, resizeListener);
|
||||
}
|
||||
const callback = () => {
|
||||
chart.resize();
|
||||
onResize?.();
|
||||
};
|
||||
|
||||
cleanup(() => {
|
||||
if (root && resizeListener) {
|
||||
removeListener(root, resizeListener);
|
||||
const resizeCallback = wait ? throttle(callback, wait) : callback;
|
||||
|
||||
ro = new ResizeObserver(() => {
|
||||
// We just skip ResizeObserver's initial resize callback if the
|
||||
// size has not changed since the chart is rendered.
|
||||
if (!initialResizeTriggered) {
|
||||
initialResizeTriggered = true;
|
||||
if (
|
||||
root.offsetWidth === offsetWidth &&
|
||||
root.offsetHeight === offsetHeight
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Skip if container has zero size
|
||||
if (root.offsetWidth === 0 || root.offsetHeight === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
resizeCallback();
|
||||
});
|
||||
ro.observe(root);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
onCleanup(() => {
|
||||
if (ro) {
|
||||
ro.disconnect();
|
||||
ro = null;
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export const autoresizeProps = {
|
||||
autoresize: [Boolean, Object] as PropType<AutoresizeProp>
|
||||
autoresize: [Boolean, Object] as PropType<AutoResize>,
|
||||
};
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
export * from "./api";
|
||||
export * from "./autoresize";
|
||||
export * from "./loading";
|
||||
export * from "./slot";
|
||||
|
||||
@ -1,28 +1,24 @@
|
||||
import { unwrapInjected } from "../utils";
|
||||
import {
|
||||
inject,
|
||||
computed,
|
||||
watchEffect,
|
||||
type Ref,
|
||||
type InjectionKey,
|
||||
type PropType
|
||||
} from "vue-demi";
|
||||
import type { EChartsType, LoadingOptions } from "../types";
|
||||
import { inject, computed, watchEffect, toValue } from "vue";
|
||||
|
||||
export const LOADING_OPTIONS_KEY =
|
||||
"ecLoadingOptions" as unknown as InjectionKey<
|
||||
LoadingOptions | Ref<LoadingOptions>
|
||||
>;
|
||||
import type { Ref, InjectionKey, PropType } from "vue";
|
||||
import type {
|
||||
EChartsType,
|
||||
LoadingOptions,
|
||||
LoadingOptionsInjection,
|
||||
} from "../types";
|
||||
|
||||
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(() => {
|
||||
@ -41,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
@ -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>
|
||||
>;
|
||||
@ -1,6 +0,0 @@
|
||||
{
|
||||
"files": [
|
||||
"./Demo.vue",
|
||||
"./examples/RadarChart.vue"
|
||||
],
|
||||
}
|
||||
@ -3,5 +3,5 @@ import ECharts, * as exported from "./index";
|
||||
|
||||
export default {
|
||||
...ECharts,
|
||||
...exported
|
||||
...exported,
|
||||
};
|
||||
|
||||
63
src/index.vue2.d.ts
vendored
@ -1,63 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
import type { Ref, DefineComponent } from "vue-demi";
|
||||
import type {
|
||||
Option,
|
||||
InitOptions,
|
||||
UpdateOptions,
|
||||
EChartsType,
|
||||
Emits
|
||||
} from "./types";
|
||||
|
||||
declare const LOADING_OPTIONS_KEY = "ecLoadingOptions";
|
||||
declare const THEME_KEY = "ecTheme";
|
||||
declare const INIT_OPTIONS_KEY = "ecInitOptions";
|
||||
declare const UPDATE_OPTIONS_KEY = "ecUpdateOptions";
|
||||
|
||||
declare type ChartProps = {
|
||||
loading?: boolean;
|
||||
loadingOptions?: Record<string, unknown>;
|
||||
autoresize?: boolean;
|
||||
option?: Option;
|
||||
theme?: string | Record<string, unknown>;
|
||||
initOptions?: InitOptions;
|
||||
updateOptions?: UpdateOptions;
|
||||
group?: string;
|
||||
manualUpdate?: boolean;
|
||||
};
|
||||
|
||||
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,
|
||||
{
|
||||
root: Ref<HTMLElement | undefined>;
|
||||
chart: Ref<EChartsType | undefined>;
|
||||
},
|
||||
{},
|
||||
{},
|
||||
ChartMethods,
|
||||
{},
|
||||
{},
|
||||
Emits
|
||||
>;
|
||||
|
||||
export default Chart;
|
||||
export { INIT_OPTIONS_KEY, LOADING_OPTIONS_KEY, THEME_KEY, UPDATE_OPTIONS_KEY };
|
||||
@ -1 +1 @@
|
||||
x-vue-echarts{display:block;width:100%;height:100%;min-width:0}x-vue-echarts>[_echarts_instance_]{width:100%;height:100%}
|
||||
x-vue-echarts{display:block;width:100%;height:100%;min-width:0;}
|
||||
16
src/style.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
38
src/types.ts
@ -1,29 +1,30 @@
|
||||
import { init } from "echarts/core";
|
||||
import type {
|
||||
SetOptionOpts,
|
||||
ECElementEvent,
|
||||
ElementEvent,
|
||||
EChartsOption
|
||||
} from "echarts";
|
||||
import type { Ref } from "vue";
|
||||
|
||||
export type Injection<T> = T | null | Ref<T | null> | { value: T | null };
|
||||
import type { SetOptionOpts, ECElementEvent, ElementEvent } from "echarts/core";
|
||||
import type { MaybeRefOrGetter } from "vue";
|
||||
|
||||
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;
|
||||
export type Option = EChartsOption;
|
||||
|
||||
export type SetOptionType = EChartsType["setOption"];
|
||||
export type Option = Parameters<SetOptionType>[0];
|
||||
|
||||
export type AutoResize =
|
||||
| boolean
|
||||
| {
|
||||
throttle?: number;
|
||||
onResize?: () => void;
|
||||
};
|
||||
|
||||
export type LoadingOptions = {
|
||||
text?: string;
|
||||
@ -39,6 +40,7 @@ export type LoadingOptions = {
|
||||
lineWidth?: number;
|
||||
zlevel?: number;
|
||||
};
|
||||
export type LoadingOptionsInjection = Injection<LoadingOptions>;
|
||||
|
||||
type MouseEventName =
|
||||
| "click"
|
||||
@ -94,19 +96,19 @@ type OtherEventName =
|
||||
| "globalcursortaken";
|
||||
|
||||
type MouseEmits = {
|
||||
[key in MouseEventName]: (params: ECElementEvent) => boolean;
|
||||
[key in MouseEventName]: (params: ECElementEvent) => void;
|
||||
};
|
||||
|
||||
type ZRenderEmits = {
|
||||
[key in ZRenderEventName]: (params: ElementEvent) => boolean;
|
||||
[key in ZRenderEventName]: (params: ElementEvent) => void;
|
||||
};
|
||||
|
||||
type OtherEmits = {
|
||||
[key in OtherEventName]: null;
|
||||
[key in OtherEventName]: (params: any) => void;
|
||||
};
|
||||
|
||||
export type Emits = MouseEmits &
|
||||
OtherEmits & {
|
||||
rendered: (params: { elapsedTime: number }) => boolean;
|
||||
finished: () => boolean;
|
||||
rendered: (params: { elapsedTime: number }) => void;
|
||||
finished: () => void;
|
||||
} & ZRenderEmits;
|
||||
|
||||
34
src/utils.ts
@ -1,10 +1,4 @@
|
||||
import { unref } 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
|
||||
@ -22,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 = unref(injection);
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
if (value && typeof value === "object" && "value" in value) {
|
||||
return value.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 value || defaultValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
35
src/wc.ts
@ -19,31 +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",
|
||||
`class EChartsElement extends HTMLElement {
|
||||
__dispose = null;
|
||||
class ECElement extends HTMLElement implements EChartsElement {
|
||||
__dispose: (() => void) | null = null;
|
||||
|
||||
disconnectedCallback() {
|
||||
if (this.__dispose) {
|
||||
this.__dispose();
|
||||
this.__dispose = null;
|
||||
disconnectedCallback() {
|
||||
if (this.__dispose) {
|
||||
this.__dispose();
|
||||
this.__dispose = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (customElements.get(tag) == null) {
|
||||
customElements.define(tag, EChartsElement);
|
||||
}
|
||||
`
|
||||
);
|
||||
reg(TAG_NAME);
|
||||
} catch (e) {
|
||||
if (customElements.get(TAG_NAME) == null) {
|
||||
customElements.define(TAG_NAME, ECElement);
|
||||
}
|
||||
} catch {
|
||||
return (registered = false);
|
||||
}
|
||||
|
||||
|
||||
@ -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
@ -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
@ -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
@ -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
@ -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()],
|
||||
},
|
||||
},
|
||||
});
|
||||
@ -1,34 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const nested = require("postcss-nested");
|
||||
|
||||
module.exports = {
|
||||
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.plugin("define").tap(([options]) => [
|
||||
{
|
||||
...options,
|
||||
__CSP__: "false"
|
||||
}
|
||||
]);
|
||||
},
|
||||
devServer: {
|
||||
allowedHosts: "all"
|
||||
}
|
||||
};
|
||||