├── .browserslistrc ├── .gitignore ├── .prettierignore ├── README.md ├── babel.config.js ├── index.js ├── index.ts ├── jest.config.js ├── package-lock.json ├── package.json ├── prettier.config.js ├── public ├── favicon.ico └── index.html ├── src ├── App.tsx ├── App.vue ├── assets │ ├── bar.png │ ├── bubble.png │ ├── doughnut.png │ ├── line.png │ ├── logo.png │ ├── pie.png │ ├── polar.png │ ├── radar.png │ └── scatter.png ├── components │ ├── BaseChart.js │ ├── BaseChart.tsx │ ├── BaseChart.vue │ ├── Chart.vue │ ├── RandomChart.vue │ ├── example │ │ └── ChartWthLocalData.vue │ └── minxins.ts ├── main.ts ├── shims-tsx.d.ts └── shims-vue.d.ts ├── tests └── unit │ └── example.spec.ts └── tsconfig.json /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | src/assets -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue3-chart-v2 2 | vue3-chart-v2 is a wrapper for [Chart.js](https://github.com/chartjs/Chart.js) in Vue 3. You can easily create reuseable chart components. Inspired by vue-chartjs 3 | 4 | [![npm](https://img.shields.io/badge/npm%20package-0.8.3-blue)](https://www.npmjs.com/package/vue3-chart-v2) 5 | 6 | ## Looking for the documentation? 7 | Head over here ==> [vue3-chart-v2](https://vue3-chart-v2.netlify.app/) 8 | 9 | ## Prerequisite 10 | - Vue 3 is required 11 | 12 | ## Install 13 | 14 | - **npm** `npm install vue3-chart-v2 chart.js --save` 15 | - **yarn** `yarn add vue3-chart-v2 chart.js` 16 | 17 | ## How to use 18 | 19 | You need to import the component and then either use `extends` or `mixins` and add it. 20 | 21 | You can import each module individual. 22 | 23 | ```js 24 | import { Bar } from 'vue3-chart-v2' 25 | ``` 26 | 27 | Just create your own component. 28 | 29 | // MonthlyChart.vue 30 | ```vue 31 | 53 | ``` 54 | 55 | Then simply import and use your own extended component and use it like a normal vue component 56 | 57 | ```vue 58 | 61 | 62 | 73 | ``` 74 | ## Another Example with options 75 | You can overwrite the default chart options as props. Just pass the options object as a second parameter to the render method 76 | 77 | ```js 78 | // MonthlyChart.vue 79 | import { defineComponent } from 'vue' 80 | import { Line } from 'vue3-chart-v2' 81 | 82 | export default defineComponent({ 83 | name: 'MonthlyChart', 84 | extends: Line, 85 | props: { 86 | chartData: { 87 | type: Object, 88 | required: true 89 | }, 90 | chartOptions: { 91 | type: Object, 92 | required: false 93 | }, 94 | }, 95 | mounted () { 96 | this.renderChart(this.chartData, this.chartOptions) 97 | } 98 | }) 99 | ``` 100 | 101 | Then simply use it in your vue app 102 | 103 | ```vue 104 | 107 | 108 | 119 | ``` 120 | ## Available Charts 121 | 122 | ### Bar Chart 123 | 124 | ![Bar](src/assets/bar.png) 125 | 126 | ### Line Chart 127 | 128 | ![Line](src/assets/line.png) 129 | 130 | ### Doughnut 131 | 132 | ![Doughnut](src/assets/doughnut.png) 133 | 134 | ### Pie 135 | 136 | ![Pie](src/assets/pie.png) 137 | 138 | ### Radar 139 | 140 | ![Pie](src/assets/radar.png) 141 | 142 | ### Polar Area 143 | 144 | ![Pie](src/assets/polar.png) 145 | 146 | ### Bubble 147 | 148 | ![Bubble](src/assets/bubble.png) 149 | 150 | ### Scatter 151 | 152 | ![Scatter](src/assets/scatter.png) 153 | 154 | ## Build Setup 155 | 156 | ``` bash 157 | # install dependencies 158 | npm install 159 | 160 | # serve with hot reload at localhost:8080 161 | npm run dev 162 | 163 | # run unit tests 164 | npm run test:unit 165 | ``` 166 | 167 | ## Contributing 168 | 169 | 1. Fork it (https://github.com/vutran6853/vue3-chart-v2/fork ) 170 | 2. Create your feature branch (`git checkout -b my-new-feature`) 171 | 3. Commit your changes (`git commit -m 'Add some feature'`) 172 | 4. Push to the branch (`git push origin my-new-feature`) 173 | 5. Create a new Pull Request -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import { 2 | Bar, 3 | Bubble, 4 | Doughnut, 5 | HorizontalBar, 6 | Line, 7 | Pie, 8 | PolarArea, 9 | Radar, 10 | Scatter, 11 | generateChart 12 | } from './src/components/BaseChart' 13 | 14 | export { 15 | Bar, 16 | Bubble, 17 | Doughnut, 18 | HorizontalBar, 19 | Line, 20 | Pie, 21 | PolarArea, 22 | Radar, 23 | Scatter, 24 | generateChart 25 | // renderChart 26 | } 27 | 28 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Bar, 3 | Bubble, 4 | Doughnut, 5 | HorizontalBar, 6 | Line, 7 | Pie, 8 | PolarArea, 9 | Radar, 10 | Scatter, 11 | generateChart 12 | } from './src/components/BaseChart' 13 | 14 | export { 15 | Bar, 16 | Bubble, 17 | Doughnut, 18 | HorizontalBar, 19 | Line, 20 | Pie, 21 | PolarArea, 22 | Radar, 23 | Scatter, 24 | generateChart 25 | // renderChart 26 | } 27 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: '@vue/cli-plugin-unit-jest/presets/typescript-and-babel', 3 | transform: { 4 | '^.+\\.vue$': 'vue-jest' 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue3-chart-v2", 3 | "version": "0.8.3", 4 | "private": false, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "test:unit": "vue-cli-service test:unit", 9 | "prettier:src": "npx prettier -w src/**" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/vutran6853/vue3-chart-v2" 14 | }, 15 | "keywords": [ 16 | "ChartJs", 17 | "Vue3-chart", 18 | "wrapper", 19 | "Vue 3" 20 | ], 21 | "dependencies": { 22 | "@types/chart.js": "^2.9.29", 23 | "chart.js": "^2.9.4", 24 | "core-js": "^3.6.5", 25 | "prettier": "^2.2.1", 26 | "vue": "^3.0.0" 27 | }, 28 | "devDependencies": { 29 | "@types/jest": "^24.0.19", 30 | "@vue/cli-plugin-babel": "~4.5.0", 31 | "@vue/cli-plugin-typescript": "~4.5.0", 32 | "@vue/cli-plugin-unit-jest": "~4.5.0", 33 | "@vue/cli-service": "~4.5.0", 34 | "@vue/compiler-sfc": "^3.0.0", 35 | "@vue/test-utils": "^2.0.0-0", 36 | "typescript": "~3.9.3", 37 | "vue-jest": "^5.0.0-0" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | const config = { 2 | tabWidth: 2, 3 | semi: false, 4 | printWidth: 120, 5 | bracketSpacing: true, 6 | jsxBracketSameLine: true, 7 | trailingComma: 'none', 8 | arrowParens: 'always', 9 | singleQuote: true 10 | } 11 | 12 | module.exports = config 13 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vutran6853/vue3-chart-v2/8fcb80798815a1043975bd1b08c0709530a4ca6e/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent, VNode } from 'vue' 2 | // import { Pie, Doughnut, Bubble, Line } from './components/BaseChart' 3 | import Chart from './components/Chart.vue' 4 | // import RandomChart from './components/RandomChart.vue' 5 | 6 | interface IAppState { 7 | chartData: { 8 | labels?: string[] 9 | datasets?: [ 10 | { 11 | label?: string 12 | backgroundColor?: string 13 | data?: number[] 14 | } 15 | ] 16 | } 17 | dataCollection: {} 18 | chartOptions: { 19 | responsive?: boolean 20 | maintainAspectRatio?: boolean 21 | height?: number 22 | intersect?: boolean 23 | hover?: any 24 | tooltips?: any 25 | layout?: any 26 | pointHoverRadius?: any 27 | } 28 | height: number 29 | } 30 | 31 | export default defineComponent({ 32 | name: 'App', 33 | data(): IAppState { 34 | return { 35 | chartData: { 36 | labels: ['January', 'February'], 37 | datasets: [ 38 | { 39 | label: 'Data One', 40 | backgroundColor: '#f87979', 41 | data: [40, 20] 42 | } 43 | ] 44 | }, 45 | dataCollection: {}, 46 | chartOptions: { 47 | responsive: true, 48 | maintainAspectRatio: false 49 | }, 50 | height: 500 51 | } 52 | }, 53 | beforeMount() { 54 | this.fillData() 55 | }, 56 | methods: { 57 | fillData() { 58 | this.dataCollection = { 59 | labels: ['Red', 'Blue'], 60 | datasets: [ 61 | { 62 | label: 'Data One', 63 | data: [this.getRandomInt(), this.getRandomInt()], 64 | backgroundColor: ['red', 'blue'], 65 | borderColor: ['red', 'blue'], 66 | borderWidth: 1 67 | } 68 | ] 69 | // labels: [this.getRandomInt(), this.getRandomInt()], 70 | // datasets: [ 71 | // { 72 | // label: 'Data One', 73 | // backgroundColor: '#f87979', 74 | // data: [this.getRandomInt(), this.getRandomInt()] 75 | // }, { 76 | // label: 'Data One', 77 | // backgroundColor: '#f87979', 78 | // data: [this.getRandomInt(), this.getRandomInt()] 79 | // } 80 | // ] 81 | } 82 | }, 83 | getRandomInt() { 84 | return Math.floor(Math.random() * (20 - 5)) 85 | }, 86 | increase() { 87 | this.height += 10 88 | } 89 | }, 90 | computed: { 91 | myStyles(): { height?: string; position?: string; border?: string } { 92 | return { 93 | height: `${this.height}px`, 94 | border: `2px solid red`, 95 | position: 'relative' 96 | } 97 | } 98 | }, 99 | render(): VNode { 100 | return ( 101 |
102 | {/*

app tsx

*/} 103 | {/* */} 104 | 105 | 106 | 107 |
108 | ) 109 | } 110 | }) 111 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 106 | -------------------------------------------------------------------------------- /src/assets/bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vutran6853/vue3-chart-v2/8fcb80798815a1043975bd1b08c0709530a4ca6e/src/assets/bar.png -------------------------------------------------------------------------------- /src/assets/bubble.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vutran6853/vue3-chart-v2/8fcb80798815a1043975bd1b08c0709530a4ca6e/src/assets/bubble.png -------------------------------------------------------------------------------- /src/assets/doughnut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vutran6853/vue3-chart-v2/8fcb80798815a1043975bd1b08c0709530a4ca6e/src/assets/doughnut.png -------------------------------------------------------------------------------- /src/assets/line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vutran6853/vue3-chart-v2/8fcb80798815a1043975bd1b08c0709530a4ca6e/src/assets/line.png -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vutran6853/vue3-chart-v2/8fcb80798815a1043975bd1b08c0709530a4ca6e/src/assets/logo.png -------------------------------------------------------------------------------- /src/assets/pie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vutran6853/vue3-chart-v2/8fcb80798815a1043975bd1b08c0709530a4ca6e/src/assets/pie.png -------------------------------------------------------------------------------- /src/assets/polar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vutran6853/vue3-chart-v2/8fcb80798815a1043975bd1b08c0709530a4ca6e/src/assets/polar.png -------------------------------------------------------------------------------- /src/assets/radar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vutran6853/vue3-chart-v2/8fcb80798815a1043975bd1b08c0709530a4ca6e/src/assets/radar.png -------------------------------------------------------------------------------- /src/assets/scatter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vutran6853/vue3-chart-v2/8fcb80798815a1043975bd1b08c0709530a4ca6e/src/assets/scatter.png -------------------------------------------------------------------------------- /src/components/BaseChart.js: -------------------------------------------------------------------------------- 1 | import Chart from 'chart.js' 2 | import { defineComponent, h, reactive } from 'vue' 3 | 4 | function useChartInfo() { 5 | const state = reactive({ 6 | myName: '', 7 | userData: {}, 8 | userOptions: {} 9 | }) 10 | 11 | function setChartData(payload) { 12 | state.userData = payload 13 | } 14 | 15 | function setChartOption(payload) { 16 | state.userOptions = payload 17 | } 18 | 19 | return { 20 | state, 21 | setChartData, 22 | setChartOption 23 | } 24 | } 25 | 26 | /** 27 | * 28 | * @param chartsId string 29 | * @param chartsType string 30 | */ 31 | function generateChart(chartsId, chartsType) { 32 | let { state, setChartData, setChartOption } = useChartInfo() 33 | 34 | return defineComponent({ 35 | name: 'BaseChart', 36 | props: { 37 | chartId: { 38 | type: String, 39 | required: false 40 | }, 41 | chartType: { 42 | type: String, 43 | required: false 44 | }, 45 | width: { 46 | type: Number, 47 | required: false, 48 | default: 400 49 | }, 50 | height: { 51 | type: Number, 52 | required: false, 53 | default: 400 54 | }, 55 | cssClasses: { 56 | type: String, 57 | required: false, 58 | default: '' 59 | }, 60 | styles: { 61 | type: Object, 62 | required: false 63 | } 64 | }, 65 | data() { 66 | return { 67 | state: { 68 | chartObj: null 69 | } 70 | } 71 | }, 72 | // emits: ['chart:update'], 73 | 74 | beforeUnmount() { 75 | if (this.state.chartObj) { 76 | this.state.chartObj.destroy() 77 | } 78 | }, 79 | methods: { 80 | renderChart(userData, userOptions) { 81 | setChartData(userData) 82 | setChartOption(userOptions) 83 | 84 | if (this.state.chartObj) { 85 | // this.state.chartObj.destroy() 86 | } 87 | // if (!this.$refs.canvas) { 88 | // throw new Error('Please remove the tags from your chart component. See https://vue-chartjs.org/guide/#vue-single-file-components') 89 | // } 90 | 91 | // REMOVE OLD DATA FIRST BEFORE UPDATE. 92 | if (this.state.chartObj != null && this.state.chartObj.data != null) { this.state.chartObj.data.datasets.pop() } 93 | 94 | let ctx = this.$refs.canvas.getContext('2d') 95 | this.state.chartObj = new Chart(ctx, { 96 | type: chartsType, 97 | data: userData, 98 | options: userOptions 99 | // plugins: this.$data._plugins 100 | }) 101 | } 102 | }, 103 | beforeMount() { 104 | if (document.getElementById(chartsId)) { 105 | let ctx = document.getElementById(chartsId).getContext('2d') 106 | this.state.chartObj = new Chart(ctx, { 107 | type: chartsType, 108 | data: { 109 | datasets: [ 110 | { 111 | data: [1, 2, 3, 4], 112 | backgroundColor: ['Red', 'Yellow', 'Blue', 'Green'] 113 | } 114 | ], 115 | // These labels appear in the legend and in the tooltips when hovering different arcs 116 | labels: ['Red', 'Yellow', 'Blue', 'Green'] 117 | }, 118 | options: { 119 | responsive: false 120 | } 121 | // options: this.options, 122 | // plugins: this.$data._plugins 123 | }) 124 | } 125 | }, 126 | computed: { 127 | currentChartData() { 128 | return state.userData 129 | }, 130 | currentChartOption() { 131 | return state.userOptions 132 | } 133 | }, 134 | watch: { 135 | chartData(prevState, newState) { 136 | if (prevState !== newState) { 137 | this.renderChart(newState, this.currentChartOption) 138 | } 139 | } 140 | }, 141 | render() { 142 | // return h('canvas', { 143 | // ref: 'canvas', 144 | // id: this.chartId, 145 | // width: this.width, 146 | // height: this.height 147 | // }) 148 | return h('div', { style: this.styles, class: this.cssClasses }, [ 149 | h('canvas', { 150 | ref: 'canvas', 151 | id: this.chartId, 152 | width: this.width, 153 | height: this.height 154 | }) 155 | ]) 156 | } 157 | }) 158 | } 159 | 160 | const Bar = generateChart('bar-chart', 'bar') 161 | const Bubble = generateChart('bubble-chart', 'bubble') 162 | const Doughnut = generateChart('doughnut-chart', 'doughnut') 163 | const HorizontalBar = generateChart('horizontalbar-chart', 'horizontalBar') 164 | const Line = generateChart('line-chart', 'line') 165 | const Pie = generateChart('pie-chart', 'pie') 166 | const PolarArea = generateChart('polar-chart', 'polarArea') 167 | const Radar = generateChart('radar-chart', 'radar') 168 | const Scatter = generateChart('scatter-chart', 'scatter') 169 | 170 | export { 171 | Bar, 172 | Bubble, 173 | Doughnut, 174 | HorizontalBar, 175 | Line, 176 | Pie, 177 | PolarArea, 178 | Radar, 179 | Scatter, 180 | generateChart 181 | // renderChart 182 | } 183 | -------------------------------------------------------------------------------- /src/components/BaseChart.tsx: -------------------------------------------------------------------------------- 1 | import Chart from 'chart.js' 2 | import { defineComponent, h, reactive } from 'vue' 3 | 4 | // interface IBaseChart { 5 | // width: number, 6 | // height: number, 7 | // chartObj: any, 8 | // chartType: any 9 | // } 10 | 11 | interface IBaseChart { 12 | state: { 13 | chartObj?: Chart | null 14 | } 15 | } 16 | 17 | type IBaseChartProps = { 18 | chartId: string 19 | chartType: string 20 | width: number 21 | height: number 22 | cssClasses: string 23 | styles: string 24 | plugins: any[] 25 | } 26 | 27 | interface IState { 28 | myName: string 29 | userData: {} 30 | userOptions: {} 31 | } 32 | 33 | function useChartInfo() { 34 | const state: IState = reactive({ 35 | myName: '', 36 | userData: {}, 37 | userOptions: {} 38 | }) 39 | 40 | function setChartData(payload: {}) { 41 | state.userData = payload 42 | } 43 | 44 | function setChartOption(payload: {}) { 45 | state.userOptions = payload 46 | } 47 | 48 | return { 49 | state, 50 | setChartData, 51 | setChartOption 52 | } 53 | } 54 | 55 | /** 56 | * 57 | * @param chartsId string 58 | * @param chartsType string 59 | */ 60 | function generateChart(chartsId: string, chartsType: string) { 61 | let { state, setChartData, setChartOption } = useChartInfo() 62 | 63 | return defineComponent({ 64 | name: 'BaseChart', 65 | props: { 66 | chartId: { 67 | type: String, 68 | required: false 69 | }, 70 | chartType: { 71 | type: String, 72 | required: false 73 | }, 74 | width: { 75 | type: Number, 76 | required: false 77 | // default: 400 78 | }, 79 | height: { 80 | type: Number, 81 | required: false 82 | // default: 400 83 | }, 84 | cssClasses: { 85 | type: String, 86 | required: false, 87 | default: '' 88 | }, 89 | styles: { 90 | type: Object, 91 | required: false 92 | } 93 | }, 94 | data(): IBaseChart { 95 | return { 96 | state: { 97 | chartObj: null 98 | } 99 | } 100 | }, 101 | // emits: ['chart:update'], 102 | 103 | beforeUnmount() { 104 | if (this.state.chartObj) { 105 | this.state.chartObj.destroy() 106 | } 107 | }, 108 | methods: { 109 | renderChart(userData: any, userOptions: any) { 110 | setChartData(userData) 111 | setChartOption(userOptions) 112 | if (this.state.chartObj) { 113 | // this.state.chartObj.destroy() 114 | // setChartData({}) 115 | // setChartOption({}) 116 | } 117 | // if (!this.$refs.canvas) { 118 | // throw new Error('Please remove the tags from your chart component. See https://vue-chartjs.org/guide/#vue-single-file-components') 119 | // } 120 | 121 | // REMOVE OLD DATA FIRST BEFORE UPDATE. 122 | if (this.state.chartObj != null && this.state.chartObj.data != null) { 123 | if (this.state.chartObj.data.datasets) { 124 | this.state.chartObj.data.datasets.pop() 125 | } 126 | } 127 | 128 | let ctx = (this as any).$refs.canvas.getContext('2d') 129 | this.state.chartObj = new Chart(ctx, { 130 | type: chartsType, 131 | data: userData, 132 | options: userOptions 133 | // plugins: this.$data._plugins 134 | }) 135 | this.state.chartObj.update() 136 | } 137 | }, 138 | beforeMount() { 139 | if ((document as any).getElementById(chartsId)) { 140 | let ctx = (document as any).getElementById(chartsId).getContext('2d') 141 | this.state.chartObj = new Chart(ctx, { 142 | type: chartsType, 143 | data: { 144 | datasets: [ 145 | { 146 | data: [1, 2, 3, 4], 147 | backgroundColor: ['Red', 'Yellow', 'Blue', 'Green'] 148 | } 149 | ], 150 | // These labels appear in the legend and in the tooltips when hovering different arcs 151 | labels: ['Red', 'Yellow', 'Blue', 'Green'] 152 | }, 153 | options: { 154 | responsive: false 155 | } 156 | // options: this.options, 157 | // plugins: this.$data._plugins 158 | }) 159 | this.state.chartObj.update() 160 | } 161 | }, 162 | computed: { 163 | currentChartData(): any { 164 | return state.userData 165 | }, 166 | currentChartOption(): any { 167 | return state.userOptions 168 | } 169 | }, 170 | watch: { 171 | chartData(prevState, newState) { 172 | if (prevState !== newState) { 173 | this.renderChart(newState, this.currentChartOption) 174 | } 175 | } 176 | }, 177 | render() { 178 | return ( 179 |
180 | 181 |
182 | ) 183 | } 184 | }) 185 | } 186 | 187 | const Bar = generateChart('bar-chart', 'bar') 188 | const Bubble = generateChart('bubble-chart', 'bubble') 189 | const Doughnut = generateChart('doughnut-chart', 'doughnut') 190 | const HorizontalBar = generateChart('horizontalbar-chart', 'horizontalBar') 191 | const Line = generateChart('line-chart', 'line') 192 | const Pie = generateChart('pie-chart', 'pie') 193 | const PolarArea = generateChart('polar-chart', 'polarArea') 194 | const Radar = generateChart('radar-chart', 'radar') 195 | const Scatter = generateChart('scatter-chart', 'scatter') 196 | 197 | export { 198 | Bar, 199 | Bubble, 200 | Doughnut, 201 | HorizontalBar, 202 | Line, 203 | Pie, 204 | PolarArea, 205 | Radar, 206 | Scatter, 207 | generateChart 208 | // renderChart 209 | } 210 | -------------------------------------------------------------------------------- /src/components/BaseChart.vue: -------------------------------------------------------------------------------- 1 | 195 | -------------------------------------------------------------------------------- /src/components/Chart.vue: -------------------------------------------------------------------------------- 1 | 44 | -------------------------------------------------------------------------------- /src/components/RandomChart.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 50 | 51 | 57 | -------------------------------------------------------------------------------- /src/components/example/ChartWthLocalData.vue: -------------------------------------------------------------------------------- 1 | 32 | -------------------------------------------------------------------------------- /src/components/minxins.ts: -------------------------------------------------------------------------------- 1 | import { defineComponent } from 'vue' 2 | 3 | function dataHandler(this: { chartData: (newData: any, oldData: any) => void }, newData: any, oldData: any) { 4 | if (oldData) { 5 | let chart = (this as any).chartData 6 | // Get new and old DataSet Labels 7 | let newDatasetLabels = newData.datasets.map((dataset: any) => { 8 | return dataset.label 9 | }) 10 | 11 | let oldDatasetLabels = oldData.datasets.map((dataset: any) => { 12 | return dataset.label 13 | }) 14 | 15 | // Stringify 'em for easier compare 16 | const oldLabels = JSON.stringify(oldDatasetLabels) 17 | const newLabels = JSON.stringify(newDatasetLabels) 18 | 19 | // Check if Labels are equal and if dataset length is equal 20 | if (newLabels === oldLabels && oldData.datasets.length === newData.datasets.length) { 21 | newData.datasets.forEach((dataset: any, index: any) => { 22 | // Get new and old dataset keys 23 | const oldDatasetKeys = Object.keys(oldData.datasets[index]) 24 | const newDatasetKeys = Object.keys(dataset) 25 | 26 | // Get keys that aren't present in the new data 27 | const deletionKeys = oldDatasetKeys.filter((key) => { 28 | return key !== '_meta' && newDatasetKeys.indexOf(key) === -1 29 | }) 30 | 31 | // Remove outdated key-value pairs 32 | deletionKeys.forEach((deletionKey) => { 33 | delete chart.data.datasets[index][deletionKey] 34 | }) 35 | 36 | // Update attributes individually to avoid re-rendering the entire chart 37 | for (const attribute in dataset) { 38 | if (dataset.hasOwnProperty(attribute)) { 39 | chart.datasets[index][attribute] = dataset[attribute] 40 | } 41 | } 42 | }) 43 | 44 | if (newData.hasOwnProperty('labels')) { 45 | chart.labels = newData.labels 46 | // (this as any).$emit('labels:update') // TODO: FIX 47 | } 48 | if (newData.hasOwnProperty('xLabels')) { 49 | chart.xLabels = newData.xLabels(this as any).$emit('xlabels:update') // TODO: FIX 50 | } 51 | if (newData.hasOwnProperty('yLabels')) { 52 | chart.yLabels = newData.yLabels(this as any).$emit('ylabels:update') // TODO: FIX 53 | } 54 | // console.log('(this as any).$root(this as any).$root', (this as any).$nextTick()) 55 | // (this as any).$root.updated 56 | console.log('(this as any).$.data.state.localChartData', (this as any).$.data.state.chartObj) 57 | console.log('this.$', (this as any).$.data.state.chartObj) 58 | // (this as any).$.data.state.localChartData.clear() 59 | // console.log('this', (this as any)) 60 | // (this as any).$emit('chart:update') // TODO: FIX 61 | } else { 62 | if (chart) { 63 | chart 64 | .destroy()(this as any) 65 | .$emit('chart:destroy') 66 | } 67 | ;(this as any) 68 | .renderChart( 69 | this.chartData, 70 | (this as any).options 71 | )(this as any) 72 | .$emit('chart:render') 73 | } 74 | } else { 75 | if ((this as any).chartData) { 76 | ;(this as any).chartData 77 | .destroy()(this as any) 78 | .$emit('chart:destroy') 79 | } 80 | ;(this as any) 81 | .renderChart( 82 | this.chartData, 83 | (this as any).options 84 | )(this as any) 85 | .$emit('chart:render') 86 | } 87 | } 88 | 89 | const reactiveData = defineComponent({ 90 | data() { 91 | return { 92 | chartData: null 93 | } 94 | }, 95 | watch: { 96 | chartData: dataHandler 97 | } 98 | }) 99 | 100 | const reactiveProp = defineComponent({ 101 | props: { 102 | chartData: { 103 | type: Object, 104 | required: false, 105 | default: () => {} 106 | } 107 | }, 108 | computed: { 109 | getme(): any { 110 | return this.chartData 111 | } 112 | } 113 | // watch: { 114 | // chartData: dataHandler 115 | // }, 116 | // watch: { 117 | // 'chartData' (prevState, newState) { 118 | // if (prevState !== newState) { 119 | // console.log(true) 120 | // } else { 121 | // console.log(false) 122 | // } 123 | // } 124 | // } 125 | }) 126 | 127 | export { reactiveData, reactiveProp } 128 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App' 3 | 4 | const app = createApp(App) 5 | 6 | app.mount('#app') 7 | -------------------------------------------------------------------------------- /src/shims-tsx.d.ts: -------------------------------------------------------------------------------- 1 | import Vue, { VNode } from 'vue' 2 | 3 | declare global { 4 | namespace JSX { 5 | // tslint:disable no-empty-interface 6 | interface Element extends VNode {} 7 | // tslint:disable no-empty-interface 8 | interface ElementClass extends Vue {} 9 | interface IntrinsicElements { 10 | [elem: string]: any 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | declare module '*.vue' { 3 | import type { DefineComponent } from 'vue' 4 | const component: DefineComponent<{}, {}, any> 5 | export default component 6 | } 7 | -------------------------------------------------------------------------------- /tests/unit/example.spec.ts: -------------------------------------------------------------------------------- 1 | import { shallowMount } from '@vue/test-utils' 2 | import HelloWorld from '@/components/HelloWorld.vue' 3 | 4 | describe('HelloWorld.vue', () => { 5 | it('renders props.msg when passed', () => { 6 | const msg = 'new message' 7 | const wrapper = shallowMount(HelloWorld, { 8 | props: { msg } 9 | }) 10 | expect(wrapper.text()).toMatch(msg) 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "strict": true, 6 | "jsx": "preserve", 7 | "importHelpers": true, 8 | "moduleResolution": "node", 9 | "skipLibCheck": true, 10 | "esModuleInterop": true, 11 | "allowSyntheticDefaultImports": true, 12 | "sourceMap": true, 13 | "baseUrl": ".", 14 | "types": [ 15 | "webpack-env", 16 | "jest" 17 | ], 18 | "paths": { 19 | "@/*": [ 20 | "src/*" 21 | ] 22 | }, 23 | "lib": [ 24 | "esnext", 25 | "dom", 26 | "dom.iterable", 27 | "scripthost" 28 | ] 29 | }, 30 | "include": [ 31 | "src/**/*.ts", 32 | "src/**/*.tsx", 33 | "src/**/*.vue", 34 | "tests/**/*.ts", 35 | "tests/**/*.tsx" 36 | ], 37 | "exclude": [ 38 | "node_modules" 39 | ] 40 | } 41 | --------------------------------------------------------------------------------