mirror of
https://github.com/ecomfe/vue-echarts.git
synced 2025-11-05 20:36:09 +08:00
Compare commits
104 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 | |||
| 8132842be7 | |||
| a2f8f0010a | |||
| 70c32603d4 | |||
| 19cc931595 | |||
| 21fae71b9e | |||
| ccb951e11b | |||
| 1a3d7e2004 | |||
| c41d55b054 | |||
| 4484b4e180 | |||
| 705bff310f | |||
| 5985dd8c86 | |||
| ef8d368638 | |||
| 125cbd3d53 | |||
| bd862426ea | |||
| 29bf99420d | |||
| f5bebc0920 | |||
| b843f805a5 | |||
| 66988fab4f | |||
| c103128c99 | |||
| eb59b4b185 | |||
| 03a3a6a401 | |||
| 088f18ac80 | |||
| 3e4615f94b | |||
| e224c49951 | |||
| f8e7597193 | |||
| aa22d332ed | |||
| 82a098c416 | |||
| 3da9f285ff | |||
| e48067c5d3 | |||
| 53ebc2941e | |||
| e8697382a1 | |||
| 47f7885f19 | |||
| 9c34d682c4 | |||
| 87d4811509 | |||
| d082883bc5 | |||
| b86280e1f0 | |||
| 6159c7d684 | |||
| 4313d301e4 | |||
| c13b482e12 | |||
| b003b07222 | |||
| 89d15a9ee3 | |||
| 81272bfa0f | |||
| f554c89c58 | |||
| 5f7f9c0d4c | |||
| f5819e4361 | |||
| 16b9274ae1 | |||
| cf963efc6d | |||
| fd36c5120b | |||
| 4fab6c272b | |||
| 0edfb3936c | |||
| ed4bd392ff |
24
.eslintrc.js
24
.eslintrc.js
@ -1,24 +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"
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ["*.ts"],
|
||||
extends: [
|
||||
"@vue/typescript/recommended",
|
||||
"@vue/prettier/@typescript-eslint"
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
20
.eslintrc.json
Normal file
20
.eslintrc.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"root": true,
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:vue/vue3-essential",
|
||||
"eslint:recommended",
|
||||
"@vue/eslint-config-typescript",
|
||||
"@vue/eslint-config-prettier/skip-formatting"
|
||||
],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2020,
|
||||
"parser": "@typescript-eslint/parser"
|
||||
},
|
||||
"rules": {
|
||||
"no-console": "off",
|
||||
"vue/multi-word-component-names": "off"
|
||||
}
|
||||
}
|
||||
4
.stackblitzrc
Normal file
4
.stackblitzrc
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"installDependencies": true,
|
||||
"startCommand": "pnpm serve"
|
||||
}
|
||||
111
CHANGELOG.md
111
CHANGELOG.md
@ -1,3 +1,114 @@
|
||||
## 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.
|
||||
|
||||
## 6.6.1
|
||||
|
||||
* 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.
|
||||
|
||||
## 6.5.5
|
||||
|
||||
* 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.
|
||||
|
||||
## 6.5.3
|
||||
|
||||
* Fixed default behavior for `notMerge` option (#691).
|
||||
|
||||
## 6.5.2
|
||||
|
||||
* Added `dist/csp/*` to support strict CSP with extracted CSS file.
|
||||
|
||||
## 6.5.1
|
||||
|
||||
* 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`.
|
||||
|
||||
## 6.4.1
|
||||
|
||||
* Improve typings for mouse event params.
|
||||
|
||||
234
README.md
234
README.md
@ -1,39 +1,21 @@
|
||||
# Vue-ECharts
|
||||
<h1 align="center">Vue-ECharts</h1>
|
||||
|
||||
> Vue.js component for Apache ECharts.
|
||||
<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>
|
||||
|
||||
> [🇨🇳 中文版](./README.zh-Hans.md)
|
||||
---
|
||||
|
||||
Uses [Apache ECharts](https://echarts.apache.org/en/index.html) 5 and works for both [Vue.js](https://vuejs.org/) 2/3.
|
||||
|
||||
## 💡 Heads up 💡
|
||||
|
||||
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 v6? Read v6 docs [here →](https://github.com/ecomfe/vue-echarts/tree/6.x)
|
||||
|
||||
## Installation & Usage
|
||||
|
||||
### npm & ESM
|
||||
|
||||
```bash
|
||||
$ npm install echarts vue-echarts
|
||||
```
|
||||
|
||||
To make `vue-echarts` work for _Vue 2_ (<2.7.0), you need to have `@vue/composition-api` installed:
|
||||
### npm
|
||||
|
||||
```sh
|
||||
npm i -D @vue/composition-api
|
||||
npm add echarts vue-echarts
|
||||
```
|
||||
|
||||
If you are using _NuxtJS_ on top of _Vue 2_ (<2.7.0), you'll also need `@nuxtjs/composition-api`:
|
||||
|
||||
```sh
|
||||
npm i -D @nuxtjs/composition-api
|
||||
```
|
||||
|
||||
And then add `'@nuxtjs/composition-api/module'` in the `buildModules` option in your `nuxt.config.js`.
|
||||
|
||||
#### Example
|
||||
|
||||
<details>
|
||||
@ -208,7 +190,12 @@ export default {
|
||||
|
||||
</details>
|
||||
|
||||
We encourage manually importing components and charts from ECharts for smaller bundle size. See all supported renderers/charts/components [here →](https://github.com/apache/echarts/blob/master/src/echarts.all.ts)
|
||||
> [!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:
|
||||
|
||||
@ -216,7 +203,7 @@ 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`.
|
||||
|
||||
@ -225,9 +212,9 @@ Drop `<script>` inside your HTML file and access the component via `window.VueEC
|
||||
|
||||
<!-- vue3Scripts:start -->
|
||||
```html
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue@3.2.37"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/echarts@5.3.3"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue-echarts@6.4.0"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue@3.4.33"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/echarts@5.5.1"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue-echarts@7.0.0"></script>
|
||||
```
|
||||
<!-- vue3Scripts:end -->
|
||||
|
||||
@ -245,9 +232,9 @@ app.component('v-chart', VueECharts)
|
||||
|
||||
<!-- vue2Scripts:start -->
|
||||
```html
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.5"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/echarts@5.3.3"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue-echarts@6.4.0"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.16"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/echarts@5.5.1"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue-echarts@7.0.0"></script>
|
||||
```
|
||||
<!-- vue2Scripts:end -->
|
||||
|
||||
@ -290,9 +277,9 @@ See more examples [here](https://github.com/ecomfe/vue-echarts/tree/main/src/dem
|
||||
|
||||
Group name to be used in chart [connection](https://echarts.apache.org/en/api.html#echarts.connect). See `echartsInstance.group` [here →](https://echarts.apache.org/en/api.html#echartsInstance.group)
|
||||
|
||||
- `autoresize: boolean` (default: `false`)
|
||||
- `autoresize: boolean | { throttle?: number, onResize?: () => void }` (default: `false`)
|
||||
|
||||
Whether the chart should be resized automatically whenever its root is resized.
|
||||
Whether the chart should be resized automatically whenever its root is resized. Use the options object to specify a custom throttle delay (in milliseconds) and/or an extra resize callback function.
|
||||
|
||||
- `loading: boolean` (default: `false`)
|
||||
|
||||
@ -308,6 +295,78 @@ 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.
|
||||
|
||||
### Events
|
||||
|
||||
You can bind events with Vue's `v-on` directive.
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<v-chart :option="option" @highlight="handleHighlight" />
|
||||
</template>
|
||||
```
|
||||
|
||||
> **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:
|
||||
|
||||
- `highlight` [→](https://echarts.apache.org/en/api.html#events.highlight)
|
||||
- `downplay` [→](https://echarts.apache.org/en/api.html#events.downplay)
|
||||
- `selectchanged` [→](https://echarts.apache.org/en/api.html#events.selectchanged)
|
||||
- `legendselectchanged` [→](https://echarts.apache.org/en/api.html#events.legendselectchanged)
|
||||
- `legendselected` [→](https://echarts.apache.org/en/api.html#events.legendselected)
|
||||
- `legendunselected` [→](https://echarts.apache.org/en/api.html#events.legendunselected)
|
||||
- `legendselectall` [→](https://echarts.apache.org/en/api.html#events.legendselectall)
|
||||
- `legendinverseselect` [→](https://echarts.apache.org/en/api.html#events.legendinverseselect)
|
||||
- `legendscroll` [→](https://echarts.apache.org/en/api.html#events.legendscroll)
|
||||
- `datazoom` [→](https://echarts.apache.org/en/api.html#events.datazoom)
|
||||
- `datarangeselected` [→](https://echarts.apache.org/en/api.html#events.datarangeselected)
|
||||
- `timelinechanged` [→](https://echarts.apache.org/en/api.html#events.timelinechanged)
|
||||
- `timelineplaychanged` [→](https://echarts.apache.org/en/api.html#events.timelineplaychanged)
|
||||
- `restore` [→](https://echarts.apache.org/en/api.html#events.restore)
|
||||
- `dataviewchanged` [→](https://echarts.apache.org/en/api.html#events.dataviewchanged)
|
||||
- `magictypechanged` [→](https://echarts.apache.org/en/api.html#events.magictypechanged)
|
||||
- `geoselectchanged` [→](https://echarts.apache.org/en/api.html#events.geoselectchanged)
|
||||
- `geoselected` [→](https://echarts.apache.org/en/api.html#events.geoselected)
|
||||
- `geounselected` [→](https://echarts.apache.org/en/api.html#events.geounselected)
|
||||
- `axisareaselected` [→](https://echarts.apache.org/en/api.html#events.axisareaselected)
|
||||
- `brush` [→](https://echarts.apache.org/en/api.html#events.brush)
|
||||
- `brushEnd` [→](https://echarts.apache.org/en/api.html#events.brushEnd)
|
||||
- `brushselected` [→](https://echarts.apache.org/en/api.html#events.brushselected)
|
||||
- `globalcursortaken` [→](https://echarts.apache.org/en/api.html#events.globalcursortaken)
|
||||
- `rendered` [→](https://echarts.apache.org/en/api.html#events.rendered)
|
||||
- `finished` [→](https://echarts.apache.org/en/api.html#events.finished)
|
||||
- Mouse events
|
||||
- `click` [→](https://echarts.apache.org/en/api.html#events.Mouse%20events.click)
|
||||
- `dblclick` [→](https://echarts.apache.org/en/api.html#events.Mouse%20events.dblclick)
|
||||
- `mouseover` [→](https://echarts.apache.org/en/api.html#events.Mouse%20events.mouseover)
|
||||
- `mouseout` [→](https://echarts.apache.org/en/api.html#events.Mouse%20events.mouseout)
|
||||
- `mousemove` [→](https://echarts.apache.org/en/api.html#events.Mouse%20events.mousemove)
|
||||
- `mousedown` [→](https://echarts.apache.org/en/api.html#events.Mouse%20events.mousedown)
|
||||
- `mouseup` [→](https://echarts.apache.org/en/api.html#events.Mouse%20events.mouseup)
|
||||
- `globalout` [→](https://echarts.apache.org/en/api.html#events.Mouse%20events.globalout)
|
||||
- `contextmenu` [→](https://echarts.apache.org/en/api.html#events.Mouse%20events.contextmenu)
|
||||
- ZRender events
|
||||
- `zr:click`
|
||||
- `zr:mousedown`
|
||||
- `zr:mouseup`
|
||||
- `zr:mousewheel`
|
||||
- `zr:dblclick`
|
||||
- `zr:contextmenu`
|
||||
|
||||
See supported events [here →](https://echarts.apache.org/en/api.html#events)
|
||||
|
||||
#### Native DOM Events
|
||||
|
||||
As Vue-ECharts binds events to the ECharts instance by default, there is some caveat when using native DOM events. You need to prefix the event name with `native:` to bind native DOM events (or you can use the `.native` modifier in Vue 2, which is dropped in Vue 3).
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<v-chart @native:click="handleClick" />
|
||||
</template>
|
||||
```
|
||||
|
||||
### 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:
|
||||
@ -391,104 +450,23 @@ import { THEME_KEY } from 'vue-echarts'
|
||||
|
||||
Static methods can be accessed from [`echarts` itself](https://echarts.apache.org/en/api.html#echarts).
|
||||
|
||||
### Events
|
||||
## CSP: `style-src` or `style-src-elem`
|
||||
|
||||
You can bind events with Vue's `v-on` directive.
|
||||
If you are applying a CSP to prevent inline `<style>` injection, you need to use `vue-echarts/csp` instead of `vue-echarts` and include `vue-echarts/csp/style.css` manually.
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<v-chart :option="option" @highlight="handleHighlight" />
|
||||
</template>
|
||||
```
|
||||
## Migration to v7
|
||||
|
||||
> **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:
|
||||
|
||||
- `highlight` [→](https://echarts.apache.org/en/api.html#events.highlight)
|
||||
- `downplay` [→](https://echarts.apache.org/en/api.html#events.downplay)
|
||||
- `selectchanged` [→](https://echarts.apache.org/en/api.html#events.selectchanged)
|
||||
- `legendselectchanged` [→](https://echarts.apache.org/en/api.html#events.legendselectchanged)
|
||||
- `legendselected` [→](https://echarts.apache.org/en/api.html#events.legendselected)
|
||||
- `legendunselected` [→](https://echarts.apache.org/en/api.html#events.legendunselected)
|
||||
- `legendselectall` [→](https://echarts.apache.org/en/api.html#events.legendselectall)
|
||||
- `legendinverseselect` [→](https://echarts.apache.org/en/api.html#events.legendinverseselect)
|
||||
- `legendscroll` [→](https://echarts.apache.org/en/api.html#events.legendscroll)
|
||||
- `datazoom` [→](https://echarts.apache.org/en/api.html#events.datazoom)
|
||||
- `datarangeselected` [→](https://echarts.apache.org/en/api.html#events.datarangeselected)
|
||||
- `timelinechanged` [→](https://echarts.apache.org/en/api.html#events.timelinechanged)
|
||||
- `timelineplaychanged` [→](https://echarts.apache.org/en/api.html#events.timelineplaychanged)
|
||||
- `restore` [→](https://echarts.apache.org/en/api.html#events.restore)
|
||||
- `dataviewchanged` [→](https://echarts.apache.org/en/api.html#events.dataviewchanged)
|
||||
- `magictypechanged` [→](https://echarts.apache.org/en/api.html#events.magictypechanged)
|
||||
- `geoselectchanged` [→](https://echarts.apache.org/en/api.html#events.geoselectchanged)
|
||||
- `geoselected` [→](https://echarts.apache.org/en/api.html#events.geoselected)
|
||||
- `geounselected` [→](https://echarts.apache.org/en/api.html#events.geounselected)
|
||||
- `axisareaselected` [→](https://echarts.apache.org/en/api.html#events.axisareaselected)
|
||||
- `brush` [→](https://echarts.apache.org/en/api.html#events.brush)
|
||||
- `brushEnd` [→](https://echarts.apache.org/en/api.html#events.brushEnd)
|
||||
- `brushselected` [→](https://echarts.apache.org/en/api.html#events.brushselected)
|
||||
- `globalcursortaken` [→](https://echarts.apache.org/en/api.html#events.globalcursortaken)
|
||||
- `rendered` [→](https://echarts.apache.org/en/api.html#events.rendered)
|
||||
- `finished` [→](https://echarts.apache.org/en/api.html#events.finished)
|
||||
- Mouse events
|
||||
- `click` [→](https://echarts.apache.org/en/api.html#events.Mouse%20events.click)
|
||||
- `dblclick` [→](https://echarts.apache.org/en/api.html#events.Mouse%20events.dblclick)
|
||||
- `mouseover` [→](https://echarts.apache.org/en/api.html#events.Mouse%20events.mouseover)
|
||||
- `mouseout` [→](https://echarts.apache.org/en/api.html#events.Mouse%20events.mouseout)
|
||||
- `mousemove` [→](https://echarts.apache.org/en/api.html#events.Mouse%20events.mousemove)
|
||||
- `mousedown` [→](https://echarts.apache.org/en/api.html#events.Mouse%20events.mousedown)
|
||||
- `mouseup` [→](https://echarts.apache.org/en/api.html#events.Mouse%20events.mouseup)
|
||||
- `globalout` [→](https://echarts.apache.org/en/api.html#events.Mouse%20events.globalout)
|
||||
- `contextmenu` [→](https://echarts.apache.org/en/api.html#events.Mouse%20events.contextmenu)
|
||||
- ZRender events
|
||||
- `zr:click`
|
||||
- `zr:mousedown`
|
||||
- `zr:mouseup`
|
||||
- `zr:mousewheel`
|
||||
- `zr:dblclick`
|
||||
- `zr:contextmenu`
|
||||
|
||||
See supported events [here →](https://echarts.apache.org/en/api.html#events)
|
||||
|
||||
## Migration to v6
|
||||
|
||||
> 💡 Please make sure to read the [migration guide](https://echarts.apache.org/en/tutorial.html#ECharts%205%20Upgrade%20Guide) for ECharts 5 as well.
|
||||
|
||||
The following breaking changes are introduced in `vue-echarts@6`:
|
||||
|
||||
### Vue 2 support
|
||||
|
||||
- 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.
|
||||
|
||||
### Props
|
||||
|
||||
- `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.
|
||||
|
||||
### 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.
|
||||
|
||||
### Computed getters
|
||||
|
||||
- Computed getters (`width`, `height`, `isDisposed` and `computedOptions`) are removed. Use the **`getWidth`, `getHeight`, `isDisposed` and `getOption`** methods instead.
|
||||
|
||||
### Styles
|
||||
|
||||
- Now the root element of the component have **`100%×100%`** size by default, instead of `600×400`.
|
||||
Read the breaking changes document in the [release log](https://github.com/ecomfe/vue-echarts/releases/tag/v7.0.0-beta.0) and the migration shoud be straightforward.
|
||||
|
||||
## Local development
|
||||
|
||||
```bash
|
||||
$ npm i
|
||||
$ npm run serve
|
||||
```sh
|
||||
pnpm i
|
||||
pnpm serve
|
||||
```
|
||||
|
||||
Open `http://localhost:8080` 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,37 +1,21 @@
|
||||
# Vue-ECharts
|
||||
<h1 align="center">Vue-ECharts</h1>
|
||||
|
||||
> Apache ECharts 的 Vue.js 组件。
|
||||
<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>
|
||||
|
||||
使用 [Apache ECharts](https://echarts.apache.org/zh/index.html) 5,同时支持 [Vue.js](https://vuejs.org/) 2/3。
|
||||
---
|
||||
|
||||
## 💡 注意 💡
|
||||
|
||||
若您准备从 `vue-echarts` ≤ 5 的版本迁移到新版本,请在升级 v6 前阅读 _[迁移到 v6](#迁移到-v6)_ 部分文档。
|
||||
|
||||
没准备好的话,可以继续阅读老版本的文档。[前往 →](https://github.com/ecomfe/vue-echarts/blob/5.x/README.zh_CN.md)
|
||||
> 还在使用 v6?可以继续阅读老版本的文档。[前往 →](https://github.com/ecomfe/vue-echarts/blob/6.x/README.zh_CN.md)
|
||||
|
||||
## 安装 & 使用
|
||||
|
||||
### npm & ESM
|
||||
|
||||
```bash
|
||||
$ npm install echarts vue-echarts
|
||||
```
|
||||
|
||||
要在 _Vue 2_(<2.7.0)下使用 `vue-echarts`,需要确保 `@vue/composition-api` 已经安装:
|
||||
### npm
|
||||
|
||||
```sh
|
||||
npm i -D @vue/composition-api
|
||||
npm add echarts vue-echarts
|
||||
```
|
||||
|
||||
如果你在使用基于 _Vue 2_(<2.7.0)的 _NuxtJS_,那么还需要安装 `@nuxtjs/composition-api`:
|
||||
|
||||
```sh
|
||||
npm i -D @nuxtjs/composition-api
|
||||
```
|
||||
|
||||
然后在 `nuxt.config.js` 的 `buildModules` 选项中添加 `'@nuxtjs/composition-api/module'`。
|
||||
|
||||
#### 示例
|
||||
|
||||
<details>
|
||||
@ -206,7 +190,12 @@ export default {
|
||||
|
||||
</details>
|
||||
|
||||
为了更小的打包体积,我们建议手动从 ECharts 引入单个图表和组件。请参考所有支持的渲染器/图表/组件。[前往 →](https://github.com/apache/echarts/blob/master/src/echarts.all.ts)
|
||||
> [!IMPORTANT]
|
||||
> 我们鼓励手动从 ECharts 中引入组件和图表,以减小打包体积。我们已经为此构建了一个[导入代码生成器](https://vue-echarts.dev/#codegen)。你只需要把`option` 代码粘贴进去,就可以得到精确的导入代码。
|
||||
>
|
||||
> 
|
||||
>
|
||||
> [试一试 →](https://vue-echarts.dev/#codegen)
|
||||
|
||||
但如果你实在需要全量引入 ECharts 从而无需手动引入模块,只需要在代码中添加:
|
||||
|
||||
@ -214,7 +203,7 @@ export default {
|
||||
import "echarts";
|
||||
```
|
||||
|
||||
### CDN & 全局变量
|
||||
### CDN
|
||||
|
||||
用如下方式在 HTML 中插入 `<script>` 标签,并且通过 `window.VueECharts` 来访问组件接口:
|
||||
|
||||
@ -223,9 +212,9 @@ import "echarts";
|
||||
|
||||
<!-- vue3Scripts:start -->
|
||||
```html
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue@3.2.37"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/echarts@5.3.3"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue-echarts@6.4.0"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue@3.4.33"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/echarts@5.5.1"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue-echarts@7.0.0"></script>
|
||||
```
|
||||
<!-- vue3Scripts:end -->
|
||||
|
||||
@ -243,9 +232,9 @@ app.component('v-chart', VueECharts)
|
||||
|
||||
<!-- vue2Scripts:start -->
|
||||
```html
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.5"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/echarts@5.3.3"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue-echarts@6.4.0"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.16"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/echarts@5.5.1"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue-echarts@7.0.0"></script>
|
||||
```
|
||||
<!-- vue2Scripts:end -->
|
||||
|
||||
@ -288,9 +277,9 @@ Vue.component("v-chart", VueECharts);
|
||||
|
||||
图表的分组,用于[联动](https://echarts.apache.org/zh/api.html#echarts.connect)。请参考 `echartsInstance.group`。[前往 →](https://echarts.apache.org/zh/api.html#echartsInstance.group)
|
||||
|
||||
- `autoresize: boolean`(默认值`false`)
|
||||
- `autoresize: boolean | { throttle?: number, onResize?: () => void }`(默认值`false`)
|
||||
|
||||
图表在组件根元素尺寸变化时是否需要自动进行重绘。
|
||||
图表在组件根元素尺寸变化时是否需要自动进行重绘。也可以传入一个选项对象来指定自定义的节流延迟和尺寸变化时的额外回调函数。
|
||||
|
||||
- `loading: boolean`(默认值:`false`)
|
||||
|
||||
@ -368,6 +357,16 @@ Vue-ECharts 支持如下事件:
|
||||
|
||||
请参考支持的事件列表。[前往 →](https://echarts.apache.org/zh/api.html#events)
|
||||
|
||||
#### 原生 DOM 事件
|
||||
|
||||
由于 Vue-ECharts 默认将事件绑定到 ECharts 实例,因此在使用原生 DOM 事件时需要做一些特殊处理。你需要在事件名称前加上 `native:` 前缀来绑定原生 DOM 事件(可以在 Vue 2 中也可以使用 `.native` 修饰符,但这在 Vue 3 中已被废弃)。
|
||||
|
||||
```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` 提供上下文配置:
|
||||
@ -451,42 +450,28 @@ import { THEME_KEY } from 'vue-echarts'
|
||||
|
||||
静态方法请直接通过 [`echarts` 本身](https://echarts.apache.org/zh/api.html#echarts)进行调用。
|
||||
|
||||
## 迁移到 v6
|
||||
|
||||
> 💡 请确保同时查阅 ECharts 5 的[升级指南](https://echarts.apache.org/zh/tutorial.html#ECharts%205%20%E5%8D%87%E7%BA%A7%E6%8C%87%E5%8D%97)。
|
||||
|
||||
`vue-echarts@6` 引入了如下破坏性变更:
|
||||
## CSP: `style-src` 或 `style-src-elem`
|
||||
|
||||
### Vue 2 支持
|
||||
如果你正在应用 CSP 来防止内联 `<style>` 注入,则需要使用 `vue-echarts/csp` 代替 `vue-echarts`,并手动引入 `vue-echarts/csp/style.css`。
|
||||
|
||||
- 要在 `vue@2.7.0` 之前的版本中使用 Vue-ECharts,必须安装 `@vue/composition-api`。
|
||||
## 迁移到 v7
|
||||
|
||||
### Prop
|
||||
Translate:
|
||||
Read the breaking changes document in the [release log](https://github.com/ecomfe/vue-echarts/releases/tag/v7.0.0-beta.0) and the migration shoud be straightforward.
|
||||
|
||||
- `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`。
|
||||
请阅读[发布日志](https://github.com/ecomfe/vue-echarts/releases/tag/v7.0.0-beta.0)中的变更记录,之后迁移过程应该会相对简单。
|
||||
|
||||
## 本地开发
|
||||
|
||||
```bash
|
||||
$ npm i
|
||||
$ npm run serve
|
||||
```sh
|
||||
pnpm i
|
||||
pnpm serve
|
||||
```
|
||||
|
||||
打开 `http://localhost:8080` 来查看 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"]
|
||||
};
|
||||
3
babel.config.json
Normal file
3
babel.config.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"presets": ["@vue/cli-plugin-babel/preset"]
|
||||
}
|
||||
130
package.json
130
package.json
@ -1,82 +1,90 @@
|
||||
{
|
||||
"name": "vue-echarts",
|
||||
"version": "6.4.1",
|
||||
"description": "Vue.js component for Apache ECharts.",
|
||||
"version": "7.0.1",
|
||||
"description": "Vue.js component for Apache ECharts™.",
|
||||
"license": "MIT",
|
||||
"repository": "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",
|
||||
"build": "pnpm run docs && rimraf dist && pnpm run build:rollup",
|
||||
"build:rollup": "vue-demi-switch 3 && rollup -c rollup.config.js",
|
||||
"lint": "vue-cli-service lint",
|
||||
"publint": "publint",
|
||||
"build:demo": "vue-cli-service build",
|
||||
"docs": "node -r esm ./scripts/docs.js",
|
||||
"postinstall": "node ./scripts/postinstall.js",
|
||||
"prepare": "pnpm run build"
|
||||
"docs": "node ./scripts/docs.mjs",
|
||||
"prepublishOnly": "pnpm run build && publint"
|
||||
},
|
||||
"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",
|
||||
"./csp": "./dist/csp/index.js",
|
||||
"./csp/style.css": "./dist/csp/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"
|
||||
],
|
||||
"dependencies": {
|
||||
"resize-detector": "^0.3.0",
|
||||
"vue-demi": "^0.13.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.17.10",
|
||||
"@rollup/plugin-node-resolve": "^11.1.1",
|
||||
"@typescript-eslint/eslint-plugin": "^4.15.1",
|
||||
"@typescript-eslint/parser": "^4.15.1",
|
||||
"@vue/cli-plugin-babel": "^5.0.4",
|
||||
"@vue/cli-plugin-eslint": "^5.0.4",
|
||||
"@vue/cli-plugin-typescript": "^5.0.4",
|
||||
"@vue/cli-service": "^5.0.4",
|
||||
"@vue/compiler-sfc": "^3.2.33",
|
||||
"@vue/composition-api": "^1.7.0",
|
||||
"@vue/eslint-config-prettier": "^6.0.0",
|
||||
"@vue/eslint-config-typescript": "^10.0.0",
|
||||
"comment-mark": "^1.0.0",
|
||||
"core-js": "^3.23.0",
|
||||
"echarts": "^5.3.2",
|
||||
"echarts-liquidfill": "^3.1.0",
|
||||
"eslint": "^7.20.0",
|
||||
"eslint-plugin-prettier": "^3.3.1",
|
||||
"eslint-plugin-vue": "^8.7.1",
|
||||
"esm": "^3.2.25",
|
||||
"postcss": "^8.3.0",
|
||||
"postcss-loader": "^5.0.0",
|
||||
"postcss-nested": "^5.0.5",
|
||||
"prettier": "^2.6.2",
|
||||
"qs": "^6.10.5",
|
||||
"raw-loader": "^4.0.2",
|
||||
"resize-detector": "^0.3.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"rollup": "^2.72.1",
|
||||
"rollup-plugin-dts": "^4.2.1",
|
||||
"rollup-plugin-styles": "^4.0.0",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"rollup-plugin-ts": "^2.0.7",
|
||||
"tslib": "^2.4.0",
|
||||
"typescript": "4.6.4",
|
||||
"vue": "^3.2.33",
|
||||
"vue2": "npm:vue@^2.7.14",
|
||||
"webpack": "^5.72.1"
|
||||
"vue-demi": "^0.13.11"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vue/composition-api": "^1.0.5",
|
||||
"echarts": "^5.1.2",
|
||||
"vue": "^2.6.12 || ^3.1.1"
|
||||
"@vue/runtime-core": "^3.0.0",
|
||||
"echarts": "^5.5.1",
|
||||
"vue": "^2.7.0 || ^3.1.1"
|
||||
},
|
||||
"jsdelivr": "dist/index.umd.min.js",
|
||||
"license": "MIT",
|
||||
"peerDependenciesMeta": {
|
||||
"@vue/composition-api": {
|
||||
"@vue/runtime-core": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"repository": "https://github.com/ecomfe/vue-echarts.git",
|
||||
"types": "dist/index.d.ts"
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.24.9",
|
||||
"@highlightjs/vue-plugin": "^2.1.0",
|
||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||
"@rollup/plugin-replace": "^5.0.7",
|
||||
"@typescript-eslint/eslint-plugin": "^7.17.0",
|
||||
"@typescript-eslint/parser": "^7.17.0",
|
||||
"@vercel/analytics": "^1.3.1",
|
||||
"@vue/cli-plugin-babel": "^5.0.8",
|
||||
"@vue/cli-plugin-eslint": "^5.0.8",
|
||||
"@vue/cli-plugin-typescript": "^5.0.8",
|
||||
"@vue/cli-service": "^5.0.8",
|
||||
"@vue/compiler-sfc": "^3.4.33",
|
||||
"@vue/eslint-config-prettier": "^9.0.0",
|
||||
"@vue/eslint-config-typescript": "^13.0.0",
|
||||
"@vueuse/core": "^10.11.0",
|
||||
"comment-mark": "^1.1.1",
|
||||
"core-js": "^3.37.1",
|
||||
"echarts": "^5.5.1",
|
||||
"echarts-gl": "^2.0.9",
|
||||
"echarts-liquidfill": "^3.1.0",
|
||||
"esbuild-wasm": "^0.23.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-prettier": "^5.2.1",
|
||||
"eslint-plugin-vue": "^9.27.0",
|
||||
"highlight.js": "^11.10.0",
|
||||
"pinia": "^2.1.7",
|
||||
"postcss": "^8.4.39",
|
||||
"postcss-loader": "^8.1.1",
|
||||
"postcss-nested": "^6.2.0",
|
||||
"prettier": "^3.3.3",
|
||||
"publint": "^0.2.9",
|
||||
"raw-loader": "^4.0.2",
|
||||
"resize-detector": "^0.3.0",
|
||||
"rimraf": "^6.0.1",
|
||||
"rollup": "^4.19.0",
|
||||
"rollup-plugin-dts": "^6.1.0",
|
||||
"rollup-plugin-esbuild": "^6.1.1",
|
||||
"rollup-plugin-import-css": "^3.5.0",
|
||||
"tslib": "^2.6.3",
|
||||
"typescript": "5.5.4",
|
||||
"vue": "^3.4.33",
|
||||
"vue2": "npm:vue@^2.7.16",
|
||||
"webpack": "^5.93.0"
|
||||
}
|
||||
}
|
||||
|
||||
12759
pnpm-lock.yaml
generated
12759
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -1,12 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="">
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<link href="https://fonts.googleapis.com/css?family=Inter:300,500;display=swap" rel="stylesheet">
|
||||
<title>Vue-ECharts: Vue.js component for Apache ECharts.</title>
|
||||
<title>Vue-ECharts: Vue.js component for Apache ECharts™.</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
|
||||
138
rollup.config.js
138
rollup.config.js
@ -1,75 +1,60 @@
|
||||
import typescript from "rollup-plugin-ts";
|
||||
import { terser } from "rollup-plugin-terser";
|
||||
import resolve from "@rollup/plugin-node-resolve";
|
||||
import styles from "rollup-plugin-styles";
|
||||
import { injectVueDemi } from "./scripts/rollup";
|
||||
import replace from "@rollup/plugin-replace";
|
||||
import esbuild from "rollup-plugin-esbuild";
|
||||
import { dts } from "rollup-plugin-dts";
|
||||
import css from "rollup-plugin-import-css";
|
||||
import { injectVueDemi } from "./scripts/rollup.mjs";
|
||||
|
||||
/**
|
||||
* Modifies the Rollup options for a build to support strict CSP
|
||||
* @param {import('rollup').RollupOptions} options the original options
|
||||
* @param {boolean} csp whether to support strict CSP
|
||||
* @returns {import('rollup').RollupOptions} the modified options
|
||||
*/
|
||||
function configBuild(options, csp) {
|
||||
const result = { ...options };
|
||||
const { plugins, output } = result;
|
||||
|
||||
result.plugins = [
|
||||
...(csp ? [replace({ __CSP__: `${csp}`, preventAssignment: true })] : []),
|
||||
...plugins,
|
||||
csp ? css({ output: "style.css" }) : css({ inject: true })
|
||||
];
|
||||
|
||||
// modify output file names
|
||||
if (csp && output) {
|
||||
result.output = (Array.isArray(output) ? output : [output]).map(output => {
|
||||
return {
|
||||
...output,
|
||||
file: output.file.replace(/^dist\//, "dist/csp/"),
|
||||
assetFileNames: "[name][extname]"
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** @type {import('rollup').RollupOptions[]} */
|
||||
const options = [
|
||||
const builds = [
|
||||
{
|
||||
input: "src/index.ts",
|
||||
plugins: [
|
||||
typescript({
|
||||
tsconfig: resolvedConfig => ({ ...resolvedConfig, declaration: true }),
|
||||
hook: {
|
||||
outputPath: (path, kind) =>
|
||||
kind === "declaration" ? "dist/index.d.ts" : path
|
||||
}
|
||||
}),
|
||||
styles()
|
||||
],
|
||||
external: ["vue-demi", "echarts/core", "resize-detector"],
|
||||
output: {
|
||||
file: "dist/index.esm.js",
|
||||
format: "esm",
|
||||
sourcemap: true
|
||||
}
|
||||
},
|
||||
{
|
||||
input: "src/index.ts",
|
||||
plugins: [typescript(), styles()],
|
||||
external: ["vue-demi", "echarts/core", "resize-detector"],
|
||||
plugins: [esbuild()],
|
||||
external: ["vue-demi", /^echarts/],
|
||||
output: [
|
||||
{
|
||||
file: "dist/index.esm.min.js",
|
||||
file: "dist/index.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(), styles()],
|
||||
external: ["vue-demi", "echarts", "echarts/core"],
|
||||
plugins: [esbuild({ minify: true })],
|
||||
external: ["vue-demi", /^echarts/],
|
||||
output: [
|
||||
{
|
||||
file: "dist/index.umd.js",
|
||||
file: "dist/index.min.js", // for unpkg/jsdelivr
|
||||
format: "umd",
|
||||
name: "VueECharts",
|
||||
exports: "default",
|
||||
@ -80,29 +65,26 @@ const options = [
|
||||
"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 options;
|
||||
export default [
|
||||
...builds.map(options => configBuild(options, false)),
|
||||
...builds.map(options => configBuild(options, true)),
|
||||
{
|
||||
input: "src/index.d.ts",
|
||||
plugins: [dts()],
|
||||
output: [
|
||||
{
|
||||
file: "dist/index.d.ts",
|
||||
format: "esm"
|
||||
},
|
||||
{
|
||||
file: "dist/csp/index.d.ts",
|
||||
format: "esm"
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
@ -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;
|
||||
@ -1,16 +1,15 @@
|
||||
import fs from "fs";
|
||||
import { resolve } from "path";
|
||||
import { readFileSync, writeFileSync } from "node:fs";
|
||||
import commentMark from "comment-mark";
|
||||
import { name, version } from "../package.json";
|
||||
import { getPackageMeta, resolvePath } from "./utils.mjs";
|
||||
|
||||
const { readFile, writeFile } = fs.promises;
|
||||
const { name, version } = getPackageMeta();
|
||||
|
||||
const CDN_PREFIX = "https://cdn.jsdelivr.net/npm/";
|
||||
|
||||
const DEP_VERSIONS = {
|
||||
"vue@3": "3.2.37",
|
||||
"vue@2": "2.7.5",
|
||||
echarts: "5.3.3",
|
||||
"vue@3": "3.4.33",
|
||||
"vue@2": "2.7.16",
|
||||
echarts: "5.5.1",
|
||||
[name]: version
|
||||
};
|
||||
|
||||
@ -39,29 +38,20 @@ const scripts = {
|
||||
};
|
||||
|
||||
const README_FILES = ["README.md", "README.zh-Hans.md"].map(name =>
|
||||
resolve(__dirname, "..", name)
|
||||
resolvePath(import.meta.url, "..", name)
|
||||
);
|
||||
|
||||
function exec() {
|
||||
return Promise.all(
|
||||
README_FILES.map(async file => {
|
||||
const content = await readFile(file, "utf8");
|
||||
README_FILES.forEach(file => {
|
||||
const content = readFileSync(file, "utf8");
|
||||
|
||||
return writeFile(
|
||||
file,
|
||||
commentMark(content, {
|
||||
vue2Scripts: getCodeBlock(scripts[2]),
|
||||
vue3Scripts: getCodeBlock(scripts[3])
|
||||
}),
|
||||
"utf8"
|
||||
);
|
||||
})
|
||||
writeFileSync(
|
||||
file,
|
||||
commentMark(content, {
|
||||
vue2Scripts: getCodeBlock(scripts[2]),
|
||||
vue3Scripts: getCodeBlock(scripts[3])
|
||||
}),
|
||||
"utf8"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
async function main() {
|
||||
await exec();
|
||||
console.log("README files updated.");
|
||||
}
|
||||
|
||||
main();
|
||||
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;
|
||||
}
|
||||
};
|
||||
17
scripts/rollup.mjs
Normal file
17
scripts/rollup.mjs
Normal file
@ -0,0 +1,17 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
const VUE_DEMI_IIFE = readFileSync(
|
||||
require.resolve("vue-demi/lib/index.iife.js"),
|
||||
"utf8"
|
||||
);
|
||||
|
||||
/** @type {import('rollup').Plugin} */
|
||||
export const injectVueDemi = {
|
||||
name: "inject-vue-demi",
|
||||
banner() {
|
||||
return `${VUE_DEMI_IIFE};\n;`;
|
||||
}
|
||||
};
|
||||
@ -1,6 +0,0 @@
|
||||
{
|
||||
"globals": {
|
||||
"Vue": true,
|
||||
"VueECharts": true
|
||||
}
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
#app {
|
||||
height: 400px;
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
<div id="app">
|
||||
<v-chart autoresize :option="option"/>
|
||||
</div>
|
||||
@ -1,55 +0,0 @@
|
||||
Vue.component("v-chart", VueECharts);
|
||||
|
||||
new Vue({
|
||||
el: "#app",
|
||||
data() {
|
||||
return {
|
||||
option: {
|
||||
textStyle: {
|
||||
fontFamily: 'Inter, "Helvetica Neue", Arial, sans-serif'
|
||||
},
|
||||
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)"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
@ -1,54 +0,0 @@
|
||||
Vue.createApp({
|
||||
data() {
|
||||
return {
|
||||
option: {
|
||||
textStyle: {
|
||||
fontFamily: 'Inter, "Helvetica Neue", Arial, sans-serif'
|
||||
},
|
||||
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)"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
}
|
||||
})
|
||||
.component("v-chart", VueECharts)
|
||||
.mount("#app");
|
||||
13
scripts/utils.mjs
Normal file
13
scripts/utils.mjs
Normal file
@ -0,0 +1,13 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { resolve, dirname } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
export function resolvePath(url, ...parts) {
|
||||
return resolve(dirname(fileURLToPath(url)), ...parts);
|
||||
}
|
||||
|
||||
export function getPackageMeta() {
|
||||
return JSON.parse(
|
||||
readFileSync(resolvePath(import.meta.url, "../package.json"), "utf8")
|
||||
);
|
||||
}
|
||||
117
src/ECharts.ts
117
src/ECharts.ts
@ -13,11 +13,21 @@ import {
|
||||
nextTick,
|
||||
watchEffect,
|
||||
getCurrentInstance,
|
||||
Vue2,
|
||||
type PropType,
|
||||
type InjectionKey
|
||||
Vue2
|
||||
} from "vue-demi";
|
||||
import { init as initChart } from "echarts/core";
|
||||
|
||||
import {
|
||||
usePublicAPI,
|
||||
useAutoresize,
|
||||
autoresizeProps,
|
||||
useLoading,
|
||||
loadingProps
|
||||
} from "./composables";
|
||||
import { isOn, omitOn, unwrapInjected } from "./utils";
|
||||
import { register, TAG_NAME } from "./wc";
|
||||
|
||||
import type { PropType, InjectionKey } from "vue-demi";
|
||||
import type {
|
||||
EChartsType,
|
||||
EventTarget,
|
||||
@ -30,18 +40,12 @@ import type {
|
||||
UpdateOptionsInjection,
|
||||
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 type { EChartsElement } from "./wc";
|
||||
|
||||
import "./style.css";
|
||||
|
||||
const wcRegistered = register();
|
||||
const __CSP__ = false;
|
||||
const wcRegistered = __CSP__ ? false : register();
|
||||
|
||||
if (Vue2) {
|
||||
Vue2.config.ignoredElements.push(TAG_NAME);
|
||||
@ -54,6 +58,8 @@ export const UPDATE_OPTIONS_KEY =
|
||||
"ecUpdateOptions" as unknown as InjectionKey<UpdateOptionsInjection>;
|
||||
export { LOADING_OPTIONS_KEY } from "./composables";
|
||||
|
||||
const NATIVE_EVENT_RE = /(^&?~?!?)native:/;
|
||||
|
||||
export default defineComponent({
|
||||
name: "echarts",
|
||||
props: {
|
||||
@ -68,7 +74,7 @@ export default defineComponent({
|
||||
...autoresizeProps,
|
||||
...loadingProps
|
||||
},
|
||||
emits: [] as unknown as Emits,
|
||||
emits: {} as unknown as Emits,
|
||||
inheritAttrs: false,
|
||||
setup(props, { attrs }) {
|
||||
const root = shallowRef<EChartsElement>();
|
||||
@ -93,9 +99,62 @@ export default defineComponent({
|
||||
() => props.updateOptions || unwrapInjected(defaultUpdateOptions, {})
|
||||
);
|
||||
const nonEventAttrs = computed(() => omitOn(attrs));
|
||||
const nativeListeners: Record<string, unknown> = {};
|
||||
|
||||
// @ts-expect-error listeners for Vue 2 compatibility
|
||||
const listeners = getCurrentInstance().proxy.$listeners;
|
||||
const realListeners: Record<string, any> = {};
|
||||
|
||||
if (!listeners) {
|
||||
// This is for Vue 3.
|
||||
// We are converting all `on<Event>` props to event listeners compatible with Vue 2
|
||||
// and collect them into `realListeners` so that we can bind them to the chart instance
|
||||
// later in the same way.
|
||||
// For `onNative:<event>` props, we just strip the `Native:` part and collect them into
|
||||
// `nativeListeners` so that we can bind them to the root element directly.
|
||||
Object.keys(attrs)
|
||||
.filter(key => isOn(key))
|
||||
.forEach(key => {
|
||||
// onClick -> c + lick
|
||||
// onZr:click -> z + r:click
|
||||
let event = key.charAt(2).toLowerCase() + key.slice(3);
|
||||
|
||||
// Collect native DOM events
|
||||
if (event.indexOf("native:") === 0) {
|
||||
// native:click -> onClick
|
||||
const nativeKey = `on${event.charAt(7).toUpperCase()}${event.slice(
|
||||
8
|
||||
)}`;
|
||||
|
||||
nativeListeners[nativeKey] = attrs[key];
|
||||
return;
|
||||
}
|
||||
|
||||
// clickOnce -> ~click
|
||||
// zr:clickOnce -> ~zr:click
|
||||
if (event.substring(event.length - 4) === "Once") {
|
||||
event = `~${event.substring(0, event.length - 4)}`;
|
||||
}
|
||||
|
||||
realListeners[event] = attrs[key];
|
||||
});
|
||||
} else {
|
||||
// This is for Vue 2.
|
||||
// We just need to distinguish normal events and `native:<event>` events and
|
||||
// collect them into `realListeners` and `nativeListeners` respectively.
|
||||
// For `native:<event>` events, we just strip the `native:` part and collect them
|
||||
// into `nativeListeners` so that we can bind them to the root element directly.
|
||||
// native:click -> click
|
||||
// ~native:click -> ~click
|
||||
// &~!native:click -> &~!click
|
||||
Object.keys(listeners).forEach(key => {
|
||||
if (NATIVE_EVENT_RE.test(key)) {
|
||||
nativeListeners[key.replace(NATIVE_EVENT_RE, "$1")] = listeners[key];
|
||||
} else {
|
||||
realListeners[key] = listeners[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function init(option?: Option) {
|
||||
if (!root.value) {
|
||||
@ -112,27 +171,6 @@ export default defineComponent({
|
||||
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];
|
||||
|
||||
@ -233,7 +271,9 @@ export default defineComponent({
|
||||
init();
|
||||
} else {
|
||||
chart.value.setOption(option, {
|
||||
notMerge: option.value !== oldOption?.value,
|
||||
// mutating `option` will lead to `notMerge: false` and
|
||||
// replacing it with new reference will lead to `notMerge: true`
|
||||
notMerge: option !== oldOption,
|
||||
...realUpdateOptions.value
|
||||
});
|
||||
}
|
||||
@ -291,6 +331,7 @@ export default defineComponent({
|
||||
root,
|
||||
setOption,
|
||||
nonEventAttrs,
|
||||
nativeListeners,
|
||||
...publicApi
|
||||
};
|
||||
},
|
||||
@ -298,7 +339,9 @@ export default defineComponent({
|
||||
// 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 }
|
||||
Vue2
|
||||
? { attrs: this.nonEventAttrs, on: this.nativeListeners }
|
||||
: { ...this.nonEventAttrs, ...this.nativeListeners }
|
||||
) as any;
|
||||
attrs.ref = "root";
|
||||
attrs.class = attrs.class ? ["echarts"].concat(attrs.class) : "echarts";
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { Ref } from "vue-demi";
|
||||
import { EChartsType } from "../types";
|
||||
import type { Ref } from "vue-demi";
|
||||
import type { EChartsType } from "../types";
|
||||
|
||||
const METHOD_NAMES = [
|
||||
"getWidth",
|
||||
@ -20,7 +20,7 @@ const METHOD_NAMES = [
|
||||
"dispose"
|
||||
] as const;
|
||||
|
||||
type MethodName = typeof METHOD_NAMES[number];
|
||||
type MethodName = (typeof METHOD_NAMES)[number];
|
||||
|
||||
type PublicMethods = Pick<EChartsType, MethodName>;
|
||||
|
||||
|
||||
@ -1,32 +1,60 @@
|
||||
import { Ref, watch } from "vue-demi";
|
||||
import { watch } from "vue-demi";
|
||||
import { throttle } from "echarts/core";
|
||||
import { addListener, removeListener, ResizeCallback } from "resize-detector";
|
||||
import { EChartsType } from "../types";
|
||||
|
||||
import type { Ref, PropType } from "vue-demi";
|
||||
import type { EChartsType, AutoResize } from "../types";
|
||||
|
||||
export function useAutoresize(
|
||||
chart: Ref<EChartsType | undefined>,
|
||||
autoresize: Ref<boolean>,
|
||||
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) {
|
||||
resizeListener = throttle(() => {
|
||||
chart.resize();
|
||||
}, 100);
|
||||
if (root && chart && autoresize) {
|
||||
const { offsetWidth, offsetHeight } = root;
|
||||
const autoresizeOptions = autoresize === true ? {} : autoresize;
|
||||
const { throttle: wait = 100, onResize } = autoresizeOptions;
|
||||
|
||||
addListener(root, resizeListener);
|
||||
}
|
||||
let initialResizeTriggered = false;
|
||||
|
||||
cleanup(() => {
|
||||
if (resizeListener && root) {
|
||||
removeListener(root, resizeListener);
|
||||
const callback = () => {
|
||||
chart.resize();
|
||||
onResize?.();
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
resizeCallback();
|
||||
});
|
||||
ro.observe(root);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
onCleanup(() => {
|
||||
if (ro) {
|
||||
ro.disconnect();
|
||||
ro = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export const autoresizeProps = {
|
||||
autoresize: Boolean
|
||||
autoresize: [Boolean, Object] as PropType<AutoResize>
|
||||
};
|
||||
|
||||
@ -1,24 +1,18 @@
|
||||
import { unwrapInjected } from "../utils";
|
||||
import {
|
||||
inject,
|
||||
computed,
|
||||
watchEffect,
|
||||
type Ref,
|
||||
type InjectionKey
|
||||
} from "vue-demi";
|
||||
import { EChartsType } from "../types";
|
||||
import { inject, computed, watchEffect } from "vue-demi";
|
||||
|
||||
import type { Ref, InjectionKey, PropType } from "vue-demi";
|
||||
import type { EChartsType, LoadingOptions } from "../types";
|
||||
|
||||
export const LOADING_OPTIONS_KEY =
|
||||
"ecLoadingOptions" as unknown as InjectionKey<
|
||||
UnknownRecord | Ref<UnknownRecord>
|
||||
LoadingOptions | Ref<LoadingOptions>
|
||||
>;
|
||||
|
||||
type UnknownRecord = Record<string, unknown>;
|
||||
|
||||
export function useLoading(
|
||||
chart: Ref<EChartsType | undefined>,
|
||||
loading: Ref<boolean>,
|
||||
loadingOptions: Ref<UnknownRecord | undefined>
|
||||
loadingOptions: Ref<LoadingOptions | undefined>
|
||||
): void {
|
||||
const defaultLoadingOptions = inject(LOADING_OPTIONS_KEY, {});
|
||||
const realLoadingOptions = computed(() => ({
|
||||
@ -42,5 +36,5 @@ export function useLoading(
|
||||
|
||||
export const loadingProps = {
|
||||
loading: Boolean,
|
||||
loadingOptions: Object
|
||||
loadingOptions: Object as PropType<LoadingOptions>
|
||||
};
|
||||
|
||||
425
src/demo/CodeGen.vue
Normal file
425
src/demo/CodeGen.vue
Normal file
@ -0,0 +1,425 @@
|
||||
<script setup>
|
||||
import {
|
||||
ref,
|
||||
computed,
|
||||
watch,
|
||||
onBeforeUnmount,
|
||||
defineProps,
|
||||
defineEmits,
|
||||
onMounted,
|
||||
nextTick
|
||||
} from "vue";
|
||||
import { useLocalStorage } from "@vueuse/core";
|
||||
import "highlight.js/styles/github.css";
|
||||
import hljs from "highlight.js/lib/core";
|
||||
import javascript from "highlight.js/lib/languages/javascript";
|
||||
import typescript from "highlight.js/lib/languages/typescript";
|
||||
import hljsVuePlugin from "@highlightjs/vue-plugin";
|
||||
import { initialize, transform } from "esbuild-wasm";
|
||||
import wasmURL from "esbuild-wasm/esbuild.wasm";
|
||||
import { track } from "@vercel/analytics";
|
||||
|
||||
import { getImportsFromOption } from "./utils/codegen";
|
||||
|
||||
hljs.registerLanguage("javascript", javascript);
|
||||
hljs.registerLanguage("typescript", typescript);
|
||||
const CodeHighlight = hljsVuePlugin.component;
|
||||
|
||||
const codegenOptions = useLocalStorage("ve.codegenOptions", {
|
||||
indent: " ",
|
||||
quote: "'",
|
||||
multiline: false,
|
||||
maxLen: 80,
|
||||
semi: false,
|
||||
includeType: false
|
||||
});
|
||||
|
||||
const props = defineProps({ open: Boolean, renderer: String });
|
||||
const emit = defineEmits(["update:open"]);
|
||||
|
||||
const dialog = ref(null);
|
||||
let clickFrom = null;
|
||||
|
||||
function closeFromOutside() {
|
||||
if (dialog.value?.contains(clickFrom)) {
|
||||
return;
|
||||
}
|
||||
close();
|
||||
}
|
||||
|
||||
function close() {
|
||||
emit("update:open", false);
|
||||
}
|
||||
|
||||
const renderer = ref(props.renderer);
|
||||
const source = ref(null);
|
||||
watch(
|
||||
() => props.open,
|
||||
async val => {
|
||||
if (val) {
|
||||
renderer.value = props.renderer;
|
||||
}
|
||||
|
||||
await nextTick();
|
||||
|
||||
if (initializing.value) {
|
||||
return;
|
||||
}
|
||||
source.value?.focus();
|
||||
}
|
||||
);
|
||||
|
||||
const copied = ref(false);
|
||||
const initializing = ref(true);
|
||||
const optionCode = ref("");
|
||||
const transformedCode = ref("");
|
||||
const transformErrors = ref([]);
|
||||
|
||||
onMounted(async () => {
|
||||
await initialize({ wasmURL });
|
||||
|
||||
initializing.value = false;
|
||||
|
||||
optionCode.value = `{
|
||||
title: {
|
||||
text: 'Referer of a Website',
|
||||
subtext: 'Fake Data',
|
||||
left: 'center'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item'
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 'left'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: 'Access From',
|
||||
type: 'pie',
|
||||
radius: '50%',
|
||||
data: [
|
||||
{ value: 1048, name: 'Search Engine' },
|
||||
{ value: 735, name: 'Direct' },
|
||||
{ value: 580, name: 'Email' },
|
||||
{ value: 484, name: 'Union Ads' },
|
||||
{ value: 300, name: 'Video Ads' }
|
||||
],
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}`;
|
||||
|
||||
await nextTick();
|
||||
|
||||
source.value?.focus();
|
||||
});
|
||||
|
||||
watch(optionCode, async val => {
|
||||
try {
|
||||
transformedCode.value = await transform(`(${val})`, { loader: "ts" });
|
||||
transformErrors.value = [];
|
||||
} catch (e) {
|
||||
transformErrors.value = e.errors;
|
||||
}
|
||||
});
|
||||
|
||||
function formatError(errors) {
|
||||
return errors
|
||||
.map(({ text, location: { lineText, line, column, length } }) => {
|
||||
const digit = Math.ceil(Math.log10(line)) || 1;
|
||||
lineText = line === 1 ? lineText.slice(1) : lineText;
|
||||
lineText =
|
||||
line === optionCode.value.split("\n").length
|
||||
? lineText.slice(0, -1)
|
||||
: lineText;
|
||||
column = line === 1 ? column - 1 : column;
|
||||
|
||||
return `/* ${text} */
|
||||
|
||||
// ${line} | ${lineText}
|
||||
// ${" ".repeat(digit)} | ${" ".repeat(column)}${"~".repeat(length)}
|
||||
`;
|
||||
})
|
||||
.join("\n\n");
|
||||
}
|
||||
|
||||
const importCode = computed(() => {
|
||||
if (optionCode.value.trim() === "") {
|
||||
return "// Paste your option code first";
|
||||
}
|
||||
|
||||
if (transformErrors.value.length) {
|
||||
return formatError(transformErrors.value);
|
||||
}
|
||||
|
||||
try {
|
||||
return getImportsFromOption(eval(transformedCode.value.code), {
|
||||
renderer: renderer.value,
|
||||
...codegenOptions.value
|
||||
});
|
||||
} catch (e) {
|
||||
return `/* Invalid ECharts option */
|
||||
|
||||
// ${e.message}
|
||||
`;
|
||||
}
|
||||
});
|
||||
|
||||
watch(importCode, () => {
|
||||
copied.value = false;
|
||||
});
|
||||
|
||||
// copy message
|
||||
const messageOpen = ref(false);
|
||||
let messageTimer;
|
||||
|
||||
function trackCopy(from) {
|
||||
if (copied.value) {
|
||||
// only track copy after modifications
|
||||
return;
|
||||
}
|
||||
|
||||
copied.value = true;
|
||||
track("copy-code", { from });
|
||||
}
|
||||
|
||||
function copy() {
|
||||
trackCopy("button");
|
||||
clearTimeout(messageTimer);
|
||||
|
||||
navigator.clipboard.writeText(importCode.value);
|
||||
messageOpen.value = true;
|
||||
|
||||
messageTimer = setTimeout(() => {
|
||||
messageOpen.value = false;
|
||||
}, 2018);
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
clearTimeout(messageTimer);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<aside
|
||||
class="modal"
|
||||
:class="{ open: props.open }"
|
||||
@mousedown="clickFrom = $event.target"
|
||||
@click="closeFromOutside"
|
||||
@keydown.esc="close"
|
||||
>
|
||||
<section class="dialog" ref="dialog">
|
||||
<h2>✨ <code>import</code> code generator</h2>
|
||||
<section class="options">
|
||||
<label>
|
||||
Renderer
|
||||
<select v-model="renderer">
|
||||
<option value="canvas">Canvas</option>
|
||||
<option value="svg">SVG</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
TypeScript
|
||||
<input type="checkbox" v-model="codegenOptions.includeType" />
|
||||
</label>
|
||||
<label>
|
||||
Multiline
|
||||
<input type="checkbox" v-model="codegenOptions.multiline" />
|
||||
</label>
|
||||
<label>
|
||||
Semi
|
||||
<input type="checkbox" v-model="codegenOptions.semi" />
|
||||
</label>
|
||||
<label>
|
||||
Quote
|
||||
<select v-model="codegenOptions.quote">
|
||||
<option value="'">single</option>
|
||||
<option value='"'>double</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Indent
|
||||
<select v-model="codegenOptions.indent">
|
||||
<option value=" ">2</option>
|
||||
<option value=" ">4</option>
|
||||
<option value=" ">Tab</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Max length
|
||||
<input
|
||||
type="number"
|
||||
step="10"
|
||||
v-model.number="codegenOptions.maxLen"
|
||||
/>
|
||||
</label>
|
||||
</section>
|
||||
<section class="code">
|
||||
<textarea
|
||||
ref="source"
|
||||
class="option-code"
|
||||
v-model="optionCode"
|
||||
:placeholder="
|
||||
initializing
|
||||
? 'Initializing...'
|
||||
: 'Paste your option code (TS/JS literal) here...'
|
||||
"
|
||||
:disabled="initializing"
|
||||
autofocus
|
||||
spellcheck="false"
|
||||
></textarea>
|
||||
<div class="import-code" @copy="trackCopy('system')">
|
||||
<code-highlight
|
||||
:language="codegenOptions.includeType ? 'typescript' : 'javascript'"
|
||||
:code="importCode"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
class="copy"
|
||||
@click="copy"
|
||||
:disabled="importCode.startsWith('/*') || importCode.startsWith('//')"
|
||||
>
|
||||
Copy
|
||||
</button>
|
||||
</section>
|
||||
</section>
|
||||
</aside>
|
||||
|
||||
<aside class="message" :class="{ open: messageOpen }">
|
||||
Copied to clipboard
|
||||
</aside>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
input::-webkit-outer-spin-button,
|
||||
input::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Works for Firefox */
|
||||
input[type="number"] {
|
||||
appearance: textfield;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
input[type="number"] {
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.dialog {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 80vw;
|
||||
height: 90vh;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 0 45px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.options {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: stretch;
|
||||
gap: 16px;
|
||||
padding: 16px;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
||||
|
||||
label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
input,
|
||||
button,
|
||||
select {
|
||||
height: 2.4em;
|
||||
}
|
||||
|
||||
input[type="number"] {
|
||||
width: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
.code {
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: stretch;
|
||||
flex-grow: 1;
|
||||
min-height: 0;
|
||||
tab-size: 4;
|
||||
|
||||
.option-code,
|
||||
.import-code {
|
||||
flex: 0 0 50%;
|
||||
margin: 0;
|
||||
border: none;
|
||||
line-height: 1.2;
|
||||
font-size: 13px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.import-code {
|
||||
border-left: 1px solid rgba(0, 0, 0, 0.1);
|
||||
|
||||
pre {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: #fff;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
code {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.option-code {
|
||||
padding: 1em;
|
||||
outline: none;
|
||||
resize: none;
|
||||
}
|
||||
}
|
||||
|
||||
.copy {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 10px;
|
||||
}
|
||||
|
||||
.message {
|
||||
position: fixed;
|
||||
z-index: 2147483647;
|
||||
bottom: 2rem;
|
||||
left: 50%;
|
||||
padding: 0.5rem 0.75rem;
|
||||
background-color: rgba(45, 52, 64, 0.98);
|
||||
box-shadow: 0 4px 16px rgba(45, 52, 64, 0.6);
|
||||
color: #fff;
|
||||
font-size: 0.875rem;
|
||||
transform: translate(-50%, 200%);
|
||||
border-radius: 4px;
|
||||
opacity: 0;
|
||||
transition:
|
||||
transform 0.2s,
|
||||
opacity 0.2s;
|
||||
}
|
||||
|
||||
.message.open {
|
||||
transform: translate(-50%, 0);
|
||||
opacity: 1;
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because it is too large
Load Diff
BIN
src/demo/assets/starfield.jpg
Normal file
BIN
src/demo/assets/starfield.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 901 KiB |
BIN
src/demo/assets/world.jpg
Normal file
BIN
src/demo/assets/world.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
@ -5,7 +5,8 @@ function random() {
|
||||
export default function getData() {
|
||||
return {
|
||||
textStyle: {
|
||||
fontFamily: 'Inter, "Helvetica Neue", Arial, sans-serif'
|
||||
fontFamily: 'Inter, "Helvetica Neue", Arial, sans-serif',
|
||||
fontWeight: 300
|
||||
},
|
||||
dataset: {
|
||||
dimensions: ["Product", "2015", "2016", "2017"],
|
||||
|
||||
@ -9,17 +9,22 @@ for (let i = 0; i < 16; i++) {
|
||||
]);
|
||||
}
|
||||
|
||||
export const c1 = {
|
||||
const c1 = {
|
||||
textStyle: {
|
||||
fontFamily: 'Inter, "Helvetica Neue", Arial, sans-serif',
|
||||
fontWeight: 300
|
||||
},
|
||||
legend: {
|
||||
top: 20,
|
||||
top: "3%",
|
||||
data: ["scatter"]
|
||||
},
|
||||
tooltip: {
|
||||
formatter: "{c}"
|
||||
},
|
||||
grid: {
|
||||
top: "26%",
|
||||
bottom: "26%"
|
||||
top: "30%",
|
||||
right: "18%",
|
||||
bottom: "20%"
|
||||
},
|
||||
xAxis: {
|
||||
type: "value",
|
||||
@ -36,7 +41,8 @@ export const c1 = {
|
||||
visualMap: [
|
||||
{
|
||||
realtime: false,
|
||||
left: "right",
|
||||
right: "2%",
|
||||
bottom: "3%",
|
||||
selectedMode: "multiple",
|
||||
dimension: 2,
|
||||
selected: [],
|
||||
@ -57,17 +63,22 @@ export const c1 = {
|
||||
]
|
||||
};
|
||||
|
||||
export const c2 = {
|
||||
const c2 = {
|
||||
textStyle: {
|
||||
fontFamily: 'Inter, "Helvetica Neue", Arial, sans-serif',
|
||||
fontWeight: 300
|
||||
},
|
||||
legend: {
|
||||
top: 20,
|
||||
top: "3%",
|
||||
data: ["scatter"]
|
||||
},
|
||||
tooltip: {
|
||||
formatter: "{c}"
|
||||
},
|
||||
grid: {
|
||||
top: "26%",
|
||||
bottom: "26%"
|
||||
top: "30%",
|
||||
right: "18%",
|
||||
bottom: "20%"
|
||||
},
|
||||
xAxis: {
|
||||
type: "value",
|
||||
@ -83,7 +94,8 @@ export const c2 = {
|
||||
},
|
||||
visualMap: [
|
||||
{
|
||||
left: "right",
|
||||
right: "2%",
|
||||
bottom: "3%",
|
||||
selectedMode: "multiple",
|
||||
dimension: 2,
|
||||
selected: [],
|
||||
@ -103,3 +115,7 @@ export const c2 = {
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export default function getData() {
|
||||
return [c1, c2];
|
||||
}
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
/* eslint-disable */
|
||||
import logo from '../assets/Vue-ECharts.svg'
|
||||
/* eslint-enable */
|
||||
import logo from "../assets/Vue-ECharts.svg";
|
||||
|
||||
const d = logo.match(/\bd="([^"]+)"/)[1];
|
||||
|
||||
@ -27,6 +25,10 @@ export default {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
},
|
||||
itemStyle: {
|
||||
shadowBlur: 12,
|
||||
shadowColor: "rgba(0, 0, 0, 0.25)"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@ -397,92 +397,98 @@ function convertData(data) {
|
||||
return res;
|
||||
}
|
||||
|
||||
export default {
|
||||
textStyle: {
|
||||
fontFamily: 'Inter, "Helvetica Neue", Arial, sans-serif'
|
||||
},
|
||||
backgroundColor: "#404a59",
|
||||
title: {
|
||||
text: "Air quality of major cities in China",
|
||||
subtext: "data from PM25.in",
|
||||
sublink: "http://www.pm25.in",
|
||||
left: "center",
|
||||
export default function getData() {
|
||||
return {
|
||||
textStyle: {
|
||||
color: "#fff"
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
trigger: "item"
|
||||
},
|
||||
legend: {
|
||||
orient: "vertical",
|
||||
y: "bottom",
|
||||
x: "right",
|
||||
data: ["pm2.5"],
|
||||
textStyle: {
|
||||
color: "#fff"
|
||||
}
|
||||
},
|
||||
geo: {
|
||||
map: "china",
|
||||
emphasis: {
|
||||
label: {
|
||||
show: false
|
||||
},
|
||||
itemStyle: {
|
||||
areaColor: "#2a333d"
|
||||
fontFamily: 'Inter, "Helvetica Neue", Arial, sans-serif',
|
||||
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"
|
||||
}
|
||||
},
|
||||
itemStyle: {
|
||||
areaColor: "#323c48",
|
||||
borderColor: "#111"
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: "pm2.5",
|
||||
type: "scatter",
|
||||
coordinateSystem: "geo",
|
||||
data: convertData(data),
|
||||
symbolSize: val => val[2] / 10,
|
||||
tooltip: {
|
||||
formatter: function (val) {
|
||||
return val.name + ": " + val.value[2];
|
||||
}
|
||||
},
|
||||
itemStyle: {
|
||||
color: "#ddb926"
|
||||
tooltip: {
|
||||
trigger: "item"
|
||||
},
|
||||
legend: {
|
||||
orient: "vertical",
|
||||
right: "5%",
|
||||
bottom: "5%",
|
||||
data: ["PM2.5"],
|
||||
textStyle: {
|
||||
color: "#fff"
|
||||
}
|
||||
},
|
||||
{
|
||||
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,
|
||||
showEffectOn: "render",
|
||||
rippleEffect: {
|
||||
brushType: "stroke"
|
||||
},
|
||||
geo: {
|
||||
map: "china",
|
||||
emphasis: {
|
||||
scale: true
|
||||
},
|
||||
tooltip: {
|
||||
formatter: function (val) {
|
||||
return val.name + ": " + val.value[2];
|
||||
label: {
|
||||
show: false
|
||||
},
|
||||
itemStyle: {
|
||||
areaColor: "#2a333d"
|
||||
}
|
||||
},
|
||||
label: {
|
||||
formatter: "{b}",
|
||||
position: "right",
|
||||
show: true
|
||||
},
|
||||
itemStyle: {
|
||||
color: "#f4e925",
|
||||
shadowBlur: 10,
|
||||
shadowColor: "#333"
|
||||
areaColor: "#323c48",
|
||||
borderColor: "#111"
|
||||
},
|
||||
zlevel: 1
|
||||
}
|
||||
]
|
||||
};
|
||||
top: "20%",
|
||||
bottom: "7%"
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: "PM2.5",
|
||||
type: "scatter",
|
||||
coordinateSystem: "geo",
|
||||
data: convertData(data),
|
||||
symbolSize: val => val[2] / 10,
|
||||
tooltip: {
|
||||
formatter: function (val) {
|
||||
return val.name + ": " + val.value[2];
|
||||
}
|
||||
},
|
||||
itemStyle: {
|
||||
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,
|
||||
showEffectOn: "render",
|
||||
rippleEffect: {
|
||||
brushType: "stroke"
|
||||
},
|
||||
emphasis: {
|
||||
scale: true
|
||||
},
|
||||
tooltip: {
|
||||
formatter: function (val) {
|
||||
return val.name + ": " + val.value[2];
|
||||
}
|
||||
},
|
||||
label: {
|
||||
formatter: "{b}",
|
||||
position: "right",
|
||||
show: true
|
||||
},
|
||||
itemStyle: {
|
||||
color: "#f4e925",
|
||||
shadowBlur: 10,
|
||||
shadowColor: "#333"
|
||||
},
|
||||
zlevel: 1
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,40 +1,45 @@
|
||||
export default {
|
||||
textStyle: {
|
||||
fontFamily: 'Inter, "Helvetica Neue", Arial, sans-serif'
|
||||
},
|
||||
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)"
|
||||
export default function getData() {
|
||||
return {
|
||||
textStyle: {
|
||||
fontFamily: 'Inter, "Helvetica Neue", Arial, sans-serif',
|
||||
fontWeight: 300
|
||||
},
|
||||
title: {
|
||||
text: "Traffic Sources",
|
||||
top: "5%",
|
||||
left: "center"
|
||||
},
|
||||
tooltip: {
|
||||
trigger: "item",
|
||||
formatter: "{a} <br/>{b} : {c} ({d}%)"
|
||||
},
|
||||
legend: {
|
||||
orient: "vertical",
|
||||
top: "5%",
|
||||
left: "5%",
|
||||
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)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
@ -6,40 +6,47 @@ for (let i = 0; i <= 360; i++) {
|
||||
data.push([r, i]);
|
||||
}
|
||||
|
||||
export default {
|
||||
textStyle: {
|
||||
fontFamily: 'Inter, "Helvetica Neue", Arial, sans-serif'
|
||||
},
|
||||
title: {
|
||||
text: "Dual Numeric Axis"
|
||||
},
|
||||
legend: {
|
||||
data: ["line"]
|
||||
},
|
||||
polar: {
|
||||
center: ["50%", "54%"]
|
||||
},
|
||||
tooltip: {
|
||||
trigger: "axis",
|
||||
axisPointer: {
|
||||
type: "cross"
|
||||
}
|
||||
},
|
||||
angleAxis: {
|
||||
type: "value",
|
||||
startAngle: 0
|
||||
},
|
||||
radiusAxis: {
|
||||
min: 0
|
||||
},
|
||||
series: [
|
||||
{
|
||||
coordinateSystem: "polar",
|
||||
name: "line",
|
||||
type: "line",
|
||||
showSymbol: false,
|
||||
data: data
|
||||
}
|
||||
],
|
||||
animationDuration: 2000
|
||||
};
|
||||
export default function getData() {
|
||||
return {
|
||||
textStyle: {
|
||||
fontFamily: 'Inter, "Helvetica Neue", Arial, sans-serif',
|
||||
fontWeight: 300
|
||||
},
|
||||
title: {
|
||||
text: "Dual Numeric Axis",
|
||||
top: "5%",
|
||||
left: "5%"
|
||||
},
|
||||
legend: {
|
||||
data: ["line"],
|
||||
top: "6%"
|
||||
},
|
||||
polar: {
|
||||
radius: "65%",
|
||||
center: ["50%", "56%"]
|
||||
},
|
||||
tooltip: {
|
||||
trigger: "axis",
|
||||
axisPointer: {
|
||||
type: "cross"
|
||||
}
|
||||
},
|
||||
angleAxis: {
|
||||
type: "value",
|
||||
startAngle: 0
|
||||
},
|
||||
radiusAxis: {
|
||||
min: 0
|
||||
},
|
||||
series: [
|
||||
{
|
||||
coordinateSystem: "polar",
|
||||
name: "line",
|
||||
type: "line",
|
||||
showSymbol: false,
|
||||
data: data
|
||||
}
|
||||
],
|
||||
animationDuration: 2000
|
||||
};
|
||||
}
|
||||
|
||||
1
src/demo/data/population.json
Normal file
1
src/demo/data/population.json
Normal file
File diff suppressed because one or more lines are too long
69
src/demo/data/radar.ts
Normal file
69
src/demo/data/radar.ts
Normal file
@ -0,0 +1,69 @@
|
||||
import { ref, computed } from "vue";
|
||||
import { defineStore } from "pinia";
|
||||
|
||||
export const useScoreStore = defineStore("store", () => {
|
||||
const scores = ref([
|
||||
{ name: "Attack", max: 20, value: 19 },
|
||||
{ name: "Defense", max: 20, value: 9 },
|
||||
{ name: "Speed", max: 20, value: 18 },
|
||||
{ name: "Strength", max: 20, value: 16 },
|
||||
{ name: "Endurance", max: 20, value: 16 },
|
||||
{ name: "Agility", max: 20, value: 20 }
|
||||
]);
|
||||
|
||||
const metrics = computed(() => {
|
||||
return scores.value.map(({ name }) => name);
|
||||
});
|
||||
|
||||
function getRadarData(activeIndex: number) {
|
||||
return {
|
||||
title: {
|
||||
text: "Player Ability",
|
||||
top: "5%",
|
||||
left: "5%"
|
||||
},
|
||||
textStyle: {
|
||||
fontFamily: 'Inter, "Helvetica Neue", Arial, sans-serif',
|
||||
fontWeight: 300
|
||||
},
|
||||
radar: {
|
||||
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) }]
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
function increase(index: number, amount: number) {
|
||||
const metric = scores.value[index];
|
||||
metric.value = Math.max(Math.min(metric.value + amount, metric.max), 0);
|
||||
}
|
||||
|
||||
function isMax(index: number) {
|
||||
const { value, max } = scores.value[index];
|
||||
return value === max;
|
||||
}
|
||||
|
||||
function isMin(index: number) {
|
||||
return scores.value[index].value === 0;
|
||||
}
|
||||
|
||||
return {
|
||||
scores,
|
||||
metrics,
|
||||
getRadarData,
|
||||
increase,
|
||||
isMax,
|
||||
isMin
|
||||
};
|
||||
});
|
||||
@ -45,96 +45,105 @@ const data = [
|
||||
]
|
||||
];
|
||||
|
||||
export default {
|
||||
textStyle: {
|
||||
fontFamily: 'Inter, "Helvetica Neue", Arial, sans-serif'
|
||||
},
|
||||
title: {
|
||||
text: "Life Expectancy vs. GDP by country"
|
||||
},
|
||||
legend: {
|
||||
right: 10,
|
||||
data: ["1990", "2015"]
|
||||
},
|
||||
xAxis: {
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
type: "dashed"
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
type: "dashed"
|
||||
export default function getData() {
|
||||
return {
|
||||
grid: {
|
||||
top: "25%"
|
||||
},
|
||||
textStyle: {
|
||||
fontFamily: 'Inter, "Helvetica Neue", Arial, sans-serif',
|
||||
fontWeight: 300
|
||||
},
|
||||
title: {
|
||||
text: "Life Expectancy vs. GDP by country",
|
||||
top: "5%",
|
||||
left: "5%"
|
||||
},
|
||||
legend: {
|
||||
top: "6%",
|
||||
right: "5%",
|
||||
data: ["1990", "2015"]
|
||||
},
|
||||
xAxis: {
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
type: "dashed"
|
||||
}
|
||||
}
|
||||
},
|
||||
scale: true
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: "1990",
|
||||
data: data[0],
|
||||
type: "scatter",
|
||||
symbolSize(data) {
|
||||
return Math.sqrt(data[2]) / 5e2;
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
formatter({ data }) {
|
||||
return data[3];
|
||||
},
|
||||
position: "top"
|
||||
yAxis: {
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
type: "dashed"
|
||||
}
|
||||
},
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowColor: "rgba(120, 36, 50, 0.5)",
|
||||
shadowOffsetY: 5,
|
||||
color: new graphic.RadialGradient(0.4, 0.3, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: "rgb(251, 118, 123)"
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: "rgb(204, 46, 72)"
|
||||
}
|
||||
])
|
||||
}
|
||||
scale: true
|
||||
},
|
||||
{
|
||||
name: "2015",
|
||||
data: data[1],
|
||||
type: "scatter",
|
||||
symbolSize(data) {
|
||||
return Math.sqrt(data[2]) / 5e2;
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
formatter({ data }) {
|
||||
return data[3];
|
||||
},
|
||||
position: "top"
|
||||
series: [
|
||||
{
|
||||
name: "1990",
|
||||
data: data[0],
|
||||
type: "scatter",
|
||||
symbolSize(data) {
|
||||
return Math.sqrt(data[2]) / 5e2;
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
formatter({ data }) {
|
||||
return data[3];
|
||||
},
|
||||
position: "top"
|
||||
}
|
||||
},
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowColor: "rgba(120, 36, 50, 0.5)",
|
||||
shadowOffsetY: 5,
|
||||
color: new graphic.RadialGradient(0.4, 0.3, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: "rgb(251, 118, 123)"
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: "rgb(204, 46, 72)"
|
||||
}
|
||||
])
|
||||
}
|
||||
},
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowColor: "rgba(25, 100, 150, 0.5)",
|
||||
shadowOffsetY: 5,
|
||||
color: new graphic.RadialGradient(0.4, 0.3, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: "rgb(129, 227, 238)"
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: "rgb(25, 183, 207)"
|
||||
{
|
||||
name: "2015",
|
||||
data: data[1],
|
||||
type: "scatter",
|
||||
symbolSize(data) {
|
||||
return Math.sqrt(data[2]) / 5e2;
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
formatter({ data }) {
|
||||
return data[3];
|
||||
},
|
||||
position: "top"
|
||||
}
|
||||
])
|
||||
},
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowColor: "rgba(25, 100, 150, 0.5)",
|
||||
shadowOffsetY: 5,
|
||||
color: new graphic.RadialGradient(0.4, 0.3, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: "rgb(129, 227, 238)"
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: "rgb(25, 183, 207)"
|
||||
}
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
77
src/demo/examples/BarChart.vue
Normal file
77
src/demo/examples/BarChart.vue
Normal file
@ -0,0 +1,77 @@
|
||||
<script setup>
|
||||
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 getData from "../data/bar";
|
||||
import theme from "../theme.json";
|
||||
|
||||
use([BarChart, DatasetComponent, GridComponent]);
|
||||
registerTheme("ovilia-green", theme);
|
||||
|
||||
const seconds = shallowRef(0);
|
||||
const loading = shallowRef(false);
|
||||
const loadingOptions = {
|
||||
text: "Loading…",
|
||||
color: "#4ea397",
|
||||
maskColor: "rgba(255, 255, 255, 0.4)"
|
||||
};
|
||||
const option = shallowRef(getData());
|
||||
|
||||
let timer = null;
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
clearInterval(timer);
|
||||
});
|
||||
|
||||
function tick() {
|
||||
seconds.value--;
|
||||
|
||||
if (seconds.value === 0) {
|
||||
clearInterval(timer);
|
||||
loading.value = false;
|
||||
option.value = getData();
|
||||
}
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
// simulating async data from server
|
||||
seconds.value = 3;
|
||||
loading.value = true;
|
||||
|
||||
timer = setInterval(tick, 1000);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-example
|
||||
id="bar"
|
||||
title="Bar chart"
|
||||
desc="(with async data & custom theme)"
|
||||
>
|
||||
<v-chart
|
||||
:option="option"
|
||||
theme="ovilia-green"
|
||||
autoresize
|
||||
:loading="loading"
|
||||
:loadingOptions="loadingOptions"
|
||||
/>
|
||||
<template #extra>
|
||||
<p v-if="seconds <= 0">
|
||||
<small>Loaded.</small>
|
||||
</p>
|
||||
<p v-else>
|
||||
<small>
|
||||
Data coming in
|
||||
<b>{{ seconds }}</b>
|
||||
second{{ seconds > 1 ? "s" : "" }}...
|
||||
</small>
|
||||
</p>
|
||||
<p class="actions">
|
||||
<button @click="refresh" :disabled="seconds > 0">Refresh</button>
|
||||
</p>
|
||||
</template>
|
||||
</v-example>
|
||||
</template>
|
||||
54
src/demo/examples/ConnectChart.vue
Normal file
54
src/demo/examples/ConnectChart.vue
Normal file
@ -0,0 +1,54 @@
|
||||
<script setup>
|
||||
import { use, connect, disconnect } from "echarts/core";
|
||||
import { ScatterChart } from "echarts/charts";
|
||||
import {
|
||||
GridComponent,
|
||||
TitleComponent,
|
||||
VisualMapComponent,
|
||||
TooltipComponent
|
||||
} from "echarts/components";
|
||||
import { shallowRef, watch } from "vue";
|
||||
import VChart from "../../ECharts";
|
||||
import VExample from "./Example";
|
||||
import getData from "../data/connect";
|
||||
|
||||
use([
|
||||
ScatterChart,
|
||||
GridComponent,
|
||||
TitleComponent,
|
||||
VisualMapComponent,
|
||||
TooltipComponent
|
||||
]);
|
||||
|
||||
const [c1, c2] = getData().map(shallowRef);
|
||||
const connected = shallowRef(true);
|
||||
|
||||
watch(
|
||||
connected,
|
||||
value => {
|
||||
if (value) {
|
||||
connect("radiance");
|
||||
} else {
|
||||
disconnect("radiance");
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-example id="connect" title="Connectable charts" split>
|
||||
<template #start>
|
||||
<v-chart :option="c1" group="radiance" autoresize />
|
||||
</template>
|
||||
<template #end>
|
||||
<v-chart :option="c2" group="radiance" autoresize />
|
||||
</template>
|
||||
<template #extra>
|
||||
<p class="actions">
|
||||
<input id="connected-check" type="checkbox" v-model="connected" />
|
||||
<label for="connected-check">Connected</label>
|
||||
</p>
|
||||
</template>
|
||||
</v-example>
|
||||
</template>
|
||||
97
src/demo/examples/Example.vue
Normal file
97
src/demo/examples/Example.vue
Normal file
@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<h3 :id="id">
|
||||
<a :href="`#${id}`">
|
||||
{{ title }}
|
||||
<small v-if="desc">{{ desc }}</small>
|
||||
</a>
|
||||
</h3>
|
||||
<section>
|
||||
<figure class="fig hero" v-if="!split">
|
||||
<slot />
|
||||
</figure>
|
||||
<div class="split" v-else>
|
||||
<figure class="fig half">
|
||||
<slot name="start" />
|
||||
</figure>
|
||||
<figure class="fig half">
|
||||
<slot name="end" />
|
||||
</figure>
|
||||
</div>
|
||||
<slot name="extra" />
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps } from "vue";
|
||||
|
||||
defineProps({
|
||||
id: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
desc: String,
|
||||
split: Boolean
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.fig {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
width: fit-content;
|
||||
margin: 2em auto;
|
||||
|
||||
.echarts {
|
||||
width: calc(60vw + 4em);
|
||||
height: 360px;
|
||||
max-width: 720px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 0 45px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
.split {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
.fig {
|
||||
margin-right: 0;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 980px) {
|
||||
.fig {
|
||||
width: 100vw;
|
||||
margin: 1em auto;
|
||||
|
||||
.echarts {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
height: 60vw;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 980px) {
|
||||
.fig.half {
|
||||
.echarts {
|
||||
width: 28vw;
|
||||
min-width: 240px;
|
||||
height: 180px;
|
||||
}
|
||||
|
||||
& + & {
|
||||
margin-left: 30px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
62
src/demo/examples/GeoChart.vue
Normal file
62
src/demo/examples/GeoChart.vue
Normal file
@ -0,0 +1,62 @@
|
||||
<script setup>
|
||||
import { use, registerMap } from "echarts/core";
|
||||
import { ScatterChart, EffectScatterChart } from "echarts/charts";
|
||||
import {
|
||||
GeoComponent,
|
||||
TitleComponent,
|
||||
LegendComponent,
|
||||
TooltipComponent
|
||||
} from "echarts/components";
|
||||
import { shallowRef } from "vue";
|
||||
import VChart from "../../ECharts";
|
||||
import VExample from "./Example";
|
||||
import getData from "../data/map";
|
||||
import chinaMap from "../china.json";
|
||||
|
||||
use([
|
||||
ScatterChart,
|
||||
EffectScatterChart,
|
||||
GeoComponent,
|
||||
TitleComponent,
|
||||
LegendComponent,
|
||||
TooltipComponent
|
||||
]);
|
||||
|
||||
registerMap("china", chinaMap);
|
||||
|
||||
const option = shallowRef(getData());
|
||||
const map = shallowRef(null);
|
||||
const open = shallowRef(false);
|
||||
|
||||
let img = null;
|
||||
|
||||
function convert() {
|
||||
img = {
|
||||
src: map.value.getDataURL({
|
||||
pixelRatio: window.devicePixelRatio || 1
|
||||
}),
|
||||
width: map.value.getWidth(),
|
||||
height: map.value.getHeight()
|
||||
};
|
||||
open.value = true;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-example id="map" title="Map" desc="(with GeoJSON & image converter)">
|
||||
<v-chart
|
||||
ref="map"
|
||||
:option="option"
|
||||
autoresize
|
||||
style="background-color: #404a59"
|
||||
/>
|
||||
<template #extra>
|
||||
<p class="actions">
|
||||
<button @click="convert">Convert to image</button>
|
||||
</p>
|
||||
<aside class="modal" :class="{ open }" @click="open = false">
|
||||
<img v-if="img" v-bind="img" />
|
||||
</aside>
|
||||
</template>
|
||||
</v-example>
|
||||
</template>
|
||||
111
src/demo/examples/GlChart.vue
Normal file
111
src/demo/examples/GlChart.vue
Normal file
@ -0,0 +1,111 @@
|
||||
<script setup>
|
||||
import { use } from "echarts/core";
|
||||
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 world from "../assets/world.jpg";
|
||||
import starfield from "../assets/starfield.jpg";
|
||||
|
||||
use([Bar3DChart, VisualMapComponent, GlobeComponent]);
|
||||
|
||||
const option = shallowRef();
|
||||
const loading = shallowRef(true);
|
||||
|
||||
const initOptions = {
|
||||
renderer: "canvas"
|
||||
};
|
||||
|
||||
const loadingOptions = {
|
||||
text: "Loading...",
|
||||
color: "#000",
|
||||
textColor: "#fff",
|
||||
maskColor: "transparent"
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
import("../data/population.json").then(({ default: data }) => {
|
||||
loading.value = false;
|
||||
|
||||
data = data
|
||||
.filter(dataItem => dataItem[2] > 0)
|
||||
.map(dataItem => [dataItem[0], dataItem[1], Math.sqrt(dataItem[2])]);
|
||||
|
||||
option.value = {
|
||||
backgroundColor: "#000",
|
||||
globe: {
|
||||
baseTexture: world,
|
||||
heightTexture: world,
|
||||
shading: "lambert",
|
||||
environment: starfield,
|
||||
light: {
|
||||
main: {
|
||||
intensity: 2
|
||||
}
|
||||
},
|
||||
viewControl: {
|
||||
autoRotate: false
|
||||
}
|
||||
},
|
||||
visualMap: {
|
||||
bottom: "3%",
|
||||
left: "3%",
|
||||
max: 40,
|
||||
calculable: true,
|
||||
realtime: false,
|
||||
inRange: {
|
||||
colorLightness: [0.2, 0.9]
|
||||
},
|
||||
textStyle: {
|
||||
color: "#fff"
|
||||
},
|
||||
controller: {
|
||||
inRange: {
|
||||
color: "orange"
|
||||
}
|
||||
},
|
||||
outOfRange: {
|
||||
colorAlpha: 0
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: "bar3D",
|
||||
coordinateSystem: "globe",
|
||||
data: data,
|
||||
barSize: 0.6,
|
||||
minHeight: 0.2,
|
||||
silent: true,
|
||||
itemStyle: {
|
||||
color: "orange"
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-example id="gl" title="GL charts" desc="(Globe & Bar3D)">
|
||||
<v-chart
|
||||
:option="option"
|
||||
:init-options="initOptions"
|
||||
autoresize
|
||||
:loading="loading"
|
||||
:loading-options="loadingOptions"
|
||||
style="background-color: #000"
|
||||
/>
|
||||
<template #extra>
|
||||
<p>
|
||||
You can use extension packs like
|
||||
<a href="https://github.com/ecomfe/echarts-gl">ECharts-GL</a>.
|
||||
</p>
|
||||
<p>
|
||||
<small>(You can only use the canvas renderer for GL charts.)</small>
|
||||
</p>
|
||||
</template>
|
||||
</v-example>
|
||||
</template>
|
||||
14
src/demo/examples/LogoChart.vue
Normal file
14
src/demo/examples/LogoChart.vue
Normal file
@ -0,0 +1,14 @@
|
||||
<script setup>
|
||||
import { registerTheme } from "echarts/core";
|
||||
|
||||
import "echarts-liquidfill";
|
||||
import VChart from "../../ECharts";
|
||||
import theme from "../theme.json";
|
||||
import logo from "../data/logo";
|
||||
|
||||
registerTheme("ovilia-green", theme);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-chart id="logo" :option="logo" />
|
||||
</template>
|
||||
116
src/demo/examples/ManualChart.vue
Normal file
116
src/demo/examples/ManualChart.vue
Normal file
@ -0,0 +1,116 @@
|
||||
<script setup>
|
||||
import { use, registerMap } from "echarts/core";
|
||||
import { LinesChart } from "echarts/charts";
|
||||
import {
|
||||
GeoComponent,
|
||||
TitleComponent,
|
||||
TooltipComponent
|
||||
} from "echarts/components";
|
||||
import { shallowRef } from "vue";
|
||||
import VChart from "../../ECharts";
|
||||
import VExample from "./Example";
|
||||
import worldMap from "../world.json";
|
||||
|
||||
use([LinesChart, GeoComponent, TitleComponent, TooltipComponent]);
|
||||
registerMap("world", worldMap);
|
||||
|
||||
const chart = shallowRef(null);
|
||||
const loading = shallowRef(false);
|
||||
const loaded = shallowRef(false);
|
||||
|
||||
const loadingOptions = {
|
||||
text: "",
|
||||
color: "#c23531",
|
||||
textColor: "rgba(255, 255, 255, 0.5)",
|
||||
maskColor: "#003",
|
||||
zlevel: 0
|
||||
};
|
||||
|
||||
function load() {
|
||||
loaded.value = true;
|
||||
loading.value = true;
|
||||
|
||||
import("../data/flight.json").then(({ default: data }) => {
|
||||
loading.value = false;
|
||||
|
||||
function getAirportCoord(idx) {
|
||||
return [data.airports[idx][3], data.airports[idx][4]];
|
||||
}
|
||||
const routes = data.routes.map(airline => {
|
||||
return [getAirportCoord(airline[1]), getAirportCoord(airline[2])];
|
||||
});
|
||||
|
||||
chart.value.setOption({
|
||||
textStyle: {
|
||||
fontFamily: 'Inter, "Helvetica Neue", Arial, sans-serif'
|
||||
},
|
||||
title: {
|
||||
text: "World Flights",
|
||||
top: "5%",
|
||||
left: "center",
|
||||
textStyle: {
|
||||
color: "#eee"
|
||||
}
|
||||
},
|
||||
backgroundColor: "#003",
|
||||
tooltip: {
|
||||
formatter(param) {
|
||||
const route = data.routes[param.dataIndex];
|
||||
return (
|
||||
data.airports[route[1]][1] + " > " + data.airports[route[2]][1]
|
||||
);
|
||||
}
|
||||
},
|
||||
geo: {
|
||||
map: "world",
|
||||
top: "15%",
|
||||
right: "5%",
|
||||
bottom: "5%",
|
||||
left: "5%",
|
||||
silent: true,
|
||||
itemStyle: {
|
||||
borderColor: "#003",
|
||||
color: "#005"
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: "lines",
|
||||
coordinateSystem: "geo",
|
||||
data: routes,
|
||||
large: true,
|
||||
largeThreshold: 100,
|
||||
lineStyle: {
|
||||
opacity: 0.05,
|
||||
width: 0.5,
|
||||
curveness: 0.3
|
||||
},
|
||||
blendMode: "lighter"
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-example id="manual" title="Manual updates">
|
||||
<v-chart
|
||||
ref="chart"
|
||||
autoresize
|
||||
:loading="loading"
|
||||
:loading-options="loadingOptions"
|
||||
style="background-color: #003"
|
||||
manual-update
|
||||
/>
|
||||
<template #extra>
|
||||
<p>
|
||||
You may use the <code>manual-update</code> prop for performance critical
|
||||
use cases.
|
||||
</p>
|
||||
<p class="actions">
|
||||
<button :disabled="loaded" @click="load">Load</button>
|
||||
</p>
|
||||
</template>
|
||||
</v-example>
|
||||
</template>
|
||||
82
src/demo/examples/PieChart.vue
Normal file
82
src/demo/examples/PieChart.vue
Normal file
@ -0,0 +1,82 @@
|
||||
<script setup>
|
||||
import { use } from "echarts/core";
|
||||
import { PieChart } from "echarts/charts";
|
||||
import {
|
||||
PolarComponent,
|
||||
TitleComponent,
|
||||
LegendComponent,
|
||||
TooltipComponent
|
||||
} from "echarts/components";
|
||||
import { shallowRef, onMounted } from "vue";
|
||||
import VChart from "../../ECharts";
|
||||
import VExample from "./Example";
|
||||
import getData from "../data/pie";
|
||||
|
||||
use([
|
||||
PieChart,
|
||||
PolarComponent,
|
||||
TitleComponent,
|
||||
LegendComponent,
|
||||
TooltipComponent
|
||||
]);
|
||||
|
||||
const option = shallowRef(getData());
|
||||
const pie = shallowRef(null);
|
||||
|
||||
let timer = null;
|
||||
|
||||
onMounted(() => {
|
||||
startActions();
|
||||
|
||||
return () => {
|
||||
stopActions();
|
||||
};
|
||||
});
|
||||
|
||||
function startActions() {
|
||||
let dataIndex = -1;
|
||||
|
||||
const dataLen = option.value?.series?.[0]?.data?.length || 0;
|
||||
|
||||
if (!pie.value || dataLen === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
clearInterval(timer);
|
||||
|
||||
timer = setInterval(() => {
|
||||
if (!pie.value) {
|
||||
clearInterval(timer);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
pie.value.dispatchAction({
|
||||
type: "downplay",
|
||||
seriesIndex: 0,
|
||||
dataIndex
|
||||
});
|
||||
dataIndex = (dataIndex + 1) % dataLen;
|
||||
pie.value.dispatchAction({
|
||||
type: "highlight",
|
||||
seriesIndex: 0,
|
||||
dataIndex
|
||||
});
|
||||
pie.value.dispatchAction({
|
||||
type: "showTip",
|
||||
seriesIndex: 0,
|
||||
dataIndex
|
||||
});
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function stopActions() {
|
||||
clearInterval(timer);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-example id="pie" title="Pie chart" desc="(with action dispatch)">
|
||||
<v-chart ref="pie" :option="option" autoresize />
|
||||
</v-example>
|
||||
</template>
|
||||
66
src/demo/examples/PolarChart.vue
Normal file
66
src/demo/examples/PolarChart.vue
Normal file
@ -0,0 +1,66 @@
|
||||
<script setup>
|
||||
import { use } from "echarts/core";
|
||||
import { LineChart } from "echarts/charts";
|
||||
import {
|
||||
PolarComponent,
|
||||
TitleComponent,
|
||||
LegendComponent,
|
||||
TooltipComponent
|
||||
} from "echarts/components";
|
||||
import { computed, shallowRef } from "vue";
|
||||
import VChart from "../../ECharts";
|
||||
import VExample from "./Example";
|
||||
import getData from "../data/polar";
|
||||
|
||||
use([
|
||||
LineChart,
|
||||
PolarComponent,
|
||||
TitleComponent,
|
||||
LegendComponent,
|
||||
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>
|
||||
<v-example id="polar" title="Polar plot" desc="(with built-in theme)">
|
||||
<v-chart
|
||||
:option="option"
|
||||
autoresize
|
||||
:loading="loading"
|
||||
:loading-options="loadingOptions"
|
||||
:theme="theme"
|
||||
:style="style"
|
||||
/>
|
||||
<template #extra>
|
||||
<p class="actions">
|
||||
Theme
|
||||
<select v-model="theme">
|
||||
<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>
|
||||
</template>
|
||||
50
src/demo/examples/RadarChart.vue
Normal file
50
src/demo/examples/RadarChart.vue
Normal file
@ -0,0 +1,50 @@
|
||||
<script setup>
|
||||
import { use } from "echarts/core";
|
||||
import { RadarChart } from "echarts/charts";
|
||||
import {
|
||||
PolarComponent,
|
||||
TitleComponent,
|
||||
TooltipComponent
|
||||
} from "echarts/components";
|
||||
import { shallowRef } from "vue";
|
||||
import VChart from "../../ECharts";
|
||||
import VExample from "./Example";
|
||||
import { useScoreStore } from "../data/radar";
|
||||
|
||||
use([RadarChart, PolarComponent, TitleComponent, TooltipComponent]);
|
||||
|
||||
const { metrics, getRadarData, increase, isMax, isMin } = useScoreStore();
|
||||
|
||||
const metricIndex = shallowRef(0);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-example id="radar" title="Radar chart" desc="(with Pinia integration)">
|
||||
<v-chart :option="getRadarData(metricIndex)" autoresize />
|
||||
<template #extra>
|
||||
<p class="actions">
|
||||
<select v-model="metricIndex">
|
||||
<option
|
||||
v-for="(metric, index) in metrics"
|
||||
:value="index"
|
||||
:key="index"
|
||||
>
|
||||
{{ metric }}
|
||||
</option>
|
||||
</select>
|
||||
<button
|
||||
@click="increase(metricIndex, 1)"
|
||||
:disabled="isMax(metricIndex)"
|
||||
>
|
||||
Increase
|
||||
</button>
|
||||
<button
|
||||
@click="increase(metricIndex, -1)"
|
||||
:disabled="isMin(metricIndex)"
|
||||
>
|
||||
Decrease
|
||||
</button>
|
||||
</p>
|
||||
</template>
|
||||
</v-example>
|
||||
</template>
|
||||
23
src/demo/examples/ScatterChart.vue
Normal file
23
src/demo/examples/ScatterChart.vue
Normal file
@ -0,0 +1,23 @@
|
||||
<script setup>
|
||||
import { use } from "echarts/core";
|
||||
import { ScatterChart } from "echarts/charts";
|
||||
import {
|
||||
GridComponent,
|
||||
TitleComponent,
|
||||
LegendComponent
|
||||
} from "echarts/components";
|
||||
import { shallowRef } from "vue";
|
||||
import VChart from "../../ECharts";
|
||||
import VExample from "./Example";
|
||||
import getData from "../data/scatter";
|
||||
|
||||
use([ScatterChart, GridComponent, TitleComponent, LegendComponent]);
|
||||
|
||||
const option = shallowRef(getData());
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-example id="scatter" title="Scatter plot" desc="(with gradient)">
|
||||
<v-chart :option="option" autoresize />
|
||||
</v-example>
|
||||
</template>
|
||||
@ -1,5 +1,6 @@
|
||||
{
|
||||
"files": [
|
||||
"./Demo.vue"
|
||||
"./Demo.vue",
|
||||
"./examples/RadarChart.vue"
|
||||
],
|
||||
}
|
||||
|
||||
@ -1,4 +1,21 @@
|
||||
import { inject } from "@vercel/analytics";
|
||||
import { createApp } from "vue";
|
||||
import { createPinia } from "pinia";
|
||||
import Demo from "./Demo.vue";
|
||||
|
||||
createApp(Demo).mount("#app");
|
||||
const SAMPLE_RATE = 0.5;
|
||||
|
||||
inject({
|
||||
beforeSend: event => {
|
||||
if (Math.random() > SAMPLE_RATE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
});
|
||||
|
||||
const pinia = createPinia();
|
||||
const app = createApp(Demo);
|
||||
app.use(pinia);
|
||||
app.mount("#app");
|
||||
|
||||
201
src/demo/utils/LICENSE
Normal file
201
src/demo/utils/LICENSE
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
472
src/demo/utils/codegen.js
Normal file
472
src/demo/utils/codegen.js
Normal file
@ -0,0 +1,472 @@
|
||||
// Modified from https://github.com/apache/echarts-examples/blob/b644ced5325ea2522cb11606df54eae69bba3a3a/common/buildCode.js
|
||||
// See license at `./LICENSE`.
|
||||
|
||||
const COMPONENTS_MAP = {
|
||||
grid: "GridComponent",
|
||||
polar: "PolarComponent",
|
||||
geo: "GeoComponent",
|
||||
singleAxis: "SingleAxisComponent",
|
||||
parallel: "ParallelComponent",
|
||||
calendar: "CalendarComponent",
|
||||
graphic: "GraphicComponent",
|
||||
toolbox: "ToolboxComponent",
|
||||
tooltip: "TooltipComponent",
|
||||
axisPointer: "AxisPointerComponent",
|
||||
brush: "BrushComponent",
|
||||
title: "TitleComponent",
|
||||
timeline: "TimelineComponent",
|
||||
markPoint: "MarkPointComponent",
|
||||
markLine: "MarkLineComponent",
|
||||
markArea: "MarkAreaComponent",
|
||||
legend: "LegendComponent",
|
||||
dataZoom: "DataZoomComponent",
|
||||
visualMap: "VisualMapComponent",
|
||||
aria: "AriaComponent",
|
||||
dataset: "DatasetComponent",
|
||||
|
||||
// Dependencies
|
||||
xAxis: "GridComponent",
|
||||
yAxis: "GridComponent",
|
||||
angleAxis: "PolarComponent",
|
||||
radiusAxis: "PolarComponent"
|
||||
};
|
||||
|
||||
const CHARTS_MAP = {
|
||||
line: "LineChart",
|
||||
bar: "BarChart",
|
||||
pie: "PieChart",
|
||||
scatter: "ScatterChart",
|
||||
radar: "RadarChart",
|
||||
map: "MapChart",
|
||||
tree: "TreeChart",
|
||||
treemap: "TreemapChart",
|
||||
graph: "GraphChart",
|
||||
gauge: "GaugeChart",
|
||||
funnel: "FunnelChart",
|
||||
parallel: "ParallelChart",
|
||||
sankey: "SankeyChart",
|
||||
boxplot: "BoxplotChart",
|
||||
candlestick: "CandlestickChart",
|
||||
effectScatter: "EffectScatterChart",
|
||||
lines: "LinesChart",
|
||||
heatmap: "HeatmapChart",
|
||||
pictorialBar: "PictorialBarChart",
|
||||
themeRiver: "ThemeRiverChart",
|
||||
sunburst: "SunburstChart",
|
||||
custom: "CustomChart"
|
||||
};
|
||||
|
||||
const COMPONENTS_GL_MAP = {
|
||||
grid3D: "Grid3DComponent",
|
||||
geo3D: "Geo3DComponent",
|
||||
globe: "GlobeComponent",
|
||||
mapbox3D: "Mapbox3DComponent",
|
||||
maptalks3D: "Maptalks3DComponent",
|
||||
|
||||
// Dependencies
|
||||
xAxis3D: "Grid3DComponent",
|
||||
yAxis3D: "Grid3DComponent",
|
||||
zAxis3D: "Grid3DComponent"
|
||||
};
|
||||
|
||||
const CHARTS_GL_MAP = {
|
||||
bar3D: "Bar3DChart",
|
||||
line3D: "Line3DChart",
|
||||
scatter3D: "Scatter3DChart",
|
||||
lines3D: "Lines3DChart",
|
||||
polygons3D: "Polygons3DChart",
|
||||
surface: "SurfaceChart",
|
||||
map3D: "Map3DChart",
|
||||
|
||||
scatterGL: "ScatterGLChart",
|
||||
graphGL: "GraphGLChart",
|
||||
flowGL: "FlowGLChart",
|
||||
linesGL: "LinesGLChart"
|
||||
};
|
||||
|
||||
const FEATURES = ["UniversalTransition", "LabelLayout"];
|
||||
const RENDERERS_MAP = {
|
||||
canvas: "CanvasRenderer",
|
||||
svg: "SVGRenderer"
|
||||
};
|
||||
|
||||
const EXTENSIONS_MAP = {
|
||||
bmap: "bmap/bmap"
|
||||
// PENDING: There seem no examples that use dataTool
|
||||
// dataTool: 'dataTool'
|
||||
};
|
||||
|
||||
// Component that will be injected automatically in preprocessor
|
||||
// These should be excluded util find they were used explicitly.
|
||||
const MARKERS = ["markLine", "markArea", "markPoint"];
|
||||
const INJECTED_COMPONENTS = [
|
||||
...MARKERS,
|
||||
"grid",
|
||||
"axisPointer",
|
||||
"aria" // TODO aria
|
||||
];
|
||||
|
||||
// Component that was dependent.
|
||||
const DEPENDENT_COMPONENTS = [
|
||||
"xAxis",
|
||||
"yAxis",
|
||||
"angleAxis",
|
||||
"radiusAxis",
|
||||
"xAxis3D",
|
||||
"yAxis3D",
|
||||
"zAxis3D"
|
||||
];
|
||||
|
||||
function createReverseMap(map) {
|
||||
const reverseMap = {};
|
||||
Object.keys(map).forEach(key => {
|
||||
// Exclude dependencies.
|
||||
if (DEPENDENT_COMPONENTS.includes(key)) {
|
||||
return;
|
||||
}
|
||||
reverseMap[map[key]] = key;
|
||||
});
|
||||
|
||||
return reverseMap;
|
||||
}
|
||||
|
||||
const COMPONENTS_MAP_REVERSE = createReverseMap(COMPONENTS_MAP);
|
||||
const CHARTS_MAP_REVERSE = createReverseMap(CHARTS_MAP);
|
||||
const COMPONENTS_GL_MAP_REVERSE = createReverseMap(COMPONENTS_GL_MAP);
|
||||
const CHARTS_GL_MAP_REVERSE = createReverseMap(CHARTS_GL_MAP);
|
||||
|
||||
function collectDeps(option) {
|
||||
let deps = [];
|
||||
if (option.options) {
|
||||
// TODO getOption() doesn't have baseOption and options.
|
||||
option.options.forEach(opt => {
|
||||
deps = deps.concat(collectDeps(opt));
|
||||
});
|
||||
|
||||
if (option.baseOption) {
|
||||
deps = deps.concat(collectDeps(option.baseOption));
|
||||
}
|
||||
|
||||
// Remove duplicates
|
||||
return Array.from(new Set(deps));
|
||||
}
|
||||
|
||||
Object.keys(option).forEach(key => {
|
||||
if (INJECTED_COMPONENTS.includes(key)) {
|
||||
return;
|
||||
}
|
||||
const val = option[key];
|
||||
|
||||
if (Array.isArray(val) && !val.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (COMPONENTS_MAP[key]) {
|
||||
deps.push(COMPONENTS_MAP[key]);
|
||||
}
|
||||
if (COMPONENTS_GL_MAP[key]) {
|
||||
deps.push(COMPONENTS_GL_MAP[key]);
|
||||
}
|
||||
if (EXTENSIONS_MAP[key]) {
|
||||
deps.push(key);
|
||||
}
|
||||
});
|
||||
|
||||
let series = option.series;
|
||||
if (!Array.isArray(series)) {
|
||||
series = [series];
|
||||
}
|
||||
|
||||
series.forEach(seriesOpt => {
|
||||
if (CHARTS_MAP[seriesOpt.type]) {
|
||||
deps.push(CHARTS_MAP[seriesOpt.type]);
|
||||
}
|
||||
if (CHARTS_GL_MAP[seriesOpt.type]) {
|
||||
deps.push(CHARTS_GL_MAP[seriesOpt.type]);
|
||||
}
|
||||
if (seriesOpt.type === "map") {
|
||||
// Needs geo component when using map
|
||||
deps.push(COMPONENTS_MAP.geo);
|
||||
}
|
||||
if (seriesOpt.coordinateSystem === "bmap") {
|
||||
deps.push("bmap");
|
||||
}
|
||||
MARKERS.forEach(markerType => {
|
||||
if (seriesOpt[markerType]) {
|
||||
deps.push(COMPONENTS_MAP[markerType]);
|
||||
}
|
||||
});
|
||||
// Features
|
||||
if (seriesOpt.labelLayout) {
|
||||
deps.push("LabelLayout");
|
||||
}
|
||||
if (seriesOpt.universalTransition) {
|
||||
deps.push("UniversalTransition");
|
||||
}
|
||||
});
|
||||
// Dataset transform
|
||||
if (option.dataset && Array.isArray(option.dataset)) {
|
||||
option.dataset.forEach(dataset => {
|
||||
if (dataset.transform) {
|
||||
deps.push("TransformComponent");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Remove duplicates
|
||||
return Array.from(new Set(deps));
|
||||
}
|
||||
|
||||
function buildMinimalBundleCode(
|
||||
deps,
|
||||
{
|
||||
includeType,
|
||||
semi = false,
|
||||
quote = "'",
|
||||
multiline = false,
|
||||
indent = " ",
|
||||
maxLen = 80
|
||||
}
|
||||
) {
|
||||
const options = {
|
||||
semi,
|
||||
quote,
|
||||
multiline,
|
||||
indent,
|
||||
maxLen
|
||||
};
|
||||
|
||||
const chartsImports = [];
|
||||
const componentsImports = [];
|
||||
const chartsGLImports = [];
|
||||
const componentsGLImports = [];
|
||||
const featuresImports = [];
|
||||
const renderersImports = [];
|
||||
const extensionImports = [];
|
||||
|
||||
deps.forEach(dep => {
|
||||
if (dep.endsWith("Renderer")) {
|
||||
renderersImports.push(dep);
|
||||
} else if (CHARTS_MAP_REVERSE[dep]) {
|
||||
chartsImports.push(dep);
|
||||
if (includeType) {
|
||||
chartsImports.push(dep.replace(/Chart$/, "SeriesOption"));
|
||||
}
|
||||
} else if (COMPONENTS_MAP_REVERSE[dep]) {
|
||||
componentsImports.push(dep);
|
||||
if (includeType) {
|
||||
componentsImports.push(dep.replace(/Component$/, "ComponentOption"));
|
||||
}
|
||||
} else if (dep === "TransformComponent") {
|
||||
// TransformComponent don't have individual option type.
|
||||
// TODO will put in to an config if there are other similar components
|
||||
componentsImports.push(dep);
|
||||
} else if (CHARTS_GL_MAP_REVERSE[dep]) {
|
||||
chartsGLImports.push(dep);
|
||||
} else if (COMPONENTS_GL_MAP_REVERSE[dep]) {
|
||||
componentsGLImports.push(dep);
|
||||
} else if (FEATURES.includes(dep)) {
|
||||
featuresImports.push(dep);
|
||||
} else if (EXTENSIONS_MAP[dep]) {
|
||||
extensionImports.push(dep);
|
||||
}
|
||||
});
|
||||
|
||||
const allImports = [
|
||||
...componentsImports,
|
||||
...chartsImports,
|
||||
...componentsGLImports,
|
||||
...chartsGLImports,
|
||||
...renderersImports,
|
||||
...featuresImports
|
||||
];
|
||||
|
||||
const ECOptionTypeCode = typeItems(
|
||||
allImports.filter(a => a.endsWith("Option")),
|
||||
options
|
||||
);
|
||||
|
||||
const importSources = [
|
||||
[chartsImports, "echarts/charts"],
|
||||
[componentsImports, "echarts/components"],
|
||||
[featuresImports, "echarts/features"],
|
||||
[renderersImports, "echarts/renderers"],
|
||||
[chartsGLImports, "echarts-gl/charts"],
|
||||
[componentsGLImports, "echarts-gl/components"]
|
||||
].filter(a => a[0].length > 0);
|
||||
const importStatements = importSources.map(([imports, mod]) =>
|
||||
importItems(
|
||||
imports.filter(a => !a.endsWith("Option")),
|
||||
mod,
|
||||
options
|
||||
)
|
||||
);
|
||||
|
||||
const semiStr = semi ? ";" : "";
|
||||
|
||||
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}`
|
||||
);
|
||||
const importTypeStatements = importSources
|
||||
.map(([imports, mod]) =>
|
||||
importItems(
|
||||
imports.filter(a => a.endsWith("Option")),
|
||||
mod,
|
||||
{ ...options, type: true }
|
||||
)
|
||||
)
|
||||
.filter(Boolean);
|
||||
importStatements.push(...importTypeStatements);
|
||||
}
|
||||
|
||||
return `import { use } from ${quote}echarts/core${quote}${semiStr}
|
||||
${importStatements.join("\n")}
|
||||
|
||||
${useItems(
|
||||
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]}`);
|
||||
}
|
||||
|
||||
/** import */
|
||||
function importItems(items, module, options) {
|
||||
if (items.length === 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const { multiline, maxLen } = options;
|
||||
|
||||
if (multiline) {
|
||||
return importMultiLine(items, module, options);
|
||||
}
|
||||
|
||||
const singleLine = importSingleLine(items, module, options);
|
||||
if (singleLine.length <= maxLen) {
|
||||
return singleLine;
|
||||
}
|
||||
|
||||
return importMultiLine(items, module, options);
|
||||
}
|
||||
|
||||
// import { foo, bar } from 'module'
|
||||
function importSingleLine(items, module, { type, semi, quote }) {
|
||||
const typeStr = type ? "type " : "";
|
||||
const semiStr = semi ? ";" : "";
|
||||
|
||||
return `import ${typeStr}{ ${items.join(
|
||||
", "
|
||||
)} } from ${quote}${module}${quote}${semiStr}`;
|
||||
}
|
||||
|
||||
// import {
|
||||
// foo,
|
||||
// bar
|
||||
// } from 'module'
|
||||
function importMultiLine(items, module, { type, indent, semi, quote }) {
|
||||
const typeStr = type ? "type " : "";
|
||||
const semiStr = semi ? ";" : "";
|
||||
|
||||
return `import ${typeStr}{
|
||||
${items.map(item => `${indent}${item}`).join(",\n")}
|
||||
} from ${quote}${module}${quote}${semiStr}`;
|
||||
}
|
||||
|
||||
/** use */
|
||||
function useItems(items, options) {
|
||||
if (items.length === 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const { multiline, maxLen } = options;
|
||||
|
||||
if (multiline) {
|
||||
return useMultiLine(items, options);
|
||||
}
|
||||
|
||||
const singleLine = useSingleLine(items, options);
|
||||
if (singleLine.length <= maxLen) {
|
||||
return singleLine;
|
||||
}
|
||||
|
||||
return useMultiLine(items, options);
|
||||
}
|
||||
|
||||
// use([foo, bar])
|
||||
function useSingleLine(items, { semi }) {
|
||||
const semiStr = semi ? ";" : "";
|
||||
|
||||
return `use([${items.join(`, `)}])${semiStr}`;
|
||||
}
|
||||
|
||||
// use([
|
||||
// foo,
|
||||
// bar
|
||||
// ])
|
||||
function useMultiLine(items, { indent, semi }) {
|
||||
const semiStr = semi ? ";" : "";
|
||||
|
||||
return `use([
|
||||
${items.map(item => `${indent}${item}`).join(`,\n`)}
|
||||
])${semiStr}`;
|
||||
}
|
||||
|
||||
/** type */
|
||||
function typeItems(items, options) {
|
||||
const { multiline, maxLen } = options;
|
||||
|
||||
if (items.length === 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (multiline) {
|
||||
return typeMultiLine(items, options);
|
||||
}
|
||||
|
||||
const singleLine = typeSingleLine(items, options);
|
||||
if (singleLine.length <= maxLen) {
|
||||
return singleLine;
|
||||
}
|
||||
|
||||
return typeMultiLine(items, options);
|
||||
}
|
||||
|
||||
// type EChartsOption = ComposeOption<FooOption | BarOption>
|
||||
function typeSingleLine(items, { semi }) {
|
||||
const semiStr = semi ? ";" : "";
|
||||
|
||||
return `type EChartsOption = ComposeOption<${items.join(` | `)}>${semiStr}`;
|
||||
}
|
||||
|
||||
// type EChartsOption = ComposeOption<
|
||||
// | FooOption
|
||||
// | BarOption
|
||||
// >
|
||||
function typeMultiLine(items, { indent, semi }) {
|
||||
const semiStr = semi ? ";" : "";
|
||||
|
||||
return `type EChartsOption = ComposeOption<
|
||||
${items.map(item => `${indent}| ${item}`).join("\n")}
|
||||
>${semiStr}`;
|
||||
}
|
||||
|
||||
export function getImportsFromOption(
|
||||
option,
|
||||
{ renderer = "canvas", ...options }
|
||||
) {
|
||||
return buildMinimalBundleCode(
|
||||
[...collectDeps(option), RENDERERS_MAP[renderer]],
|
||||
options
|
||||
);
|
||||
}
|
||||
45
src/index.vue2.d.ts → src/index.d.ts
vendored
45
src/index.vue2.d.ts → src/index.d.ts
vendored
@ -1,30 +1,44 @@
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
import type { Ref, DefineComponent } from "vue-demi";
|
||||
import type { Ref, DefineComponent, InjectionKey } from "vue-demi";
|
||||
import type {
|
||||
Option,
|
||||
Theme,
|
||||
InitOptions,
|
||||
UpdateOptions,
|
||||
LoadingOptions,
|
||||
EChartsType,
|
||||
Emits
|
||||
Emits,
|
||||
ThemeInjection,
|
||||
InitOptionsInjection,
|
||||
UpdateOptionsInjection,
|
||||
LoadingOptionsInjection
|
||||
} 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 const THEME_KEY: InjectionKey<ThemeInjection>;
|
||||
declare const INIT_OPTIONS_KEY: InjectionKey<InitOptionsInjection>;
|
||||
declare const UPDATE_OPTIONS_KEY: InjectionKey<UpdateOptionsInjection>;
|
||||
declare const LOADING_OPTIONS_KEY: InjectionKey<LoadingOptionsInjection>;
|
||||
|
||||
declare type ChartProps = {
|
||||
loading?: boolean;
|
||||
loadingOptions?: Record<string, unknown>;
|
||||
autoresize?: boolean;
|
||||
option?: Option;
|
||||
theme?: string | Record<string, unknown>;
|
||||
theme?: Theme;
|
||||
initOptions?: InitOptions;
|
||||
updateOptions?: UpdateOptions;
|
||||
loadingOptions?: LoadingOptions;
|
||||
option?: Option;
|
||||
autoresize?: boolean;
|
||||
loading?: boolean;
|
||||
group?: string;
|
||||
manualUpdate?: boolean;
|
||||
};
|
||||
|
||||
// convert Emits to Props
|
||||
// click => onClick
|
||||
declare type ChartEventProps = {
|
||||
[key in keyof Emits as key extends string
|
||||
? `on${Capitalize<key>}`
|
||||
: never]?: Emits[key];
|
||||
};
|
||||
|
||||
type MethodNames =
|
||||
| "getWidth"
|
||||
| "getHeight"
|
||||
@ -46,18 +60,15 @@ type MethodNames =
|
||||
declare type ChartMethods = Pick<EChartsType, MethodNames>;
|
||||
|
||||
declare const Chart: DefineComponent<
|
||||
ChartProps,
|
||||
ChartProps & ChartEventProps,
|
||||
{
|
||||
root: Ref<HTMLElement | undefined>;
|
||||
chart: Ref<EChartsType | undefined>;
|
||||
},
|
||||
{},
|
||||
{},
|
||||
ChartMethods,
|
||||
{},
|
||||
{},
|
||||
Emits
|
||||
ChartMethods
|
||||
>;
|
||||
|
||||
export default Chart;
|
||||
export { INIT_OPTIONS_KEY, LOADING_OPTIONS_KEY, THEME_KEY, UPDATE_OPTIONS_KEY };
|
||||
export { THEME_KEY, INIT_OPTIONS_KEY, UPDATE_OPTIONS_KEY, LOADING_OPTIONS_KEY };
|
||||
106
src/types.ts
106
src/types.ts
@ -1,5 +1,7 @@
|
||||
import { init, type SetOptionOpts } from "echarts/core";
|
||||
import type { Ref } from "vue";
|
||||
import { init } from "echarts/core";
|
||||
|
||||
import type { SetOptionOpts, ECElementEvent, ElementEvent } from "echarts/core";
|
||||
import type { Ref } from "vue-demi";
|
||||
|
||||
export type Injection<T> = T | null | Ref<T | null> | { value: T | null };
|
||||
|
||||
@ -17,20 +19,58 @@ export type UpdateOptionsInjection = Injection<UpdateOptions>;
|
||||
export type EChartsType = ReturnType<InitType>;
|
||||
type ZRenderType = ReturnType<EChartsType["getZr"]>;
|
||||
export type EventTarget = EChartsType | ZRenderType;
|
||||
|
||||
type SetOptionType = EChartsType["setOption"];
|
||||
export type Option = Parameters<SetOptionType>[0];
|
||||
|
||||
type EChartsMouseEventName =
|
||||
export type AutoResize =
|
||||
| boolean
|
||||
| {
|
||||
throttle?: number;
|
||||
onResize?: () => void;
|
||||
};
|
||||
|
||||
export type LoadingOptions = {
|
||||
text?: string;
|
||||
textColor?: string;
|
||||
fontSize?: number | string;
|
||||
fontWeight?: number | string;
|
||||
fontStyle?: string;
|
||||
fontFamily?: string;
|
||||
maskColor?: string;
|
||||
showSpinner?: boolean;
|
||||
color?: string;
|
||||
spinnerRadius?: number;
|
||||
lineWidth?: number;
|
||||
zlevel?: number;
|
||||
};
|
||||
export type LoadingOptionsInjection = Injection<LoadingOptions>;
|
||||
|
||||
type MouseEventName =
|
||||
| "click"
|
||||
| "dblclick"
|
||||
| "mouseout"
|
||||
| "mouseover"
|
||||
| "mouseup"
|
||||
| "mousedown"
|
||||
| "mousemove"
|
||||
| "mouseup"
|
||||
| "mouseover"
|
||||
| "mouseout"
|
||||
| "globalout"
|
||||
| "contextmenu";
|
||||
type EChartsOtherEventName =
|
||||
| "contextmenu"
|
||||
| "globalout";
|
||||
|
||||
type ElementEventName =
|
||||
| MouseEventName
|
||||
| "mousewheel"
|
||||
| "drag"
|
||||
| "dragstart"
|
||||
| "dragend"
|
||||
| "dragenter"
|
||||
| "dragleave"
|
||||
| "dragover"
|
||||
| "drop";
|
||||
|
||||
type ZRenderEventName = `zr:${ElementEventName}`;
|
||||
|
||||
type OtherEventName =
|
||||
| "highlight"
|
||||
| "downplay"
|
||||
| "selectchanged"
|
||||
@ -57,46 +97,22 @@ type EChartsOtherEventName =
|
||||
| "brush"
|
||||
| "brushEnd"
|
||||
| "brushselected"
|
||||
| "globalcursortaken"
|
||||
| "rendered"
|
||||
| "finished";
|
||||
type ZRenderEventName =
|
||||
| "click"
|
||||
| "dblclick"
|
||||
| "mousewheel"
|
||||
| "mouseout"
|
||||
| "mouseover"
|
||||
| "mouseup"
|
||||
| "mousedown"
|
||||
| "mousemove"
|
||||
| "contextmenu"
|
||||
| "drag"
|
||||
| "dragstart"
|
||||
| "dragend"
|
||||
| "dragenter"
|
||||
| "dragleave"
|
||||
| "dragover"
|
||||
| "drop"
|
||||
| "globalout";
|
||||
type OtherEventName = EChartsOtherEventName | `zr:${ZRenderEventName}`;
|
||||
|
||||
// See https://echarts.apache.org/en/api.html#events.Mouse%20events
|
||||
interface MouseEventParams {
|
||||
componentType: string;
|
||||
seriesType: string;
|
||||
seriesIndex: number;
|
||||
seriesName: string;
|
||||
name: string;
|
||||
dataIndex: number;
|
||||
color: string;
|
||||
}
|
||||
| "globalcursortaken";
|
||||
|
||||
type MouseEmits = {
|
||||
[k in EChartsMouseEventName]: (params: MouseEventParams) => boolean;
|
||||
[key in MouseEventName]: (params: ECElementEvent) => void;
|
||||
};
|
||||
|
||||
type ZRenderEmits = {
|
||||
[key in ZRenderEventName]: (params: ElementEvent) => void;
|
||||
};
|
||||
|
||||
type OtherEmits = {
|
||||
[key in OtherEventName]: null;
|
||||
[key in OtherEventName]: (params: any) => void;
|
||||
};
|
||||
|
||||
export type Emits = MouseEmits & OtherEmits;
|
||||
export type Emits = MouseEmits &
|
||||
OtherEmits & {
|
||||
rendered: (params: { elapsedTime: number }) => void;
|
||||
finished: () => void;
|
||||
} & ZRenderEmits;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { unref } from "vue-demi";
|
||||
import { unref, isRef } from "vue-demi";
|
||||
|
||||
import type { Injection } from "./types";
|
||||
|
||||
type Attrs = {
|
||||
@ -26,7 +27,7 @@ export function unwrapInjected<T, V>(
|
||||
injection: Injection<T>,
|
||||
defaultValue: V
|
||||
): T | V {
|
||||
const value = unref(injection);
|
||||
const value = isRef(injection) ? unref(injection) : injection;
|
||||
|
||||
if (value && typeof value === "object" && "value" in value) {
|
||||
return value.value || defaultValue;
|
||||
|
||||
18
src/wc.ts
18
src/wc.ts
@ -26,21 +26,9 @@ export function register(): boolean {
|
||||
// if the browser doesn't support native classes.
|
||||
const reg = new Function(
|
||||
"tag",
|
||||
`class EChartsElement extends HTMLElement {
|
||||
__dispose = null;
|
||||
|
||||
disconnectedCallback() {
|
||||
if (this.__dispose) {
|
||||
this.__dispose();
|
||||
this.__dispose = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (customElements.get(tag) == null) {
|
||||
customElements.define(tag, EChartsElement);
|
||||
}
|
||||
`
|
||||
// Use esbuild repl to keep build process simple
|
||||
// https://esbuild.github.io/try/#dAAwLjIzLjAALS1taW5pZnkAY2xhc3MgRUNoYXJ0c0VsZW1lbnQgZXh0ZW5kcyBIVE1MRWxlbWVudCB7CiAgX19kaXNwb3NlID0gbnVsbDsKCiAgZGlzY29ubmVjdGVkQ2FsbGJhY2soKSB7CiAgICBpZiAodGhpcy5fX2Rpc3Bvc2UpIHsKICAgICAgdGhpcy5fX2Rpc3Bvc2UoKTsKICAgICAgdGhpcy5fX2Rpc3Bvc2UgPSBudWxsOwogICAgfQogIH0KfQoKaWYgKGN1c3RvbUVsZW1lbnRzLmdldCh0YWcpID09IG51bGwpIHsKICBjdXN0b21FbGVtZW50cy5kZWZpbmUodGFnLCBFQ2hhcnRzRWxlbWVudCk7Cn0K
|
||||
"class EChartsElement extends HTMLElement{__dispose=null;disconnectedCallback(){this.__dispose&&(this.__dispose(),this.__dispose=null)}}customElements.get(tag)==null&&customElements.define(tag,EChartsElement);"
|
||||
);
|
||||
reg(TAG_NAME);
|
||||
} catch (e) {
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const nested = require("postcss-nested");
|
||||
import nested from "postcss-nested";
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
outputDir: "demo",
|
||||
css: {
|
||||
loaderOptions: {
|
||||
@ -19,7 +18,24 @@ module.exports = {
|
||||
.rule("svg")
|
||||
.clear()
|
||||
.test(/\.svg$/)
|
||||
.use("raw-loader")
|
||||
.loader("raw-loader");
|
||||
.type("asset/source");
|
||||
|
||||
config.module
|
||||
.rule("wasm")
|
||||
.test(/\.wasm$/)
|
||||
.type("asset/resource")
|
||||
.set("generator", {
|
||||
filename: "[name].[hash:8][ext]"
|
||||
});
|
||||
|
||||
config.plugin("define").tap(([options]) => [
|
||||
{
|
||||
...options,
|
||||
__CSP__: "false"
|
||||
}
|
||||
]);
|
||||
},
|
||||
devServer: {
|
||||
allowedHosts: "all"
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user