├── .eslintignore ├── .eslintrc.json ├── .gitignore ├── LICENSE ├── README.md ├── index.js ├── package.json ├── rollup.config.js └── src ├── components └── VueChart.js ├── index.js └── utils ├── equals.js └── update.js /.eslintignore: -------------------------------------------------------------------------------- 1 | /index.js 2 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | {"extends": "seregpie"} 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-2021 Sergej Sintschilin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VueChart 2 | 3 | A simple wrapper for Chart.js. 4 | 5 | Works for Vue 2 & 3. 6 | 7 | ## dependencies 8 | 9 | - [VueDemi](https://github.com/antfu/vue-demi) 10 | 11 | ## setup 12 | 13 | ### npm 14 | 15 | ```shell 16 | npm i @seregpie/vue-chart 17 | ``` 18 | 19 | --- 20 | 21 | ```javascript 22 | import VueChart from '@seregpie/vue-chart'; 23 | ``` 24 | 25 | ### browser 26 | 27 | ```html 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | ``` 39 | 40 | The module is globally available as `VueChart`. 41 | 42 | ## usage 43 | 44 | Register the component globally. 45 | 46 | ```javascript 47 | import {createApp} from 'vue'; 48 | import VueChart from '@seregpie/vue-chart'; 49 | 50 | let app = createApp({/*...*/}); 51 | app.component(VueChart.name, VueChart); 52 | app.mount('body'); 53 | ``` 54 | 55 | *or* 56 | 57 | Register the component locally. 58 | 59 | ```javascript 60 | import VueChart from '@seregpie/vue-chart'; 61 | 62 | export default { 63 | components: { 64 | VueChart, 65 | }, 66 | // ... 67 | }; 68 | ``` 69 | 70 | --- 71 | 72 | ```html 73 | 79 | ``` 80 | 81 | ## properties 82 | 83 | | name | type | description | 84 | | ---: | :--- | :--- | 85 | | `data` | `Object` | The data of the chart. | 86 | | `options` | `Object` | The configuration options of the chart of the current type. | 87 | | `type` | `String` | The type of the chart. Changing the value will recreate the chart. | 88 | | `updateMode` | `String` | The mode for the update process. | 89 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("chart.js"),require("vue-demi")):"function"==typeof define&&define.amd?define(["chart.js","vue-demi"],t):(e="undefined"!=typeof globalThis?globalThis:e||self).VueChart=t(e.Chart,e.VueDemi)}(this,(function(e,t){"use strict";var n=Object.is;let{isArray:i,isObject:o}=e.helpers;function r(e,t){if(n(e,t))return e;if(i(t)){if(i(e)){let n=e.length,i=t.length;for(let o=0,s=Math.min(n,i);oi?e.splice(i):n{n.has(o)?i.has(o)?e[o]=r(e[o],t[o]):delete e[o]:i.has(o)&&(e[o]=t[o])})),e}return t}let{clone:s}=e.helpers;var a,u,l=t.defineComponent({name:"VueChart",props:{data:Object,options:Object,type:{type:String,required:!0},updateMode:String},setup(n,{refs:i}){let o,a=t.ref(null);return t.onMounted((()=>{let u=t.isVue2?i.canvas:a.value;t.watchEffect((()=>{let{data:t,options:i,type:a,updateMode:l}=n;t=s(t),i=s(i),o=(()=>{if(o){if(o.config.type===a)return r(o.data,t),o.options=i,o.update(l),o;o.destroy()}return new e.Chart(u,{type:a,data:t,options:i})})()}))})),t.onUnmounted((()=>{o&&o.destroy()})),()=>t.h("div",{style:{height:"100%",position:"relative",width:"100%"}},[t.h("canvas",{style:{bottom:0,left:0,position:"absolute",right:0,top:0},ref:t.isVue2?"canvas":a})])}});t.isVue2&&(null===(a=globalThis.window)||void 0===a||null===(u=a.Vue)||void 0===u||u.component(l.name,l));return l})); 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@seregpie/vue-chart", 3 | "version": "1.1.0", 4 | "description": "A simple wrapper for Chart.js.", 5 | "keywords": [ 6 | "area", 7 | "bar", 8 | "canvas", 9 | "chart", 10 | "component", 11 | "diagram", 12 | "doughnut", 13 | "line", 14 | "pie", 15 | "plugin", 16 | "polar", 17 | "radar", 18 | "scatter", 19 | "vue" 20 | ], 21 | "license": "MIT", 22 | "author": "Sergej Sintschilin ", 23 | "files": [], 24 | "main": "index.js", 25 | "repository": "github:SeregPie/VueChart", 26 | "scripts": { 27 | "build": "rollup -c", 28 | "prepublishOnly": "npm run build", 29 | "serve": "rollup -c -w" 30 | }, 31 | "dependencies": { 32 | "vue-demi": "*" 33 | }, 34 | "devDependencies": { 35 | "@babel/core": "^7.14.3", 36 | "@babel/preset-env": "^7.14.2", 37 | "@rollup/plugin-babel": "^5.3.0", 38 | "eslint": "^7.27.0", 39 | "eslint-config-seregpie": "^1.0.2", 40 | "rollup": "^2.49.0", 41 | "rollup-plugin-terser": "^7.0.2" 42 | }, 43 | "peerDependencies": { 44 | "chart.js": "^3", 45 | "vue": "^2 || ^3" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import {babel} from '@rollup/plugin-babel'; 2 | import {terser} from 'rollup-plugin-terser'; 3 | 4 | import {main} from './package.json'; 5 | 6 | let globals = { 7 | 'chart.js': 'Chart', 8 | 'vue-demi': 'VueDemi', 9 | }; 10 | 11 | export default { 12 | external: Object.keys(globals), 13 | input: 'src/index.js', 14 | plugins: [ 15 | babel({ 16 | babelHelpers: 'bundled', 17 | presets: [['@babel/preset-env', { 18 | targets: 'defaults and not IE 11', 19 | }]], 20 | }), 21 | terser(), 22 | ], 23 | output: { 24 | file: main, 25 | format: 'umd', 26 | name: 'VueChart', 27 | globals, 28 | }, 29 | }; 30 | -------------------------------------------------------------------------------- /src/components/VueChart.js: -------------------------------------------------------------------------------- 1 | import { 2 | Chart, 3 | helpers, 4 | } from 'chart.js'; 5 | import { 6 | defineComponent, 7 | h, 8 | isVue2, 9 | onMounted, 10 | onUnmounted, 11 | ref, 12 | watchEffect, 13 | } from 'vue-demi'; 14 | 15 | let {clone} = helpers; 16 | 17 | import update from '../utils/update'; 18 | 19 | export default defineComponent({ 20 | name: 'VueChart', 21 | props: { 22 | data: Object, 23 | options: Object, 24 | type: { 25 | type: String, 26 | required: true, 27 | }, 28 | updateMode: String, 29 | }, 30 | setup(props, {refs}) { 31 | let chart; 32 | let canvasRef = ref(null); 33 | onMounted(() => { 34 | let canvas = isVue2 ? refs.canvas : canvasRef.value; 35 | watchEffect(() => { 36 | let { 37 | data, 38 | options, 39 | type, 40 | updateMode, 41 | } = props; 42 | data = clone(data); 43 | options = clone(options); 44 | chart = (() => { 45 | if (chart) { 46 | if (chart.config.type === type) { 47 | // todo 48 | update(chart.data, data); 49 | chart.options = options; 50 | chart.update(updateMode); 51 | return chart; 52 | } 53 | chart.destroy(); 54 | } 55 | return new Chart(canvas, {type, data, options}); 56 | })(); 57 | }); 58 | }); 59 | onUnmounted(() => { 60 | if (chart) { 61 | chart.destroy(); 62 | } 63 | }); 64 | return (() => { 65 | return h( 66 | 'div', 67 | { 68 | style: { 69 | height: '100%', 70 | position: 'relative', 71 | width: '100%', 72 | }, 73 | }, 74 | [h( 75 | 'canvas', 76 | { 77 | style: { 78 | bottom: 0, 79 | left: 0, 80 | position: 'absolute', 81 | right: 0, 82 | top: 0, 83 | }, 84 | ref: isVue2 ? 'canvas' : canvasRef, 85 | }, 86 | )], 87 | ); 88 | }); 89 | }, 90 | }); 91 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import component from './components/VueChart'; 2 | 3 | export default component; 4 | 5 | import {isVue2} from 'vue-demi'; 6 | 7 | if (isVue2) { 8 | globalThis.window?.Vue?.component(component.name, component); 9 | } 10 | -------------------------------------------------------------------------------- /src/utils/equals.js: -------------------------------------------------------------------------------- 1 | export default Object.is; 2 | -------------------------------------------------------------------------------- /src/utils/update.js: -------------------------------------------------------------------------------- 1 | import {helpers} from 'chart.js'; 2 | 3 | let { 4 | isArray, 5 | isObject, 6 | } = helpers; 7 | 8 | import equals from './equals'; 9 | 10 | function update(target, source) { 11 | if (equals(target, source)) { 12 | return target; 13 | } 14 | if (isArray(source)) { 15 | if (isArray(target)) { 16 | let targetLength = target.length; 17 | let sourceLength = source.length; 18 | for (let i = 0, ii = Math.min(targetLength, sourceLength); i < ii; i++) { 19 | target[i] = update(target[i], source[i]); 20 | } 21 | if (targetLength > sourceLength) { 22 | target.splice(sourceLength); 23 | } else 24 | if (targetLength < sourceLength) { 25 | target.push(...source.slice(targetLength)); 26 | } 27 | return target; 28 | } 29 | } else 30 | if (isObject(source)) { 31 | if (isObject(target)) { 32 | let targetKeys = new Set(Object.keys(target)); 33 | let sourceKeys = new Set(Object.keys(source)); 34 | (new Set([...targetKeys, ...sourceKeys])).forEach(key => { 35 | if (targetKeys.has(key)) { 36 | if (sourceKeys.has(key)) { 37 | target[key] = update(target[key], source[key]); 38 | } else { 39 | delete target[key]; 40 | } 41 | } else { 42 | if (sourceKeys.has(key)) { 43 | target[key] = source[key]; 44 | } 45 | } 46 | }); 47 | return target; 48 | } 49 | } 50 | return source; 51 | } 52 | 53 | export default update; 54 | --------------------------------------------------------------------------------