├── .github └── workflows │ └── run-tests.yml ├── .gitignore ├── .prettierrc.json ├── LICENSE ├── README.md ├── dist ├── index.d.ts ├── types │ ├── Vue3ChartJs.vue.d.ts │ ├── Vue3ChartJs.vue.d.ts.map │ ├── includes.d.ts │ ├── includes.d.ts.map │ ├── main.d.ts │ └── main.d.ts.map ├── vue3-chartjs.es.js └── vue3-chartjs.umd.js ├── eslint.config.mjs ├── index.html ├── jest.config.js ├── lib ├── Vue3ChartJs.vue ├── includes.ts ├── main.ts └── shims-vue.d.ts ├── package.json ├── src ├── App.vue ├── charts.js ├── direct.html └── main.js ├── test ├── chart.props.ts └── wrapper.test.ts ├── tsconfig.json ├── vite.config.js └── yarn.lock /.github/workflows/run-tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ '*' ] 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-22.04 12 | strategy: 13 | matrix: 14 | node-version: [ 20 ] 15 | steps: 16 | - uses: actions/checkout@v4 17 | 18 | - name: Use Node.js ${{ matrix.node-version }} 19 | uses: actions/setup-node@v4 20 | with: 21 | node-version: ${{ matrix.node-version }} 22 | 23 | - name: Install 24 | run: npm install --legacy-peer-deps 25 | 26 | - name: Run tests 27 | run: npm test 28 | 29 | - name: Upload coverage to Coveralls 30 | run: cat ./coverage/lcov.info | npx coveralls 31 | env: 32 | COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | .DS_Store 4 | dist-ssr 5 | *.local 6 | 7 | # Log files 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | pnpm-debug.log* 12 | 13 | # Editor directories and files 14 | .idea 15 | .vscode 16 | *.suo 17 | *.ntvs* 18 | *.njsproj 19 | *.sln 20 | *.sw? -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/J-T-McC/vue3-chartjs/35765dbaaf1cb51ab81004bb0d821ae8aea3ba34/.prettierrc.json -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Tyson McCarney 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 | # Vue3 ChartJS Wrapper 2 | 3 | [![Coverage Status](https://coveralls.io/repos/github/J-T-McC/vue3-chartjs/badge.svg?branch=main)](https://coveralls.io/github/J-T-McC/vue3-chartjs?branch=main) 4 | [![Tests](https://github.com/J-T-McC/vue3-chartjs/actions/workflows/run-tests.yml/badge.svg)](https://github.com/J-T-McC/vue3-chartjs/actions/workflows/run-tests.yml) 5 | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://github.com/J-T-McC/vue3-chartjs/pulls) 6 | ![npm](https://img.shields.io/npm/dt/@j-t-mcc/vue3-chartjs) 7 | 8 | Basic [ChartJS 4](https://www.chartjs.org/) wrapper for [Vue3](https://v3.vuejs.org/) 9 | 10 | For ChartJS 2, see [v0.3.0](https://github.com/J-T-McC/vue3-chartjs/tree/0.3.0) 11 | 12 | For ChartJS 3.1, see [v1.3.0](https://github.com/J-T-McC/vue3-chartjs/tree/v1.3.0) 13 | 14 | ## Requirements 15 | 16 | - Vue 3 17 | - ChartJS 4 18 | 19 | ## Installation 20 | 21 | ```shell script 22 | yarn add chart.js @j-t-mcc/vue3-chartjs 23 | 24 | npm install chart.js @j-t-mcc/vue3-chartjs 25 | ``` 26 | 27 | ## Configuration 28 | 29 | Component props use the same structure as ChartJS arguments and are passed as-is to the instance of ChartJS. 30 | 31 | ChartJS charts are responsive by default. If you wish to have a fixed sized chart, you can set the optional `height` and `width` properties, paired with setting responsive to `false` in the `options` property. 32 | 33 | ```js 34 | props: { 35 | type: { 36 | type: String, 37 | required: true 38 | }, 39 | height: { 40 | type: Number, 41 | required: false, 42 | default: null 43 | }, 44 | width: { 45 | type: Number, 46 | required: false, 47 | default: null 48 | }, 49 | data: { 50 | type: Object, 51 | required: true 52 | }, 53 | options: { 54 | type: Object, 55 | default: () => ({}) 56 | }, 57 | plugins: { 58 | type: Array, 59 | default: () => [] 60 | } 61 | } 62 | ``` 63 | 64 | ## Sandbox Examples 65 | 66 | * [Pie Chart](https://codesandbox.io/s/pie-chart-848by?file=/src/App.vue) 67 | * [Doughnut Chart](https://codesandbox.io/s/doughnut-chart-g7il4?file=/src/App.vue) 68 | * [Bar Chart](https://codesandbox.io/s/bar-chart-kog87?file=/src/App.vue) 69 | * [Radar Chart](https://codesandbox.io/s/radar-chart-j2dyp?file=/src/App.vue) 70 | * [Line Chart with Plugins](https://codesandbox.io/s/plugin-example-o4y3q?file=/src/App.vue) 71 | * [Events & Exports](https://codesandbox.io/s/events-and-exports-q5g9k?file=/src/App.vue) 72 | 73 | View the [ChartJS Docs](https://www.chartjs.org/docs/latest/samples/bar/vertical.html) for more examples. 74 | 75 | ## Events 76 | 77 | A default event hook plugin is injected into each chart object and emits the following events: 78 | [ChartJS events](https://www.chartjs.org/docs/latest/developers/plugins.html#plugin-core-api) 79 | 80 | Event listeners are converted to camelcase in Vue 3. Events marked as "cancellable" in the ChartJS documentation can be " 81 | canceled" by calling the preventDefault method on the event parameter available in your event function. 82 | 83 | ## Methods 84 | 85 | This library only implements a few ChartJS methods for some common interactions and are available by reference: 86 | 87 | ```javascript 88 | chartRef.value.update(animationSpeed = 750) 89 | chartRef.value.resize() 90 | chartRef.value.destroy() 91 | ``` 92 | 93 | If you require additional access to ChartJS functionality, you can interact directly with the ChartJS object via the 94 | chartJSState attribute by reference: 95 | 96 | ```javascript 97 | const base64Image = chartRef.value.chartJSState.chart.toBase64Image() 98 | ``` 99 | 100 | See the [ChartJS Docs](https://www.chartjs.org/docs/latest/developers/api.html) for more 101 | 102 | ## Examples 103 | 104 | Below are some basic chart examples to get started. 105 | 106 | ### Simple Chart 107 | 108 | ```vue 109 | 119 | 120 | 162 | ``` 163 | 164 | ### Updating chart 165 | 166 | Here is an example of updating the data, labels and title in a chart. 167 | 168 | See the [ChartJS docs](https://www.chartjs.org/docs/latest/developers/updates.html) for more details on updating charts. 169 | 170 | ```vue 171 | 184 | 185 | 248 | 249 | ``` 250 | 251 | ### Exporting Chart as PNG 252 | 253 | ```vue 254 | 265 | 266 | 312 | ``` 313 | 314 | ### Adding a plugin 315 | 316 | ChartJS has two different types of plugins: Global & Inline. 317 | 318 | Inline plugins can be passed directly to the chart via the plugins array prop and will be available for that chart only. 319 | 320 | Global plugins require registration with ChartJS and will be available for all charts. Some plugins must be registered. 321 | 322 | Here is an example of adding a global plugin, in this case [`chartjs-plugin-zoom`](https://github.com/chartjs/chartjs-plugin-zoom). 323 | 324 | Global plugins can be registered one of two ways: 325 | 326 | #### Via Component Install 327 | 328 | ```javascript 329 | import Vue3ChartJs from '@j-t-mcc/vue3-chartjs' 330 | import zoomPlugin from 'chartjs-plugin-zoom' 331 | 332 | const Vue = createApp(App) 333 | 334 | Vue.use(Vue3ChartJs, { 335 | plugins: [ 336 | zoomPlugin 337 | ] 338 | }) 339 | 340 | Vue.mount('#app') 341 | ```` 342 | 343 | #### Via Helper Function 344 | 345 | ```javascript 346 | import Vue3ChartJs from '../lib/main' 347 | import zoomPlugin from 'chartjs-plugin-zoom' 348 | 349 | Vue3ChartJs.registerGlobalPlugins([zoomPlugin]) 350 | ``` 351 | 352 | Example usage with locally imported chart component: 353 | 354 | ```vue 355 | 365 | 366 | 428 | ``` 429 | 430 | ## Demo 431 | 432 | For a demo, Clone this repository and run: 433 | 434 | ```shell script 435 | yarn install 436 | 437 | yarn dev 438 | ``` 439 | 440 | ## License 441 | 442 | MIT 443 | -------------------------------------------------------------------------------- /dist/index.d.ts: -------------------------------------------------------------------------------- 1 | import { AnyObject } from 'chart.js/dist/types/basic'; 2 | import { Chart } from 'chart.js'; 3 | import { ChartData } from 'chart.js/dist/types'; 4 | import { ChartOptions } from 'chart.js/dist/types'; 5 | import { ChartType } from 'chart.js/dist/types'; 6 | import { ChartTypeRegistry } from 'chart.js'; 7 | import { ComponentOptionsMixin } from 'vue'; 8 | import { ComponentProvideOptions } from 'vue'; 9 | import { CoreChartOptions } from 'chart.js'; 10 | import { DatasetChartOptions } from 'chart.js'; 11 | import { _DeepPartialObject } from 'chart.js/dist/types/utils'; 12 | import { DefineComponent } from 'vue'; 13 | import { ElementChartOptions } from 'chart.js'; 14 | import { Plugin as Plugin_2 } from 'chart.js'; 15 | import { PluginChartOptions } from 'chart.js'; 16 | import { PublicProps } from 'vue'; 17 | import { ScaleChartOptions } from 'chart.js'; 18 | 19 | declare type __VLS_Props = { 20 | type: ChartType; 21 | height?: number; 22 | width?: number; 23 | data: ChartData; 24 | options?: ChartOptions; 25 | plugins?: Plugin_2[]; 26 | }; 27 | 28 | declare const _default: DefineComponent<__VLS_Props, { 29 | chartJSState: { 30 | chart: Chart | null; 31 | plugins: Plugin_2[]; 32 | props: { 33 | type: ChartType; 34 | height: number; 35 | width: number; 36 | data: ChartData; 37 | options: _DeepPartialObject & ElementChartOptions & PluginChartOptions & DatasetChartOptions & ScaleChartOptions>; 38 | plugins: Plugin_2[]; 39 | }; 40 | }; 41 | render: () => void; 42 | destroy: () => void; 43 | update: (mode?: UpdateMode) => void; 44 | resize: () => void; 45 | }, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {}, string, PublicProps, Readonly<__VLS_Props> & Readonly<{}>, { 46 | plugins: Plugin_2[]; 47 | options: ChartOptions; 48 | }, {}, {}, {}, string, ComponentProvideOptions, false, { 49 | chartRef: HTMLCanvasElement; 50 | }, HTMLCanvasElement>; 51 | export default _default; 52 | 53 | declare type UpdateMode = 'resize' | 'reset' | 'default' | 'none' | 'hide' | 'show' | 'active'; 54 | 55 | export { } 56 | -------------------------------------------------------------------------------- /dist/types/Vue3ChartJs.vue.d.ts: -------------------------------------------------------------------------------- 1 | import { Chart, Plugin } from 'chart.js'; 2 | import { ChartType, ChartData, ChartOptions } from 'chart.js/dist/types'; 3 | type UpdateMode = 'resize' | 'reset' | 'default' | 'none' | 'hide' | 'show' | 'active'; 4 | type __VLS_Props = { 5 | type: ChartType; 6 | height?: number; 7 | width?: number; 8 | data: ChartData; 9 | options?: ChartOptions; 10 | plugins?: Plugin[]; 11 | }; 12 | declare const _default: import("vue").DefineComponent<__VLS_Props, { 13 | chartJSState: { 14 | chart: Chart | null; 15 | plugins: Plugin[]; 16 | props: { 17 | type: ChartType; 18 | height: number; 19 | width: number; 20 | data: ChartData; 21 | options: import("chart.js/dist/types/utils")._DeepPartialObject & import("chart.js").ElementChartOptions & import("chart.js").PluginChartOptions & import("chart.js").DatasetChartOptions & import("chart.js").ScaleChartOptions>; 22 | plugins: Plugin[]; 23 | }; 24 | }; 25 | render: () => void; 26 | destroy: () => void; 27 | update: (mode?: UpdateMode) => void; 28 | resize: () => void; 29 | }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, { 30 | plugins: Plugin[]; 31 | options: ChartOptions; 32 | }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, { 33 | chartRef: HTMLCanvasElement; 34 | }, HTMLCanvasElement>; 35 | export default _default; 36 | //# sourceMappingURL=Vue3ChartJs.vue.d.ts.map -------------------------------------------------------------------------------- /dist/types/Vue3ChartJs.vue.d.ts.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"Vue3ChartJs.vue.d.ts","sourceRoot":"","sources":["../../lib/Vue3ChartJs.vue"],"names":[],"mappings":"AAuGA,OAAO,EAAE,KAAK,EAAiB,MAAM,EAAE,MAAM,UAAU,CAAC;AACxD,OAAO,EACL,SAAS,EACT,SAAS,EACT,YAAY,EACb,MAAM,qBAAqB,CAAC;AAE7B,KAAK,UAAU,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;AASvF,KAAK,WAAW,GAAG;IACjB,IAAI,EAAE,SAAS,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB,CAAC;;;eAiBe,KAAK,GAAG,IAAI;;;kBAvBrB,SAAS;oBACN,MAAM;mBACP,MAAM;kBACR,SAAS;;;;;;;oBAmCK,UAAU;;;aAjCpB,MAAM,EAAE;aADR,YAAY;;;;AA8HxB,wBAWG"} -------------------------------------------------------------------------------- /dist/types/includes.d.ts: -------------------------------------------------------------------------------- 1 | import { Ref, VNodeRef, EmitFn } from 'vue'; 2 | declare const chartJsEventNames: string[]; 3 | interface EventObject { 4 | type: string; 5 | chartRef?: Ref; 6 | preventDefault: () => void; 7 | isDefaultPrevented: () => boolean; 8 | _defaultPrevented: boolean; 9 | } 10 | declare function generateEventObject(type: string, chartRef?: Ref): EventObject; 11 | declare function generateChartJsEventListener(emit: EmitFn, event: EventObject): { 12 | [x: string]: () => boolean; 13 | }; 14 | export { chartJsEventNames, generateEventObject, generateChartJsEventListener, }; 15 | //# sourceMappingURL=includes.d.ts.map -------------------------------------------------------------------------------- /dist/types/includes.d.ts.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"includes.d.ts","sourceRoot":"","sources":["../../lib/includes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,KAAK,CAAC;AAE5C,QAAA,MAAM,iBAAiB,EAAE,MAAM,EAqC9B,CAAC;AAEF,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,GAAG,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;IAChC,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,kBAAkB,EAAE,MAAM,OAAO,CAAC;IAClC,iBAAiB,EAAE,OAAO,CAAC;CAC5B;AAED,iBAAS,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,GAAG,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,WAAW,CAYvF;AAED,iBAAS,4BAA4B,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW;;EAQrE;AAED,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,4BAA4B,GAC7B,CAAC"} -------------------------------------------------------------------------------- /dist/types/main.d.ts: -------------------------------------------------------------------------------- 1 | import Vue3ChartJs from './Vue3ChartJs.vue'; 2 | export default Vue3ChartJs; 3 | //# sourceMappingURL=main.d.ts.map -------------------------------------------------------------------------------- /dist/types/main.d.ts.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../lib/main.ts"],"names":[],"mappings":"AACA,OAAO,WAAW,MAAM,mBAAmB,CAAC;AAgB5C,eAAe,WAAW,CAAC"} -------------------------------------------------------------------------------- /dist/vue3-chartjs.es.js: -------------------------------------------------------------------------------- 1 | import { defineComponent as m, ref as v, onMounted as w, createElementBlock as y, openBlock as E } from "vue"; 2 | import { registerables as p, Chart as i } from "chart.js"; 3 | const c = [ 4 | "install", 5 | "uninstall", 6 | "beforeInit", 7 | "resize", 8 | "afterInit", 9 | "start", 10 | "stop", 11 | "beforeUpdate", 12 | "beforeLayout", 13 | "beforeDataLimits", 14 | "afterDataLimits", 15 | "beforeBuildTicks", 16 | "afterBuildTicks", 17 | "afterLayout", 18 | "beforeElementsUpdate", 19 | "beforeDatasetsUpdate", 20 | "beforeDatasetUpdate", 21 | "afterDatasetUpdate", 22 | "afterDatasetsUpdate", 23 | "afterUpdate", 24 | "beforeRender", 25 | "beforeDraw", 26 | "beforeDatasetsDraw", 27 | "beforeDatasetDraw", 28 | "afterDatasetDraw", 29 | "afterDatasetsDraw", 30 | "beforeTooltipDraw", 31 | "afterTooltipDraw", 32 | "afterDraw", 33 | "afterRender", 34 | "resize", 35 | "reset", 36 | "beforeDestroy", 37 | "afterDestroy", 38 | "beforeEvent", 39 | "afterEvent" 40 | ]; 41 | function P(r, t) { 42 | return { 43 | type: r, 44 | chartRef: t, 45 | preventDefault() { 46 | this._defaultPrevented = !0; 47 | }, 48 | isDefaultPrevented() { 49 | return !this._defaultPrevented; 50 | }, 51 | _defaultPrevented: !1 52 | }; 53 | } 54 | function _(r, t) { 55 | return { 56 | [t.type]: () => (r(t.type, t), t.isDefaultPrevented()) 57 | }; 58 | } 59 | const J = ["height", "width"], s = /* @__PURE__ */ m({ 60 | __name: "Vue3ChartJs", 61 | props: { 62 | type: {}, 63 | height: {}, 64 | width: {}, 65 | data: {}, 66 | options: { default: () => ({}) }, 67 | plugins: { default: () => [] } 68 | }, 69 | emits: c, 70 | setup(r, { expose: t, emit: n }) { 71 | p !== void 0 && i.register(...p); 72 | const f = r, d = n, o = v(null), e = { 73 | chart: null, 74 | plugins: [ 75 | c.reduce((a, u) => { 76 | const b = P(u, o); 77 | return { ...a, ..._(d, b) }; 78 | }, { id: "Vue3ChartJsEventHookPlugin" }), 79 | ...f.plugins ?? [] 80 | ], 81 | props: { ...f } 82 | }, h = () => { 83 | e.chart && (e.chart.destroy(), e.chart = null); 84 | }, g = (a = "default") => { 85 | e.chart && (e.chart.data = { ...e.chart.data, ...e.props.data }, e.chart.options = { ...e.chart.options, ...e.props.options }, e.chart.update(a)); 86 | }, D = () => { 87 | e.chart && e.chart.resize(); 88 | }, l = () => { 89 | if (e.chart) 90 | return e.chart.update(); 91 | e.chart = new i(o.value.getContext("2d"), { 92 | type: e.props.type, 93 | data: e.props.data, 94 | options: e.props.options, 95 | plugins: e.plugins 96 | }); 97 | }; 98 | return t({ 99 | chartJSState: e, 100 | render: l, 101 | destroy: h, 102 | update: g, 103 | resize: D 104 | }), w(() => l()), (a, u) => (E(), y("canvas", { 105 | ref_key: "chartRef", 106 | ref: o, 107 | height: a.height, 108 | width: a.width 109 | }, null, 8, J)); 110 | } 111 | }); 112 | s.registerGlobalPlugins = (r) => { 113 | i.register(...r); 114 | }; 115 | s.install = (r, t = {}) => { 116 | var n; 117 | r.component(s.name ?? "Vue3ChartJs", s), (n = t == null ? void 0 : t.plugins) != null && n.length && s.registerGlobalPlugins(t.plugins); 118 | }; 119 | export { 120 | s as default 121 | }; 122 | -------------------------------------------------------------------------------- /dist/vue3-chartjs.umd.js: -------------------------------------------------------------------------------- 1 | (function(r,a){typeof exports=="object"&&typeof module<"u"?module.exports=a(require("vue"),require("chart.js")):typeof define=="function"&&define.amd?define(["vue","chart.js"],a):(r=typeof globalThis<"u"?globalThis:r||self,r.Vue3ChartJs=a(r.Vue,r.Chart))})(this,function(r,a){"use strict";const u=["install","uninstall","beforeInit","resize","afterInit","start","stop","beforeUpdate","beforeLayout","beforeDataLimits","afterDataLimits","beforeBuildTicks","afterBuildTicks","afterLayout","beforeElementsUpdate","beforeDatasetsUpdate","beforeDatasetUpdate","afterDatasetUpdate","afterDatasetsUpdate","afterUpdate","beforeRender","beforeDraw","beforeDatasetsDraw","beforeDatasetDraw","afterDatasetDraw","afterDatasetsDraw","beforeTooltipDraw","afterTooltipDraw","afterDraw","afterRender","resize","reset","beforeDestroy","afterDestroy","beforeEvent","afterEvent"];function c(s,t){return{type:s,chartRef:t,preventDefault(){this._defaultPrevented=!0},isDefaultPrevented(){return!this._defaultPrevented},_defaultPrevented:!1}}function h(s,t){return{[t.type]:()=>(s(t.type,t),t.isDefaultPrevented())}}const g=["height","width"],n=r.defineComponent({__name:"Vue3ChartJs",props:{type:{},height:{},width:{},data:{},options:{default:()=>({})},plugins:{default:()=>[]}},emits:u,setup(s,{expose:t,emit:o}){a.registerables!==void 0&&a.Chart.register(...a.registerables);const d=s,D=o,f=r.ref(null),e={chart:null,plugins:[u.reduce((i,l)=>{const v=c(l,f);return{...i,...h(D,v)}},{id:"Vue3ChartJsEventHookPlugin"}),...d.plugins??[]],props:{...d}},b=()=>{e.chart&&(e.chart.destroy(),e.chart=null)},m=(i="default")=>{e.chart&&(e.chart.data={...e.chart.data,...e.props.data},e.chart.options={...e.chart.options,...e.props.options},e.chart.update(i))},y=()=>{e.chart&&e.chart.resize()},p=()=>{if(e.chart)return e.chart.update();e.chart=new a.Chart(f.value.getContext("2d"),{type:e.props.type,data:e.props.data,options:e.props.options,plugins:e.plugins})};return t({chartJSState:e,render:p,destroy:b,update:m,resize:y}),r.onMounted(()=>p()),(i,l)=>(r.openBlock(),r.createElementBlock("canvas",{ref_key:"chartRef",ref:f,height:i.height,width:i.width},null,8,g))}});return n.registerGlobalPlugins=s=>{a.Chart.register(...s)},n.install=(s,t={})=>{var o;s.component(n.name??"Vue3ChartJs",n),(o=t==null?void 0:t.plugins)!=null&&o.length&&n.registerGlobalPlugins(t.plugins)},n}); 2 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | // eslint.config.mjs 2 | import pluginVue from 'eslint-plugin-vue' 3 | import { 4 | defineConfigWithVueTs, 5 | vueTsConfigs, 6 | } from '@vue/eslint-config-typescript' 7 | 8 | export default defineConfigWithVueTs( 9 | pluginVue.configs['flat/essential'], 10 | vueTsConfigs.recommended, 11 | ) -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | transform: { 4 | '^.+\\.vue$': '@vue/vue3-jest', 5 | '^.+\\ts': 'ts-jest' 6 | }, 7 | testEnvironment: 'jsdom', 8 | testEnvironmentOptions: { 9 | customExportConditions: ["node", "node-addons"], 10 | }, 11 | setupFiles: ['jest-canvas-mock'], 12 | collectCoverage: true, 13 | coverageDirectory: 'coverage', 14 | coverageReporters: ['lcov', 'text'], 15 | coverageProvider: "v8", 16 | moduleFileExtensions: ['vue', 'js', 'json', 'jsx', 'ts', 'tsx', 'node'], 17 | } 18 | -------------------------------------------------------------------------------- /lib/Vue3ChartJs.vue: -------------------------------------------------------------------------------- 1 | 96 | 97 | -------------------------------------------------------------------------------- /lib/includes.ts: -------------------------------------------------------------------------------- 1 | import { Ref, VNodeRef, EmitFn } from 'vue'; 2 | 3 | const chartJsEventNames: string[] = [ 4 | 'install', 5 | 'uninstall', 6 | 'beforeInit', 7 | 'resize', 8 | 'afterInit', 9 | 'start', 10 | 'stop', 11 | 'beforeUpdate', 12 | 'beforeLayout', 13 | 'beforeDataLimits', 14 | 'afterDataLimits', 15 | 'beforeBuildTicks', 16 | 'afterBuildTicks', 17 | 'afterLayout', 18 | 'beforeElementsUpdate', 19 | 'beforeDatasetsUpdate', 20 | 'beforeDatasetUpdate', 21 | 'afterDatasetUpdate', 22 | 'afterDatasetsUpdate', 23 | 'afterUpdate', 24 | 'beforeRender', 25 | 'beforeDraw', 26 | 'beforeDatasetsDraw', 27 | 'beforeDatasetDraw', 28 | 'afterDatasetDraw', 29 | 'afterDatasetsDraw', 30 | 'beforeTooltipDraw', 31 | 'afterTooltipDraw', 32 | 'afterDraw', 33 | 'afterRender', 34 | 'resize', 35 | 'reset', 36 | 'beforeDestroy', 37 | 'afterDestroy', 38 | 'beforeEvent', 39 | 'afterEvent', 40 | ]; 41 | 42 | interface EventObject { 43 | type: string; 44 | chartRef?: Ref; 45 | preventDefault: () => void; 46 | isDefaultPrevented: () => boolean; 47 | _defaultPrevented: boolean; 48 | } 49 | 50 | function generateEventObject(type: string, chartRef?: Ref): EventObject { 51 | return { 52 | type: type, 53 | chartRef: chartRef, 54 | preventDefault() { 55 | this._defaultPrevented = true; 56 | }, 57 | isDefaultPrevented() { 58 | return !this._defaultPrevented; 59 | }, 60 | _defaultPrevented: false, 61 | }; 62 | } 63 | 64 | function generateChartJsEventListener(emit: EmitFn, event: EventObject) { 65 | return { 66 | [event.type]: () => { 67 | emit(event.type, event); 68 | 69 | return event.isDefaultPrevented(); 70 | } 71 | }; 72 | } 73 | 74 | export { 75 | chartJsEventNames, 76 | generateEventObject, 77 | generateChartJsEventListener, 78 | }; -------------------------------------------------------------------------------- /lib/main.ts: -------------------------------------------------------------------------------- 1 | import { App } from 'vue'; 2 | import Vue3ChartJs from './Vue3ChartJs.vue'; 3 | import { Chart, Plugin } from 'chart.js'; 4 | 5 | /* c8 ignore next */ 6 | Vue3ChartJs.registerGlobalPlugins = (plugins: Plugin[]) => { 7 | Chart.register(...plugins); 8 | }; 9 | 10 | Vue3ChartJs.install = (app: App, options: { plugins?: Plugin[] } = {}) => { 11 | app.component(Vue3ChartJs.name ?? 'Vue3ChartJs', Vue3ChartJs); 12 | 13 | if (options?.plugins?.length) { 14 | Vue3ChartJs.registerGlobalPlugins(options.plugins); 15 | } 16 | }; 17 | 18 | export default Vue3ChartJs; -------------------------------------------------------------------------------- /lib/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import { DefineComponent } from 'vue'; 3 | const component: DefineComponent; 4 | export default component; 5 | } 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@j-t-mcc/vue3-chartjs", 3 | "version": "2.1.0", 4 | "author": "Tyson McCarney (https://tysonmccarney.com)", 5 | "description": "Vue3 wrapper for Chart.js", 6 | "license": "MIT", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/J-T-McC/vue3-chartjs" 10 | }, 11 | "homepage": "https://github.com/J-T-McC/vue3-chartjs/blob/main/README.md", 12 | "keywords": [ 13 | "chart.js", 14 | "vue3" 15 | ], 16 | "files": [ 17 | "dist/", 18 | "README.md", 19 | "LICENSE" 20 | ], 21 | "main": "./dist/vue3-chartjs.umd.js", 22 | "module": "./dist/vue3-chartjs.es.js", 23 | "types": "./dist/index.d.ts", 24 | "exports": { 25 | ".": { 26 | "import": "./dist/vue3-chartjs.es.js", 27 | "require": "./dist/vue3-chartjs.umd.js" 28 | } 29 | }, 30 | "scripts": { 31 | "dev": "vite", 32 | "build": "eslint lib/** && jest --coverage --preset ts-jest && vite build && vue-tsc --noEmit false", 33 | "test": "jest --coverage --preset ts-jest", 34 | "lint": "eslint lib/**" 35 | }, 36 | "peerDependencies": { 37 | "chart.js": "^4.0", 38 | "vue": "^3.0.0" 39 | }, 40 | "devDependencies": { 41 | "@babel/core": "^7.26.7", 42 | "@babel/preset-env": "^7.26.7", 43 | "@types/jest": "^29.5.14", 44 | "@types/node": "^22.13.1", 45 | "@vitejs/plugin-vue": "^5.0", 46 | "@vue/compiler-sfc": "^3.5.13", 47 | "@vue/eslint-config-typescript": "^14.4.0", 48 | "@vue/test-utils": "^2.4.6", 49 | "@vue/vue3-jest": "^28.1.0", 50 | "babel-jest": "^28.1.3", 51 | "chart.js": "^4.4.7", 52 | "chartjs-plugin-zoom": "^1.2.1", 53 | "coveralls": "^3.1.1", 54 | "eslint": "^9.20.1", 55 | "eslint-plugin-vue": "^9.32.0", 56 | "jest": "^29.7.0", 57 | "jest-canvas-mock": "^2.5.2", 58 | "jest-environment-jsdom": "^29.7.0", 59 | "prettier": "2.2.1", 60 | "ts-jest": "^29.2.5", 61 | "typescript": "^5.7.3", 62 | "vite": "^6.0", 63 | "vite-plugin-checker": "^0.8.0", 64 | "vite-plugin-dts": "^4.5.0", 65 | "vue": "^3.5.13", 66 | "vue-tsc": "^2.2.0" 67 | }, 68 | "babel": { 69 | "presets": [ 70 | [ 71 | "@babel/preset-env", 72 | { 73 | "targets": { 74 | "node": "current" 75 | } 76 | } 77 | ] 78 | ] 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 162 | 163 | -------------------------------------------------------------------------------- /src/charts.js: -------------------------------------------------------------------------------- 1 | const barChart = { 2 | id: 'bar', 3 | type: 'bar', 4 | data: { 5 | labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'], 6 | datasets: [{ 7 | label: '# of Votes', 8 | data: [12, 19, 3, 5, 2, 3], 9 | backgroundColor: [ 10 | 'rgba(255, 99, 132, 0.2)', 11 | 'rgba(54, 162, 235, 0.2)', 12 | 'rgba(255, 206, 86, 0.2)', 13 | 'rgba(75, 192, 192, 0.2)', 14 | 'rgba(153, 102, 255, 0.2)', 15 | 'rgba(255, 159, 64, 0.2)' 16 | ], 17 | borderColor: [ 18 | 'rgba(255, 99, 132, 1)', 19 | 'rgba(54, 162, 235, 1)', 20 | 'rgba(255, 206, 86, 1)', 21 | 'rgba(75, 192, 192, 1)', 22 | 'rgba(153, 102, 255, 1)', 23 | 'rgba(255, 159, 64, 1)' 24 | ], 25 | borderWidth: 2, 26 | borderRadius: Number.MAX_VALUE, 27 | borderSkipped: false, 28 | }] 29 | }, 30 | options: { 31 | scales: { 32 | x: { 33 | beginAtZero: true 34 | } 35 | } 36 | } 37 | } 38 | 39 | const doughnutChart = { 40 | id: 'doughnut', 41 | type: 'doughnut', 42 | data: { 43 | labels: ['VueJs', 'EmberJs', 'ReactJs', 'AngularJs'], 44 | datasets: [ 45 | { 46 | backgroundColor: [ 47 | '#41B883', 48 | '#E46651', 49 | '#00D8FF', 50 | '#DD1B16' 51 | ], 52 | data: [40, 20, 80, 10] 53 | } 54 | ] 55 | }, 56 | options: { 57 | plugins: { 58 | zoom: { 59 | zoom: { 60 | enabled: true, 61 | mode: 'xy', 62 | } 63 | } 64 | } 65 | } 66 | } 67 | 68 | export { 69 | barChart, 70 | doughnutChart 71 | } -------------------------------------------------------------------------------- /src/direct.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Test 6 | 7 | 8 |
9 |
10 | 11 |
12 | 13 |
14 | 15 | 16 | 17 | 18 | 93 | 94 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | 4 | import Vue3ChartJs from '../'; 5 | 6 | import zoomPlugin from 'chartjs-plugin-zoom' 7 | 8 | Vue3ChartJs.registerGlobalPlugins([zoomPlugin]) 9 | 10 | const Vue = createApp(App) 11 | 12 | Vue.use(Vue3ChartJs, { 13 | plugins: [ 14 | zoomPlugin 15 | ] 16 | }) 17 | 18 | Vue.mount('#app') 19 | -------------------------------------------------------------------------------- /test/chart.props.ts: -------------------------------------------------------------------------------- 1 | export interface DoughnutProps { 2 | id: string; 3 | type: string; 4 | width?: number; 5 | height?: number; 6 | data: { 7 | labels: string[]; 8 | datasets: { 9 | backgroundColor: string[]; 10 | data: number[]; 11 | }[]; 12 | }; 13 | options?: { 14 | responsive?: boolean; 15 | plugins?: { 16 | title?: { 17 | display?: boolean; 18 | text?: string; 19 | } 20 | }; 21 | }; 22 | plugins?: []; 23 | } 24 | 25 | const getDoughnutProps = (): DoughnutProps => { 26 | return { 27 | id: 'doughnut', 28 | type: 'doughnut', 29 | data: { 30 | labels: ['VueJs', 'EmberJs', 'ReactJs', 'AngularJs'], 31 | datasets: [ 32 | { 33 | backgroundColor: [ 34 | '#41B883', 35 | '#E46651', 36 | '#00D8FF', 37 | '#DD1B16' 38 | ], 39 | data: [40, 20, 80, 10] 40 | } 41 | ] 42 | }, 43 | options: { 44 | responsive: true 45 | }, 46 | } 47 | } 48 | 49 | export { 50 | getDoughnutProps, 51 | } 52 | -------------------------------------------------------------------------------- /test/wrapper.test.ts: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils'; 2 | import Vue3ChartJs from '../lib/main'; 3 | import { Chart } from 'chart.js'; 4 | import { createApp, ref } from 'vue'; 5 | import { getDoughnutProps } from './chart.props'; 6 | import { generateEventObject, generateChartJsEventListener } from '../lib/includes'; 7 | 8 | const Vue3ChartJsPlugin = Vue3ChartJs as any; 9 | 10 | const factory = function (props: Record) { 11 | return mount(Vue3ChartJsPlugin, { 12 | props: { ...props } 13 | }); 14 | }; 15 | 16 | describe('generateEventObject', () => { 17 | it('should create an event object with the given type and chartRef', () => { 18 | const type = 'testEvent'; 19 | const chartRef = ref(null); 20 | const eventObject = generateEventObject(type, chartRef); 21 | 22 | expect(eventObject.type).toBe(type); 23 | expect(eventObject.chartRef).toBe(chartRef); 24 | expect(eventObject._defaultPrevented).toBe(false); 25 | }); 26 | 27 | it('should set _defaultPrevented to true when preventDefault is called', () => { 28 | const eventObject = generateEventObject('testEvent'); 29 | 30 | eventObject.preventDefault(); 31 | expect(eventObject._defaultPrevented).toBe(true); 32 | }); 33 | 34 | it('should return false from isDefaultPrevented when _defaultPrevented is false', () => { 35 | const eventObject = generateEventObject('testEvent'); 36 | 37 | expect(eventObject.isDefaultPrevented()).toBe(true); 38 | }); 39 | 40 | it('should return true from isDefaultPrevented when _defaultPrevented is true', () => { 41 | const eventObject = generateEventObject('testEvent'); 42 | eventObject.preventDefault(); 43 | 44 | expect(eventObject.isDefaultPrevented()).toBe(false); 45 | }); 46 | }); 47 | 48 | describe('generateChartJsEventListener', () => { 49 | it('should emit the event with the correct type and event object', () => { 50 | const mockEmit = jest.fn(); 51 | const event = generateEventObject('testEvent'); 52 | const listener = generateChartJsEventListener(mockEmit, event); 53 | 54 | listener['testEvent'](); 55 | 56 | expect(mockEmit).toHaveBeenCalledWith('testEvent', event); 57 | }); 58 | 59 | it('should return true if the event is not prevented', () => { 60 | const mockEmit = jest.fn(); 61 | const event = generateEventObject('testEvent'); 62 | const listener = generateChartJsEventListener(mockEmit, event); 63 | 64 | const result = listener['testEvent'](); 65 | 66 | expect(result).toBe(true); 67 | }); 68 | 69 | it('should return false if the event is prevented', () => { 70 | const mockEmit = jest.fn(); 71 | const event = generateEventObject('testEvent'); 72 | event.preventDefault(); 73 | const listener = generateChartJsEventListener(mockEmit, event); 74 | 75 | const result = listener['testEvent'](); 76 | 77 | expect(result).toBe(false); 78 | }); 79 | }); 80 | 81 | describe('init', () => { 82 | it('installs globally', () => { 83 | const App = createApp({}); 84 | App.use(Vue3ChartJsPlugin); 85 | expect(App._context.components.hasOwnProperty('Vue3ChartJs')).toBeTruthy(); 86 | }); 87 | 88 | it('ChartJS instance is accessible', () => { 89 | const wrapper = factory(getDoughnutProps()); 90 | const ref = wrapper.vm as any; 91 | expect(ref.chartJSState.chart).toBeTruthy(); // Ensures that the Chart.js instance is initialized 92 | }); 93 | 94 | it('should create a chart instance on mount', async () => { 95 | const wrapper = factory(getDoughnutProps()); 96 | const ref = wrapper.vm as any; 97 | await ref.render(); 98 | expect(ref.chartJSState.chart).toBeTruthy(); 99 | }); 100 | 101 | it('registers global plugins', () => { 102 | const App = createApp({}); 103 | App.use(Vue3ChartJsPlugin, { 104 | plugins: [{ id: 'globallyImportedTestPlugin' }] 105 | }); 106 | const plugins = Chart.defaults.plugins as { [key: string]: any }; 107 | expect(plugins['globallyImportedTestPlugin']).toBeTruthy(); 108 | }); 109 | 110 | it('ChartJS instance is accessible', () => { 111 | const wrapper = factory(getDoughnutProps()); 112 | const ref = wrapper.vm as any; 113 | expect(ref.chartJSState.chart).toBeTruthy(); 114 | }); 115 | 116 | it('defaults options to empty object', () => { 117 | const doughnutProps = getDoughnutProps(); 118 | delete doughnutProps.options; 119 | const wrapper = factory(doughnutProps); 120 | const props = wrapper.props() as any; 121 | expect(props.options).toMatchObject({}); 122 | }); 123 | 124 | it('defaults plugins to empty array', () => { 125 | const doughnutProps = getDoughnutProps(); 126 | delete doughnutProps.plugins; 127 | const wrapper = factory(doughnutProps); 128 | const props = wrapper.props() as any; 129 | expect(props.plugins).toEqual([]); 130 | }); 131 | 132 | it("calls render on mounted", () => { 133 | const doughnutProps = getDoughnutProps(); 134 | const renderSpy = jest.spyOn(Vue3ChartJsPlugin, "render"); 135 | 136 | mount(Vue3ChartJsPlugin, { 137 | props: doughnutProps 138 | }); 139 | 140 | expect(renderSpy).toHaveBeenCalled(); 141 | }); 142 | }); 143 | 144 | describe('chart dimensions', () => { 145 | it('it sets fixed height and width', async () => { 146 | const doughnutProps = getDoughnutProps(); 147 | doughnutProps.options!.responsive = false; 148 | doughnutProps.width = doughnutProps.height = 800; 149 | const wrapper = factory(doughnutProps); 150 | const ref = wrapper.vm as any; 151 | ref.render(); 152 | expect(ref.chartJSState.chart.height).toEqual(800); 153 | expect(ref.chartJSState.chart.width).toEqual(800); 154 | }); 155 | }); 156 | 157 | describe('chart reloading', () => { 158 | it('reloads if already exists', async () => { 159 | const wrapper = factory(getDoughnutProps()); 160 | const ref = wrapper.vm as any; 161 | ref.render(); 162 | expect(wrapper.emitted('afterInit')).toHaveLength(1); 163 | ref.render(); 164 | expect(wrapper.emitted('afterUpdate')).toHaveLength(2); 165 | expect(wrapper.emitted('afterInit')).toHaveLength(1); 166 | }); 167 | }); 168 | 169 | describe('component methods', () => { 170 | it('destroys if chart exists', async () => { 171 | const wrapper = factory(getDoughnutProps()); 172 | const ref = wrapper.vm as any; 173 | expect(ref.chartJSState.chart).toBeTruthy(); 174 | ref.destroy(); 175 | expect(ref.chartJSState.chart).toBeFalsy(); 176 | }); 177 | 178 | it('updates data', async () => { 179 | const doughnutProps = getDoughnutProps(); 180 | const wrapper = factory(doughnutProps); 181 | const ref = wrapper.vm as any; 182 | const chart = ref.chartJSState.chart; 183 | expect(wrapper.emitted('afterInit')).toHaveLength(1); 184 | expect(chart.data.datasets[0].data).toEqual(doughnutProps.data.datasets[0].data); 185 | doughnutProps.data.datasets[0].data = [1, 2, 3, 4]; 186 | ref.update(); 187 | expect(wrapper.emitted('afterUpdate')).toHaveLength(1); 188 | expect(chart.data.datasets[0].data).toEqual(doughnutProps.data.datasets[0].data); 189 | }); 190 | 191 | it('updates options', () => { 192 | const doughnutProps = getDoughnutProps(); 193 | const wrapper = factory(doughnutProps); 194 | const ref = wrapper.vm as any; 195 | const chart = ref.chartJSState.chart; 196 | expect(wrapper.emitted('afterInit')).toHaveLength(1); 197 | expect(chart.options.plugins.title.display).toBeFalsy(); 198 | doughnutProps.options!.plugins!.title = { 199 | text: 'Updated', 200 | display: true 201 | }; 202 | ref.update(); 203 | expect(wrapper.emitted('afterUpdate')).toHaveLength(1); 204 | expect(chart.options.plugins.title.text).toEqual('Updated'); 205 | }); 206 | 207 | it('implements prevent default for emitted chart.js hooks', () => { 208 | let invoked = 0; 209 | 210 | const mockEmit = (e: string) => { 211 | invoked++; 212 | }; 213 | 214 | const eventAllowed = generateEventObject('test'); 215 | const pluginEventAllowed = generateChartJsEventListener(mockEmit, eventAllowed); 216 | expect(pluginEventAllowed['test']()).toBeTruthy(); 217 | 218 | const eventPrevented = generateEventObject('test'); 219 | eventPrevented.preventDefault(); 220 | const pluginEventPrevented = generateChartJsEventListener(mockEmit, eventPrevented); 221 | expect(pluginEventPrevented['test']()).toBeFalsy(); 222 | expect(invoked).toEqual(2); 223 | }); 224 | }); 225 | 226 | describe('emitted events', () => { 227 | it('subscribes to chartjs events', () => { 228 | const wrapper = factory(getDoughnutProps()); 229 | const ref = wrapper.vm as any; 230 | ref.render(); 231 | expect(wrapper.emitted()).toHaveProperty('beforeRender'); 232 | 233 | ref.resize(); 234 | expect(wrapper.emitted('resize')).toHaveLength(1); 235 | 236 | ref.chartJSState.chart.reset(); 237 | expect(wrapper.emitted('reset')).toBeTruthy(); 238 | 239 | ref.destroy(); 240 | expect(wrapper.emitted('uninstall')).toHaveLength(1); 241 | expect(wrapper.emitted('stop')).toHaveLength(1); 242 | }); 243 | }); -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "declaration": true, 7 | "declarationMap": true, 8 | "emitDeclarationOnly": true, 9 | "outDir": "dist/types", 10 | "strict": true, 11 | "esModuleInterop": true, 12 | "skipLibCheck": true, 13 | "forceConsistentCasingInFileNames": true, 14 | "sourceMap": false, 15 | "types": [ 16 | "jest" 17 | ] 18 | }, 19 | "include": ["lib/**/*.ts", "lib/**/*.vue", "lib/shims-vue.d.ts"] 20 | } 21 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import { defineConfig } from 'vite' 3 | import vue from '@vitejs/plugin-vue' 4 | import checker from 'vite-plugin-checker' 5 | import dts from 'vite-plugin-dts' 6 | 7 | const externalDeps = ['vue', 'chart.js']; 8 | const globals = { vue: 'Vue', 'chart.js': 'Chart' }; 9 | 10 | // https://vitejs.dev/config/ 11 | export default defineConfig(({ mode }) => { 12 | return { 13 | plugins: [ 14 | vue(), 15 | checker({ typescript: true }), 16 | dts({ 17 | outputDir: 'dist', 18 | entryRoot: 'lib', 19 | insertTypesEntry: true, 20 | rollupTypes: true, 21 | }), 22 | ], 23 | build: { 24 | lib: { 25 | entry: path.resolve(__dirname, 'lib/main.ts'), 26 | name: 'Vue3ChartJs', 27 | }, 28 | rollupOptions: { 29 | external: externalDeps, 30 | output: [ 31 | { 32 | format: 'es', 33 | entryFileNames: 'vue3-chartjs.es.js', 34 | globals, 35 | }, 36 | { 37 | format: 'umd', 38 | entryFileNames: 'vue3-chartjs.umd.js', 39 | name: 'Vue3ChartJs', 40 | globals, 41 | }, 42 | ], 43 | }, 44 | }, 45 | } 46 | }) 47 | --------------------------------------------------------------------------------