├── .editorconfig
├── .eslintignore
├── .eslintrc
├── .gitignore
├── .npmrc
├── .nuxtrc
├── .nvmrc
├── CHANGELOG.md
├── LICENSE
├── README.md
├── nuxt-plotly-logo.svg
├── package-lock.json
├── package.json
├── playground
├── app.vue
├── components
│ └── PieChart.client.vue
├── nuxt.config.ts
├── package.json
├── server
│ └── tsconfig.json
└── tsconfig.json
├── src
├── module.ts
└── runtime
│ ├── components
│ └── nuxt-plotly.ts
│ └── plugin.ts
├── test
├── basic.test.ts
└── fixtures
│ └── basic
│ ├── app.vue
│ ├── nuxt.config.ts
│ └── package.json
└── tsconfig.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_size = 2
5 | indent_style = space
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | dist
2 | node_modules
3 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "extends": ["@nuxt/eslint-config"]
4 | }
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Dependencies
2 | node_modules
3 |
4 | # Logs
5 | *.log*
6 |
7 | # Temp directories
8 | .temp
9 | .tmp
10 | .cache
11 |
12 | # Yarn
13 | **/.yarn/cache
14 | **/.yarn/*state*
15 |
16 | # Generated dirs
17 | dist
18 |
19 | # Nuxt
20 | .nuxt
21 | .output
22 | .vercel_build_output
23 | .build-*
24 | .env
25 | .netlify
26 |
27 | # Env
28 | .env
29 |
30 | # Testing
31 | reports
32 | coverage
33 | *.lcov
34 | .nyc_output
35 |
36 | # VSCode
37 | .vscode/*
38 | !.vscode/settings.json
39 | !.vscode/tasks.json
40 | !.vscode/launch.json
41 | !.vscode/extensions.json
42 | !.vscode/*.code-snippets
43 |
44 | # Intellij idea
45 | *.iml
46 | .idea
47 |
48 | # OSX
49 | .DS_Store
50 | .AppleDouble
51 | .LSOverride
52 | .AppleDB
53 | .AppleDesktop
54 | Network Trash Folder
55 | Temporary Items
56 | .apdisk
57 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | shamefully-hoist=true
2 | strict-peer-dependencies=false
3 |
--------------------------------------------------------------------------------
/.nuxtrc:
--------------------------------------------------------------------------------
1 | imports.autoImport=false
2 | typescript.includeWorkspace=true
3 | # enable TypeScript bundler module resolution - https://www.typescriptlang.org/docs/handbook/modules/reference.html#bundler
4 | experimental.typescriptBundlerResolution=true
5 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | v18.16.1
2 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## v1.0.14
2 |
3 | [compare changes](https://undefined/undefined/compare/v1.0.13...v1.0.14)
4 |
5 | ### 📖 Documentation
6 |
7 | - Use new `nuxi module add` command in installation (1be925d)
8 |
9 | ### 🏡 Chore
10 |
11 | - Test bundler module resolution (33ce203)
12 |
13 | ### ❤️ Contributors
14 |
15 | - Daniel Roe ([@danielroe](http://github.com/danielroe))
16 |
17 | ## v1.0.13
18 |
19 | [compare changes](https://undefined/undefined/compare/v1.0.12...v1.0.13)
20 |
21 | ## v1.0.12
22 |
23 | [compare changes](https://undefined/undefined/compare/v1.0.11...v1.0.12)
24 |
25 | ## v1.0.11
26 |
27 | [compare changes](https://undefined/undefined/compare/v1.0.10...v1.0.11)
28 |
29 | ## Updated
30 |
31 | - Add nuxt plugin `$plotly`
32 |
33 | ## Require
34 |
35 | - work with `client-only`
36 |
37 | ## Features
38 |
39 | - 🎇 All plotly.js methods and events
40 | - 🗾 Auto redraw on screensize changes and props update
41 | - 🚀 Data reactivity
42 | - 🏝️ TypeScript support
43 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2023-present Supanut Dokmaithong (Boomgeek)
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 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | # Nuxt Plotly Module
16 |
17 | 📊 `nuxt-plotly` module is thin Nuxt3 wrapper for [plotly.js](https://plotly.com/javascript/)
18 |
19 | - [🏀 Online playground](https://stackblitz.com/edit/nuxt-starter-1bs1ke?file=app.vue)
20 | - [📖 Plotly Documentation](https://plotly.com/javascript/plotly-fundamentals/)
21 |
22 | ## Features
23 |
24 |
25 |
26 | - 🎇 All plotly.js methods and events
27 | - 🗾 Auto redraw on screensize changes and props update
28 | - 🚀 Data reactivity
29 | - 🏝️ TypeScript support
30 |
31 | ## Quick Setup
32 |
33 | 1. Add `nuxt-plotly` dependency to your project
34 |
35 | ```bash
36 | npx nuxi@latest module add nuxt-plotly
37 | ```
38 |
39 | 2. Add `nuxt-plotly` to the `modules` section of `nuxt.config.ts`
40 |
41 | ```js
42 | // nuxt.config.js
43 | export default defineNuxtConfig({
44 | /**
45 | * Add nuxt-plotly module
46 | */
47 | modules: ["nuxt-plotly"],
48 |
49 | /**
50 | * Add nuxt-plotly module with options
51 | * Set the inject option to true to use plotly function via $plotly
52 | */
53 | // modules: [["nuxt-plotly", { inject: true }]],
54 | });
55 | ```
56 |
57 | 3. Add `plotly.js-dist-min` to the `vite.optimizeDeps.include` section of `nuxt.config.ts`
58 |
59 | ```js
60 | // nuxt.config.js
61 | export default defineNuxtConfig({
62 | vite: {
63 | optimizeDeps: {
64 | include: ["plotly.js-dist-min"],
65 | },
66 | },
67 | });
68 | ```
69 |
70 | That's it! You can now use Nuxt Plotly Module in your Nuxt app ✨
71 |
72 | ## Require client-side
73 |
74 | There are two ways to use the `nuxt-plotly` module on the client-side in Nuxt3:
75 |
76 | 1. Wrap the component with the `` tag.
77 |
78 | ```html
79 |
80 |
86 |
87 | ```
88 |
89 | 2. Create a file with the `.client.vue` extension, for example, [PieChart.client.vue](https://github.com/superdev-tech/nuxt-plotly/blob/main/playground/components/PieChart.client.vue) and then you can use the component without the `` tag.
90 |
91 | ## Plotly Event listeners
92 |
93 | You can access [Plotly events](https://plotly.com/javascript/plotlyjs-events) using the `@on-ready` directive to receive the `PlotlyHTMLElement` object from the `` component.
94 |
95 | - HTML template example
96 |
97 | ```html
98 |
99 |
100 |
106 |
107 |
108 | ```
109 |
110 | - After receiving the PlotlyHTMLElement, you can access Plotly events
111 |
112 | ```typescript
113 | function myChartOnReady(plotlyHTMLElement: NuxtPlotlyHTMLElement) {
114 | console.log({ plotlyHTMLElement });
115 |
116 | plotlyHTMLElement.on?.("plotly_afterplot", function () {
117 | console.log("done plotting");
118 | });
119 |
120 | plotlyHTMLElement.on?.("plotly_click", function () {
121 | alert("You clicked this Plotly chart!");
122 | });
123 | }
124 | ```
125 |
126 | ## Plotly Functions
127 |
128 | To use the [Plotly Function](https://plotly.com/javascript/plotlyjs-function-reference/) in your nuxt project, follow these steps:
129 |
130 | - Step 1: Set the `inject` option to `true` in the `nuxt-plotly` module configuration of your `nuxt.config.ts` file.
131 |
132 | ```js
133 | // nuxt.config.js
134 | export default defineNuxtConfig({
135 | modules: [["nuxt-plotly", { inject: true }]],
136 | });
137 | ```
138 |
139 | - Step 2: After setting the inject option to true, you can now access the plotly function via `$plotly` in your nuxt project.
140 |
141 | ```ts
142 | // app.vue
143 |
144 | const { $plotly } = useNuxtApp();
145 |
146 | /**
147 | * Show all plotly functions
148 | */
149 | console.log($plotly);
150 |
151 | /**
152 | * Use downloadImage function
153 | */
154 | $plotly.downloadImage(plotlyHTMLElement as HTMLElement, {
155 | format: "png",
156 | width: 800,
157 | height: 600,
158 | filename: "newplot",
159 | });
160 | ```
161 |
162 | ## Type Aliases
163 |
164 | These type aliases simplify the usage of Plotly types in your Nuxt project:
165 |
166 | ```typescript
167 | /**
168 | * Represents an array of Plotly data objects.
169 | */
170 | export type NuxtPlotlyData = Array;
171 |
172 | /**
173 | * Represents a partial configuration object for Plotly charts.
174 | */
175 | export type NuxtPlotlyConfig = Partial;
176 |
177 | /**
178 | * Represents a partial layout object for Plotly charts.
179 | */
180 | export type NuxtPlotlyLayout = Partial;
181 |
182 | /**
183 | * Represents a partial HTML element that holds a rendered Plotly chart.
184 | */
185 | export type NuxtPlotlyHTMLElement = Partial;
186 | ```
187 |
188 | With these type aliases, you can easily work with Plotly data, configurations, layouts, and HTML elements in your Nuxt application, enhancing your experience when creating interactive charts and visualizations.
189 |
190 | ## Development: If you want to contribute
191 |
192 | ```bash
193 | # Install dependencies
194 | npm install
195 |
196 | # Generate type stubs
197 | npm run dev:prepare
198 |
199 | # Develop with the playground
200 | npm run dev
201 |
202 | # Build the playground
203 | npm run dev:build
204 |
205 | # Run ESLint
206 | npm run lint
207 |
208 | # Run Vitest
209 | npm run test
210 | npm run test:watch
211 |
212 | # Release new version
213 | npm run release
214 | ```
215 |
216 | ## License
217 |
218 | Copyright © 2023 [Supanut Dokmaithong](https://github.com/Boomgeek).
219 |
220 | This project is [MIT licensed](https://github.com/superdev-tech/nuxt-plotly/blob/main/LICENSE).
221 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nuxt-plotly",
3 | "version": "1.0.14",
4 | "description": "nuxt-plotly module is thin Nuxt3 wrapper for plotly.js",
5 | "repository": "superdev-tech/nuxt-plotly",
6 | "author": "Supanut Dokmaithong",
7 | "keywords": [
8 | "nuxt",
9 | "nuxt3",
10 | "plotly",
11 | "plotly.js",
12 | "module"
13 | ],
14 | "license": "MIT",
15 | "type": "module",
16 | "exports": {
17 | ".": {
18 | "types": "./dist/types.d.ts",
19 | "import": "./dist/module.mjs",
20 | "require": "./dist/module.cjs"
21 | }
22 | },
23 | "main": "./dist/module.cjs",
24 | "types": "./dist/types.d.ts",
25 | "files": [
26 | "dist"
27 | ],
28 | "scripts": {
29 | "prepack": "nuxt-module-build",
30 | "dev": "nuxi dev playground",
31 | "dev:build": "nuxi build playground",
32 | "dev:prepare": "nuxt-module-build --stub && nuxi prepare playground",
33 | "release": "npm run lint && npm run test && npm run prepack && changelogen --release && npm publish && git push --follow-tags",
34 | "lint": "eslint .",
35 | "test": "vitest run",
36 | "test:watch": "vitest watch"
37 | },
38 | "dependencies": {
39 | "@nuxt/kit": "^3.6.5",
40 | "plotly.js-dist-min": "^2.24.3",
41 | "uuid": "^9.0.0"
42 | },
43 | "devDependencies": {
44 | "@nuxt/devtools": "latest",
45 | "@nuxt/eslint-config": "^0.1.1",
46 | "@nuxt/module-builder": "^0.4.0",
47 | "@nuxt/schema": "^3.6.5",
48 | "@nuxt/test-utils": "^3.6.5",
49 | "@types/node": "^18.16.19",
50 | "@types/plotly.js-dist-min": "^2.3.1",
51 | "@types/uuid": "^9.0.2",
52 | "changelogen": "^0.5.4",
53 | "eslint": "^8.45.0",
54 | "nuxt": "^3.6.5",
55 | "vitest": "^0.33.0"
56 | }
57 | }
--------------------------------------------------------------------------------
/playground/app.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
nuxt-plotly module playground!
5 |
6 |
13 |
14 |
15 |
16 |
17 |
18 |
63 |
--------------------------------------------------------------------------------
/playground/components/PieChart.client.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Test Re-Draw
5 |
6 |
7 |
8 |
14 |
15 |
16 |
17 |
116 |
--------------------------------------------------------------------------------
/playground/nuxt.config.ts:
--------------------------------------------------------------------------------
1 | export default defineNuxtConfig({
2 | modules: [["../src/module", { inject: true }]],
3 | devtools: { enabled: true },
4 | vite: {
5 | optimizeDeps: {
6 | include: ["plotly.js-dist-min"],
7 | },
8 | },
9 | });
10 |
--------------------------------------------------------------------------------
/playground/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "my-module-playground",
4 | "type": "module",
5 | "scripts": {
6 | "dev": "nuxi dev",
7 | "build": "nuxi build",
8 | "generate": "nuxi generate"
9 | },
10 | "devDependencies": {
11 | "nuxt": "latest"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/playground/server/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../.nuxt/tsconfig.server.json"
3 | }
4 |
--------------------------------------------------------------------------------
/playground/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./.nuxt/tsconfig.json"
3 | }
4 |
--------------------------------------------------------------------------------
/src/module.ts:
--------------------------------------------------------------------------------
1 | import {
2 | defineNuxtModule,
3 | createResolver,
4 | addComponent,
5 | addPlugin,
6 | } from "@nuxt/kit";
7 | import { name, version } from "../package.json";
8 | export type {
9 | NuxtPlotlyData,
10 | NuxtPlotlyConfig,
11 | NuxtPlotlyLayout,
12 | NuxtPlotlyHTMLElement,
13 | } from "./runtime/components/nuxt-plotly";
14 |
15 | export interface ModuleOptions {
16 | inject: boolean;
17 | }
18 |
19 | export default defineNuxtModule({
20 | defaults: () => ({
21 | inject: false,
22 | }),
23 | meta: {
24 | name,
25 | version,
26 | configKey: "nuxtPlotly",
27 | compatibility: {
28 | nuxt: "^3.6.0",
29 | },
30 | },
31 | setup(options) {
32 | const resolver = createResolver(import.meta.url);
33 |
34 | // Add component
35 | addComponent({
36 | name: "NuxtPlotly",
37 | filePath: resolver.resolve("./runtime/components/nuxt-plotly"),
38 | mode: "client",
39 | });
40 |
41 | // Add runtime plugin
42 | if (options.inject) {
43 | addPlugin({ src: resolver.resolve("./runtime/plugin"), mode: "client" });
44 | }
45 | },
46 | });
47 |
--------------------------------------------------------------------------------
/src/runtime/components/nuxt-plotly.ts:
--------------------------------------------------------------------------------
1 | // nuxt-img.ts
2 | import {
3 | h,
4 | defineComponent,
5 | reactive,
6 | onBeforeUnmount,
7 | onMounted,
8 | watch,
9 | PropType,
10 | } from "vue";
11 | import { v4 as uuidv4 } from "uuid";
12 | import Plotly from "plotly.js-dist-min";
13 |
14 | export type NuxtPlotlyData = Array;
15 | export type NuxtPlotlyConfig = Partial;
16 | export type NuxtPlotlyLayout = Partial;
17 | export type NuxtPlotlyHTMLElement = Partial;
18 |
19 | export default defineComponent({
20 | props: {
21 | data: { type: Array as PropType, required: true },
22 | config: {
23 | type: Object as PropType,
24 | required: false,
25 | default: undefined,
26 | },
27 | layout: {
28 | type: Object as PropType,
29 | required: false,
30 | default: undefined,
31 | },
32 | },
33 | emits: ["on-ready"],
34 | setup(props, ctx) {
35 | const datas = reactive({
36 | plotlyId: `plotly-${uuidv4()}`,
37 | resizeObserver: {} as ResizeObserver,
38 | timeOutFunctionId: {} as NodeJS.Timeout,
39 | plotlyHTMLElement: {} as NuxtPlotlyHTMLElement,
40 | });
41 |
42 | const setGraph = async () => {
43 | datas.plotlyHTMLElement = await Plotly.newPlot(
44 | datas.plotlyId,
45 | props.data,
46 | props.layout,
47 | props.config
48 | );
49 |
50 | ctx.emit("on-ready", datas.plotlyHTMLElement);
51 | };
52 |
53 | const setResizeObserver = () => {
54 | datas.resizeObserver = new ResizeObserver(() => {
55 | // debounce the reset
56 | clearTimeout(datas.timeOutFunctionId);
57 | datas.timeOutFunctionId = setTimeout(() => {
58 | setGraph();
59 | }, 100);
60 | });
61 | const plotlyElm = document.getElementById(datas.plotlyId);
62 | if (plotlyElm) {
63 | datas.resizeObserver.observe(plotlyElm);
64 | }
65 | };
66 |
67 | // the plotly component is running only on the client-side
68 | onMounted(() => {
69 | setGraph();
70 | setResizeObserver();
71 | });
72 |
73 | // reset graph when props change
74 | watch(
75 | () => [props.data, props.layout, props.config],
76 | () => {
77 | setGraph();
78 | },
79 | { deep: true }
80 | );
81 |
82 | onBeforeUnmount(() => {
83 | datas.resizeObserver.disconnect();
84 | });
85 |
86 | return () => h("div", { id: datas.plotlyId });
87 | },
88 | });
89 |
--------------------------------------------------------------------------------
/src/runtime/plugin.ts:
--------------------------------------------------------------------------------
1 | import type { Plugin } from "nuxt/app";
2 | import { defineNuxtPlugin } from "#imports";
3 |
4 | import Plotly from "plotly.js-dist-min";
5 |
6 | export default defineNuxtPlugin(() => {
7 | return {
8 | provide: {
9 | plotly: Plotly,
10 | },
11 | };
12 | }) as Plugin<{ plotly: typeof Plotly }>;
13 |
--------------------------------------------------------------------------------
/test/basic.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, it, expect } from 'vitest'
2 | import { fileURLToPath } from 'node:url'
3 | import { setup, $fetch } from '@nuxt/test-utils'
4 |
5 | describe('ssr', async () => {
6 | await setup({
7 | rootDir: fileURLToPath(new URL('./fixtures/basic', import.meta.url)),
8 | })
9 |
10 | it('renders the index page', async () => {
11 | // Get response to a server-rendered page with `$fetch`.
12 | const html = await $fetch('/')
13 | expect(html).toContain('basic
')
14 | })
15 | })
16 |
--------------------------------------------------------------------------------
/test/fixtures/basic/app.vue:
--------------------------------------------------------------------------------
1 |
2 | basic
3 |
4 |
5 |
7 |
--------------------------------------------------------------------------------
/test/fixtures/basic/nuxt.config.ts:
--------------------------------------------------------------------------------
1 | import MyModule from '../../../src/module'
2 |
3 | export default defineNuxtConfig({
4 | modules: [
5 | MyModule
6 | ]
7 | })
8 |
--------------------------------------------------------------------------------
/test/fixtures/basic/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "basic",
4 | "type": "module"
5 | }
6 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./playground/.nuxt/tsconfig.json",
3 | "compilerOptions": {
4 | "target": "esnext",
5 | "module": "esnext"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------