├── .eslintignore
├── .gitignore
├── .npmignore
├── assets
└── solid-chartjs-logo.png
├── src
├── index.tsx
├── types.ts
├── typedCharts.tsx
└── chart.tsx
├── dev
├── tsconfig.json
├── index.tsx
├── index.html
├── utils
│ └── index.ts
├── vite.config.ts
├── pages
│ ├── TypedChart.tsx
│ ├── Registerables.tsx
│ └── DefaultChart.tsx
├── App.tsx
└── styles
│ └── App.module.css
├── tsconfig.node.json
├── .editorconfig
├── tsup.config.ts
├── .prettierrc
├── env.d.ts
├── tsconfig.json
├── LICENSE
├── vitest.config.ts
├── .eslintrc
├── scripts
└── prepareCMD.sh
├── package.json
└── README.md
/.eslintignore:
--------------------------------------------------------------------------------
1 | .publish/*
2 | dist/*
3 | lib/*
4 | node_modules/*
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | gitignore
4 | .idea
5 |
6 | # tsup
7 | tsup.config.bundled_*.{m,c,}s
8 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | gitignore
4 | .idea
5 |
6 | # tsup
7 | tsup.config.bundled_*.{m,c,}s
8 |
--------------------------------------------------------------------------------
/assets/solid-chartjs-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s0ftik3/solid-chartjs/HEAD/assets/solid-chartjs-logo.png
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import DefaultChart from './chart'
2 | export type { ChartProps } from './types.js'
3 | export { DefaultChart }
4 | export * from './typedCharts'
5 |
--------------------------------------------------------------------------------
/dev/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "types": ["vite/client"]
5 | },
6 | "exclude": ["node_modules", "dist"]
7 | }
8 |
--------------------------------------------------------------------------------
/dev/index.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-non-null-assertion */
2 | import { render } from 'solid-js/web'
3 | import App from './App'
4 | render(() => , document.getElementById('root')!)
5 |
--------------------------------------------------------------------------------
/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "module": "esnext",
5 | "moduleResolution": "node"
6 | },
7 | "include": ["vitest.config.ts"]
8 | }
9 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # This file is for unifying the coding style for different editors and IDEs
2 | # editorconfig.org
3 | root = true
4 |
5 | [*]
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 | indent_style = space
11 | indent_size = 4
12 |
--------------------------------------------------------------------------------
/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'tsup-preset-solid'
2 |
3 | export default defineConfig(
4 | {
5 | entry: 'src/index.tsx',
6 | devEntry: true,
7 | dropConsole: true,
8 | },
9 | {
10 | // Enable this to write export conditions to package.json
11 | // writePackageJson: true,
12 | },
13 | )
14 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "bracketSpacing": true,
3 | "quoteProps": "as-needed",
4 | "semi": false,
5 | "tabWidth": 4,
6 | "singleQuote": true,
7 | "printWidth": 100,
8 | "bracketSameLine": true,
9 | "arrowParens": "always",
10 | "endOfLine": "lf",
11 | "htmlWhitespaceSensitivity": "ignore",
12 | "jsxSingleQuote": false,
13 | "trailingComma": "all",
14 | "useTabs": false
15 | }
16 |
--------------------------------------------------------------------------------
/env.d.ts:
--------------------------------------------------------------------------------
1 | declare global {
2 | interface ImportMeta {
3 | env: {
4 | NODE_ENV: 'production' | 'development'
5 | PROD: boolean
6 | DEV: boolean
7 | }
8 | }
9 | namespace NodeJS {
10 | interface ProcessEnv {
11 | NODE_ENV: 'production' | 'development'
12 | PROD: boolean
13 | DEV: boolean
14 | }
15 | }
16 | }
17 |
18 | export {}
19 |
--------------------------------------------------------------------------------
/dev/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Solid App
9 |
10 |
11 |
12 | You need to enable JavaScript to run this app.
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/dev/utils/index.ts:
--------------------------------------------------------------------------------
1 | import { ChartData, ChartDataset } from 'chart.js'
2 |
3 | export const generateRandomDataset = (labels: string[], index: number) => ({
4 | label: 'Dataset ' + index,
5 | data: labels.map(() => Math.random() * 200 - 100),
6 | })
7 |
8 | export const generateRandomChartData = (datasetsLength = 2): ChartData => {
9 | const labels = [
10 | 'January',
11 | 'February',
12 | 'March',
13 | 'April',
14 | 'May',
15 | 'June',
16 | 'July',
17 | 'August',
18 | 'September',
19 | 'October',
20 | 'November',
21 | 'December',
22 | ]
23 | const datasets: ChartDataset[] = []
24 |
25 | for (let i = 0; i < datasetsLength; i++) {
26 | datasets.push(generateRandomDataset(labels, i + 1))
27 | }
28 |
29 | return {
30 | labels,
31 | datasets,
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "strict": true,
5 | "target": "ESNext",
6 | "module": "ESNext",
7 | "moduleResolution": "node",
8 | "allowSyntheticDefaultImports": true,
9 | "esModuleInterop": true,
10 | "jsx": "preserve",
11 | "jsxImportSource": "solid-js",
12 | "types": ["vite/client"],
13 | "useDefineForClassFields": true,
14 | "noImplicitAny": false,
15 | "isolatedModules": true,
16 | "declaration": true,
17 | "noEmit": false,
18 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
19 | "allowJs": true,
20 | "skipLibCheck": false,
21 | "forceConsistentCasingInFileNames": true,
22 | "resolveJsonModule": true,
23 | "paths": {
24 | "@/*": ["src/*"]
25 | }
26 | },
27 | "include": ["src", "**/*.ts", "**/*.tsx", "index.js"],
28 | "references": [
29 | {
30 | "path": "./tsconfig.node.json"
31 | }
32 | ],
33 | "exclude": ["node_modules"]
34 | }
35 |
--------------------------------------------------------------------------------
/dev/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import solidPlugin from 'vite-plugin-solid'
3 |
4 | export default defineConfig({
5 | plugins: [
6 | solidPlugin(),
7 | {
8 | name: 'Replace env variables',
9 | transform(code, id) {
10 | if (id.includes('node_modules')) {
11 | return code
12 | }
13 | return code
14 | .replace(/process\.env\.SSR/g, 'false')
15 | .replace(/process\.env\.DEV/g, 'true')
16 | .replace(/process\.env\.PROD/g, 'false')
17 | .replace(/process\.env\.NODE_ENV/g, '"development"')
18 | .replace(/import\.meta\.env\.SSR/g, 'false')
19 | .replace(/import\.meta\.env\.DEV/g, 'true')
20 | .replace(/import\.meta\.env\.PROD/g, 'false')
21 | .replace(/import\.meta\.env\.NODE_ENV/g, '"development"')
22 | },
23 | },
24 | ],
25 | server: {
26 | port: 3000,
27 | },
28 | build: {
29 | target: 'esnext',
30 | },
31 | })
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Vyacheslav
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 |
--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------
1 | import type { Ref } from '@solid-primitives/refs'
2 | import type { ChartData, ChartOptions, Plugin, ChartTypeRegistry } from 'chart.js'
3 | import type { JSXElement } from 'solid-js'
4 |
5 | /**
6 | * Chart props
7 | */
8 | export interface ChartProps {
9 | /**
10 | * Chart.js chart type
11 | */
12 | type: keyof ChartTypeRegistry
13 | /**
14 | * The data object that is passed into the Chart.js chart
15 | * @see https://www.chartjs.org/docs/latest/getting-started/
16 | */
17 | data?: ChartData
18 | /**
19 | * The options object that is passed into the Chart.js chart
20 | * @see https://www.chartjs.org/docs/latest/general/options.html
21 | * @default {}
22 | */
23 | options?: ChartOptions
24 | /**
25 | * The plugins array that is passed into the Chart.js chart
26 | * @see https://www.chartjs.org/docs/latest/developers/plugins.html
27 | * @default []
28 | */
29 | plugins?: Plugin[]
30 | /**
31 | * The width of the canvas element
32 | * @default 512
33 | */
34 | width?: number | undefined
35 | /**
36 | * The height of the canvas element
37 | * @default 512
38 | */
39 | height?: number | undefined
40 | /**
41 | * The fallback element to render when the canvas cannot be rendered.
42 | * @default null
43 | */
44 | fallback?: JSXElement | null
45 | /**
46 | * Support for any other Chart.js options
47 | * @default {}
48 | */
49 | [key: string]: any
50 | /**
51 | * A ref to the Chart.js instance
52 | * @default null
53 | */
54 | ref?: Ref
55 | }
56 |
--------------------------------------------------------------------------------
/src/typedCharts.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Chart,
3 | LineController,
4 | BarController,
5 | RadarController,
6 | DoughnutController,
7 | PolarAreaController,
8 | BubbleController,
9 | PieController,
10 | ScatterController,
11 | CategoryScale,
12 | LinearScale,
13 | RadialLinearScale,
14 | PointElement,
15 | LineElement,
16 | BarElement,
17 | ArcElement,
18 | } from 'chart.js'
19 | import DefaultChart from './chart'
20 | import type { ChartProps } from './types'
21 | import type { ChartType, ChartComponentLike } from 'chart.js'
22 |
23 | export type TypedChartProps = Omit
24 |
25 | function createTypedChart(type: T, registerables: ChartComponentLike) {
26 | Chart.register(registerables)
27 | return (props: TypedChartProps) =>
28 | }
29 |
30 | export const Line = /* #__PURE__ */ createTypedChart('line', [
31 | LineController,
32 | CategoryScale,
33 | LinearScale,
34 | PointElement,
35 | LineElement,
36 | ])
37 | export const Bar = /* #__PURE__ */ createTypedChart('bar', [
38 | BarController,
39 | CategoryScale,
40 | BarElement,
41 | ])
42 | export const Radar = /* #__PURE__ */ createTypedChart('radar', [RadarController, RadialLinearScale])
43 | export const Doughnut = /* #__PURE__ */ createTypedChart('doughnut', [
44 | DoughnutController,
45 | ArcElement,
46 | ])
47 | export const PolarArea = /* #__PURE__ */ createTypedChart('polarArea', PolarAreaController)
48 | export const Bubble = /* #__PURE__ */ createTypedChart('bubble', BubbleController)
49 | export const Pie = /* #__PURE__ */ createTypedChart('pie', PieController)
50 | export const Scatter = /* #__PURE__ */ createTypedChart('scatter', ScatterController)
51 |
--------------------------------------------------------------------------------
/dev/pages/TypedChart.tsx:
--------------------------------------------------------------------------------
1 | import 'chart.js/auto'
2 | import { ChartData } from 'chart.js'
3 | import { createSignal } from 'solid-js'
4 | import { createStore } from 'solid-js/store'
5 | import { Line } from '../../src'
6 | import styles from '../styles/App.module.css'
7 | import { generateRandomChartData } from '../utils'
8 | import type { Component } from 'solid-js'
9 |
10 | const TypedChartPage: Component = () => {
11 | const [chartData] = createSignal(generateRandomChartData())
12 | const [chartConfig] = createStore({
13 | width: 700,
14 | height: 400,
15 | })
16 |
17 | const fallback = () => {
18 | return (
19 |
20 |
Chart is not available
21 |
22 | )
23 | }
24 |
25 | return (
26 |
49 | )
50 | }
51 |
52 | export default TypedChartPage
53 |
--------------------------------------------------------------------------------
/vitest.config.ts:
--------------------------------------------------------------------------------
1 | import solidPlugin from 'vite-plugin-solid'
2 | import { defineConfig } from 'vitest/config'
3 |
4 | export default defineConfig(({ mode }) => {
5 | // to test in server environment, run with "--mode ssr" or "--mode test:ssr" flag
6 | // loads only server.test.ts file
7 | const testSSR = mode === 'test:ssr' || mode === 'ssr'
8 |
9 | return {
10 | plugins: [
11 | solidPlugin({
12 | // https://github.com/solidjs/solid-refresh/issues/29
13 | hot: false,
14 | // For testing SSR we need to do a SSR JSX transform
15 | solid: { generate: testSSR ? 'ssr' : 'dom' },
16 | }),
17 | ],
18 | test: {
19 | watch: false,
20 | isolate: !testSSR,
21 | env: {
22 | NODE_ENV: testSSR ? 'production' : 'development',
23 | DEV: testSSR ? '' : '1',
24 | SSR: testSSR ? '1' : '',
25 | PROD: testSSR ? '1' : '',
26 | },
27 | environment: testSSR ? 'node' : 'jsdom',
28 | deps: {
29 | inline: ['vitest-canvas-mock'],
30 | },
31 | coverage: {
32 | reporter: ['lcovonly', 'text'],
33 | },
34 | transformMode: { web: [/\.[jt]sx$/] },
35 | ...(testSSR
36 | ? {
37 | include: ['test/server.test.{ts,tsx}'],
38 | }
39 | : {
40 | include: ['test/*.test.{ts,tsx}'],
41 | exclude: ['test/server.test.{ts,tsx}'],
42 | }),
43 | },
44 | resolve: {
45 | conditions: testSSR ? ['node'] : ['browser', 'development'],
46 | },
47 | }
48 | })
49 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "es2021": true
5 | },
6 | "extends": [
7 | "prettier",
8 | "eslint:recommended",
9 | "plugin:@typescript-eslint/recommended",
10 | "plugin:solid/typescript",
11 | "plugin:import/typescript",
12 | "plugin:import/recommended"
13 | ],
14 | "parser": "@typescript-eslint/parser",
15 | "parserOptions": {
16 | "ecmaVersion": "latest",
17 | "sourceType": "module",
18 | "ecmaFeatures": {
19 | "jsx": true
20 | }
21 | },
22 | "plugins": ["@typescript-eslint", "solid", "import"],
23 | "rules": {
24 | "solid/reactivity": "warn",
25 | "solid/no-destructure": "warn",
26 | "solid/jsx-no-undef": "error",
27 | "indent": ["error", 4],
28 | "quotes": ["error", "single"],
29 | "semi": ["error", "never"],
30 | "arrow-body-style": "off",
31 | "import/no-unresolved": "error",
32 | "import/extensions": [
33 | "error",
34 | "ignorePackages",
35 | {
36 | "js": "never",
37 | "jsx": "never",
38 | "ts": "never",
39 | "tsx": "never"
40 | }
41 | ],
42 | "import/order": [
43 | "error",
44 | {
45 | "groups": [
46 | "builtin",
47 | "external",
48 | "parent",
49 | "sibling",
50 | "index",
51 | "object",
52 | "type"
53 | ],
54 | "pathGroups": [
55 | {
56 | "pattern": "@/**/**",
57 | "group": "parent",
58 | "position": "before"
59 | }
60 | ],
61 | "alphabetize": {
62 | "order": "asc"
63 | }
64 | }
65 | ]
66 | },
67 | "settings": {
68 | "import/resolver": {
69 | "typescript": "true",
70 | "node": "true"
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/dev/pages/Registerables.tsx:
--------------------------------------------------------------------------------
1 | import { Chart, ChartData, Title, Tooltip, Legend, Colors } from 'chart.js'
2 | import { createSignal, onMount } from 'solid-js'
3 | import { createStore } from 'solid-js/store'
4 | import { DefaultChart } from '../../src'
5 | import styles from '../styles/App.module.css'
6 | import { generateRandomChartData } from '../utils'
7 | import type { Component } from 'solid-js'
8 |
9 | const RegisterablesPage: Component = () => {
10 | const [refBar, setRefBar] = createSignal()
11 | const [chartData] = createSignal(generateRandomChartData())
12 | const [chartConfig] = createStore({
13 | width: 700,
14 | height: 400,
15 | })
16 |
17 | const fallback = () => {
18 | return (
19 |
20 |
Chart is not available
21 |
22 | )
23 | }
24 |
25 | onMount(() => {
26 | Chart.register(Title, Tooltip, Legend, Colors)
27 | console.debug('[Chart Ref]: Bar', refBar())
28 | })
29 |
30 | return (
31 |
56 | )
57 | }
58 |
59 | export default RegisterablesPage
60 |
--------------------------------------------------------------------------------
/scripts/prepareCMD.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # create a vairable to hold a passed in argument
4 | # this argument is the next release version
5 | # this is passed in from the .releaserc file
6 |
7 | sudo apt-get install -y jq
8 |
9 | nextReleaseVersion=$1
10 | TARGET_KEY="version"
11 |
12 | # parse all letters a-z and A-Z and replace with nothing
13 | # this will remove all letters from the version string
14 | # this is to ensure that the version string is a valid semver
15 |
16 | # check if there is a letter in the version string
17 | # if there is a letter, then remove it
18 | # if there is no letter, then do nothing
19 | if [[ $nextReleaseVersion =~ [a-zA-Z] ]]; then
20 | nextReleaseVersion=$(echo $nextReleaseVersion | sed 's/[a-zA-Z]//g')
21 |
22 | # check if there is a dash in the version string
23 | # if there is a dash, then replace it with a dot
24 | # if there is no dash, then do nothing
25 | if [[ $nextReleaseVersion =~ "-" ]]; then
26 | # parse all dashes and replace with dots
27 | # this is to ensure that the version string is a valid semver
28 | nextReleaseVersion=$(echo $nextReleaseVersion | sed 's/-/./g')
29 |
30 | # remove everything after the third dot and the dot itself
31 | # this is to ensure that the version string is a valid semver
32 | nextReleaseVersion=$(echo $nextReleaseVersion | sed 's/\.[0-9]*$//g')
33 | # remove the last dot
34 | nextReleaseVersion=$(echo $nextReleaseVersion | sed 's/\.$//g')
35 | fi
36 | fi
37 |
38 | # print the next release version
39 |
40 | printf "[prepareCMD.sh]: Next version: ${nextReleaseVersion}\n"
41 |
42 | # This script is used to execute the prepareCMD.sh script on the remote host
43 | printf "[prepareCMD.sh]: Executing prepareCMD.sh on remote host \n"
44 |
45 | printf "[prepareCMD.sh]: Updating the version in the package.json file \n"
46 |
47 | # make a temp file
48 | tmp=$(mktemp)
49 |
50 | jq --arg a "$nextReleaseVersion" '.version = $a' ./package.json > "$tmp" && mv "$tmp" ./package.json -f
51 |
52 | # Tar zip the entire project
53 |
54 | printf "[prepareCMD.sh]: Zipping the project \n"
55 |
56 | tar -czvf solid-chartjs.tar.gz ./dist
57 |
58 | printf "[prepareCMD.sh]: Done, continuing with release. \n"
--------------------------------------------------------------------------------
/dev/App.tsx:
--------------------------------------------------------------------------------
1 | import { createSignal, For, Switch, Match } from 'solid-js'
2 | import DefaultChartPage from './pages/DefaultChart'
3 | import RegisterablesPage from './pages/Registerables'
4 | import TypedChartPage from './pages/TypedChart'
5 | import styles from './styles/App.module.css'
6 | import type { Component } from 'solid-js'
7 |
8 | type DemoPage = 'DefaultChart.tsx' | 'TypedChart.tsx' | 'Registerables.tsx'
9 |
10 | const App: Component = () => {
11 | const demoPages: DemoPage[] = ['DefaultChart.tsx', 'TypedChart.tsx', 'Registerables.tsx']
12 |
13 | const [demoPage, setDemoPage] = createSignal(demoPages[0])
14 |
15 | const onDemoPageSelect = (event: any) => {
16 | setDemoPage(event.target.value as DemoPage)
17 | }
18 |
19 | return (
20 | <>
21 |
22 |
27 |
28 | {(page) => (
29 |
30 | {page}
31 |
32 | )}
33 |
34 |
35 |
36 | or{' '}
37 |
43 | view source
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | >
59 | )
60 | }
61 |
62 | export default App
63 |
--------------------------------------------------------------------------------
/dev/styles/App.module.css:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | height: 100%;
4 | margin: 0;
5 | padding: 0;
6 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu',
7 | 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
8 | -webkit-font-smoothing: antialiased;
9 | -moz-osx-font-smoothing: grayscale;
10 | }
11 |
12 | .container {
13 | text-align: center;
14 | position: absolute;
15 | top: 50%;
16 | left: 50%;
17 | transform: translate(-50%, -50%);
18 | }
19 |
20 | .chart {
21 | /*height: 400px;*/
22 | /*width: 700px;*/
23 | }
24 |
25 | .buttonGroup {
26 | justify-content: center;
27 | margin-top: 20px;
28 | }
29 |
30 | .buttonGroup button {
31 | margin: 0 8px;
32 | }
33 |
34 | .inputGroup {
35 | justify-content: center;
36 | margin-top: 20px;
37 | }
38 |
39 | .inputGroup input {
40 | margin: 0 8px;
41 | }
42 |
43 | .header {
44 | background-color: #ffffff;
45 | min-height: 100vh;
46 | display: flex;
47 | flex-direction: column;
48 | align-items: center;
49 | justify-content: center;
50 | font-size: calc(10px + 2vmin);
51 | color: white;
52 | }
53 |
54 | button {
55 | transition: background 0.25s, border-color 0.25s;
56 | background: rgba(40, 44, 52, 0.05);
57 | border: 1px solid transparent;
58 | border-radius: 6px;
59 | color: #3080d0;
60 | text-decoration: none !important;
61 | display: inline-block;
62 | font-size: 0.8rem;
63 | padding: 8px 16px;
64 | margin: 0 8px 8px 0;
65 | cursor: pointer;
66 | -webkit-user-select: none;
67 | -moz-user-select: none;
68 | user-select: none;
69 | }
70 |
71 | button:hover {
72 | background: rgba(40, 44, 52, 0.1);
73 | border-color: #3080d0;
74 | }
75 |
76 | .inputField {
77 | transition: background 0.25s, border-color 0.25s;
78 | background: rgba(40, 44, 52, 0.05);
79 | border: 1px solid transparent;
80 | border-radius: 6px;
81 | color: #3080d0;
82 | font-size: 0.8rem;
83 | padding: 8px 16px;
84 | margin: 0 8px 8px 0;
85 | }
86 |
87 | .inputField:hover,
88 | .inputField:focus {
89 | background: rgba(40, 44, 52, 0.1);
90 | border-color: #3080d0;
91 | outline: none;
92 | }
93 |
94 | .selectField {
95 | -moz-appearance: none;
96 | -webkit-appearance: none;
97 | appearance: none;
98 | transition: background 0.25s, border-color 0.25s;
99 | background: rgba(40, 44, 52, 0.05);
100 | border: 1px solid transparent;
101 | border-radius: 6px;
102 | color: #3080d0;
103 | font-size: 0.8rem;
104 | padding: 8px 16px;
105 | }
106 |
107 | .selectField:hover,
108 | .selectField:focus {
109 | background: rgba(40, 44, 52, 0.1);
110 | border-color: #3080d0;
111 | outline: none;
112 | }
113 |
114 | .selectContainer {
115 | padding: 3vh;
116 | display: flex;
117 | align-items: center;
118 | justify-content: center;
119 | }
120 |
121 | .link {
122 | color: #3080d0;
123 | text-decoration: none;
124 | transition: ease-in 0.25s;
125 | position: relative;
126 | display: inline-block;
127 | }
128 |
129 | .link:hover,
130 | .link:focus {
131 | color: #183a5c;
132 | }
133 |
--------------------------------------------------------------------------------
/src/chart.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-non-null-assertion */
2 | import { mergeRefs } from '@solid-primitives/refs'
3 | import { Chart, ChartData, ChartItem, ChartOptions, Plugin } from 'chart.js'
4 | import { createEffect, mergeProps, on, onCleanup, onMount, createSignal } from 'solid-js'
5 | import { unwrap } from 'solid-js/store'
6 | import { ChartProps } from './types'
7 |
8 | export default function DefaultChart(props: ChartProps) {
9 | const [canvasRef, setCanvasRef] = createSignal()
10 | const [chart, setChart] = createSignal()
11 |
12 | const merged = mergeProps(
13 | {
14 | width: 512,
15 | height: 512,
16 | type: 'line',
17 | data: {} as ChartData,
18 | options: { responsive: true } as ChartOptions,
19 | plugins: [] as Plugin[],
20 | },
21 | props,
22 | )
23 |
24 | const init = () => {
25 | const ctx = canvasRef()?.getContext('2d') as ChartItem
26 | const config = unwrap(merged)
27 |
28 | if (config.type !== 'radar') {
29 | //* remove the RadialLinearScale from the chart's scales
30 | if (config.options.scales?.r) {
31 | console.debug('[Solid-ChartJS]: Removing un-needed RadialLinearScale')
32 | delete config.options.scales?.r
33 | }
34 | }
35 |
36 | const chart = new Chart(ctx, {
37 | type: config.type,
38 | data: config.data,
39 | options: config.options,
40 | plugins: config.plugins,
41 | })
42 | setChart(chart)
43 | }
44 |
45 | onMount(() => {
46 | init()
47 | })
48 |
49 | createEffect(
50 | on(
51 | () => merged.data,
52 | () => {
53 | chart()!.data = merged.data
54 | chart()!.update()
55 | },
56 | {
57 | defer: true,
58 | },
59 | ),
60 | )
61 |
62 | createEffect(
63 | on(
64 | () => merged.options,
65 | () => {
66 | chart()!.options = merged.options
67 | chart()!.update()
68 | },
69 | {
70 | defer: true,
71 | },
72 | ),
73 | )
74 |
75 | createEffect(
76 | on(
77 | [() => merged.width, () => merged.height],
78 | () => {
79 | chart()!.resize(merged.width, merged.height)
80 | },
81 | {
82 | defer: true,
83 | },
84 | ),
85 | )
86 |
87 | createEffect(
88 | on(
89 | () => merged.type,
90 | () => {
91 | // save the chart's dimensions
92 | const dimensions = [chart()!.width, chart()!.height]
93 |
94 | chart()!.destroy()
95 | init()
96 |
97 | // restore the chart's dimensions before destroying
98 | chart()!.resize(...dimensions)
99 | },
100 | {
101 | defer: true,
102 | },
103 | ),
104 | )
105 |
106 | onCleanup(() => {
107 | chart()?.destroy()
108 | mergeRefs(props.ref, null)
109 | })
110 |
111 | return (
112 | setCanvasRef(el))}
114 | height={merged.height}
115 | width={merged.width}>
116 | {merged.fallback}
117 |
118 | )
119 | }
120 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "solid-chartjs",
3 | "type": "module",
4 | "version": "1.3.11",
5 | "description": "SolidJS components for Chart.js",
6 | "license": "MIT",
7 | "repository": {
8 | "type": "git",
9 | "url": "https://github.com/s0ftik3/solid-chartjs.git"
10 | },
11 | "bugs": {
12 | "url": "https://github.com/s0ftik3/solid-chartjs/issues"
13 | },
14 | "files": [
15 | "dist"
16 | ],
17 | "private": false,
18 | "sideEffects": false,
19 | "exports": {
20 | "solid": {
21 | "development": "./dist/index.dev.jsx",
22 | "import": "./dist/index.jsx"
23 | },
24 | "browser": {
25 | "development": {
26 | "require": "./dist/index.dev.cjs",
27 | "import": {
28 | "types": "./dist/index.d.ts",
29 | "default": "./dist/index.dev.js"
30 | }
31 | },
32 | "require": "./dist/index.cjs",
33 | "import": {
34 | "types": "./dist/index.d.ts",
35 | "default": "./dist/index.js"
36 | }
37 | },
38 | "development": {
39 | "require": "./dist/index.dev.cjs",
40 | "import": {
41 | "types": "./dist/index.d.ts",
42 | "default": "./dist/index.dev.js"
43 | }
44 | },
45 | "require": "./dist/index.cjs",
46 | "import": {
47 | "types": "./dist/index.d.ts",
48 | "default": "./dist/index.js"
49 | }
50 | },
51 | "main": "./dist/index.cjs",
52 | "module": "./dist/index.js",
53 | "types": "./dist/index.d.ts",
54 | "browser": {},
55 | "typesVersions": {},
56 | "scripts": {
57 | "dev": "vite serve dev",
58 | "build": "tsup",
59 | "test": "concurrently pnpm:test:*",
60 | "test:client": "vitest",
61 | "test:ssr": "pnpm run test:client --mode ssr",
62 | "prepublishOnly": "pnpm build",
63 | "format": "prettier --ignore-path .gitignore -w \"src/**/*.{js,ts,json,css,tsx,jsx}\" \"dev/**/*.{js,ts,json,css,tsx,jsx}\"",
64 | "lint": "eslint --ext .js,.ts,.jsx,.tsx src",
65 | "format:eslint": "pnpm run lint --fix & pnpm prettier --write \"src/**/*.{js,jsx,ts,tsx}\"",
66 | "update-deps": "pnpm up -Li",
67 | "typecheck": "tsc --noEmit"
68 | },
69 | "peerDependencies": {
70 | "chart.js": "^4.3.0",
71 | "solid-js": "^1.7.5"
72 | },
73 | "devDependencies": {
74 | "@babel/core": "^7.21.8",
75 | "@babel/preset-env": "^7.21.5",
76 | "@rollup/plugin-node-resolve": "^15.0.2",
77 | "@size-limit/preset-big-lib": "^8.2.4",
78 | "@swc/core": "^1.3.58",
79 | "@swc/helpers": "^0.5.1",
80 | "@testing-library/jest-dom": "^5.16.5",
81 | "@types/node": "^20.1.4",
82 | "@types/testing-library__jest-dom": "^5.14.5",
83 | "@typescript-eslint/eslint-plugin": "^5.59.6",
84 | "@typescript-eslint/parser": "^5.59.6",
85 | "@vitest/coverage-c8": "^0.31.0",
86 | "@solid-primitives/refs": "^1.0.3",
87 | "babel-loader": "^9.1.2",
88 | "browserslist": "^4.21.5",
89 | "chart.js": "^4.3.0",
90 | "chartjs-adapter-date-fns": "^3.0.0",
91 | "chartjs-plugin-annotation": "^3.0.1",
92 | "chartjs-plugin-zoom": "^2.0.1",
93 | "concurrently": "^8.0.1",
94 | "esbuild": "^0.17.19",
95 | "esbuild-plugin-solid": "^0.5.0",
96 | "eslint": "^8.40.0",
97 | "eslint-config-google": "^0.14.0",
98 | "eslint-config-prettier": "^8.8.0",
99 | "eslint-config-standard": "^17.0.0",
100 | "eslint-config-standard-react": "^13.0.0",
101 | "eslint-import-resolver-typescript": "^3.5.5",
102 | "eslint-plugin-autofix": "^1.1.0",
103 | "eslint-plugin-import": "^2.27.5",
104 | "eslint-plugin-node": "^11.1.0",
105 | "eslint-plugin-prettier": "^4.2.1",
106 | "eslint-plugin-promise": "^6.1.1",
107 | "eslint-plugin-solid": "^0.12.1",
108 | "jsdom": "^22.0.0",
109 | "prettier": "^2.8.8",
110 | "solid-js": "^1.7.5",
111 | "tsd": "^0.28.1",
112 | "tsup": "^6.5.0",
113 | "tsup-preset-solid": "^0.0.6",
114 | "typescript": "^5.0.4",
115 | "vite": "^4.3.6",
116 | "vite-plugin-solid": "^2.7.0",
117 | "vitest": "^0.31.0",
118 | "vitest-canvas-mock": "^0.2.2"
119 | },
120 | "keywords": [
121 | "solid",
122 | "solidjs",
123 | "solid-js",
124 | "solid js",
125 | "chart",
126 | "chart-js",
127 | "chart.js",
128 | "solid-chartjs-2",
129 | "solidjs-chartjs-2",
130 | "solid chart.js",
131 | "solidjs chart.js",
132 | "solid-chart.js"
133 | ],
134 | "packageManager": "pnpm@7.22.0"
135 | }
136 |
--------------------------------------------------------------------------------
/dev/pages/DefaultChart.tsx:
--------------------------------------------------------------------------------
1 | import 'chart.js/auto'
2 | import { ChartData, ChartTypeRegistry } from 'chart.js'
3 | import { createSignal, For } from 'solid-js'
4 | import { createStore } from 'solid-js/store'
5 | import { DefaultChart } from '../../src'
6 | import styles from '../styles/App.module.css'
7 | import { generateRandomChartData, generateRandomDataset } from '../utils'
8 | import type { Component } from 'solid-js'
9 |
10 | const DefaultChartPage: Component = () => {
11 | const [chartData, setChartData] = createSignal(generateRandomChartData())
12 | const [chartConfig, setChartConfig] = createStore({
13 | width: 700,
14 | height: 400,
15 | })
16 |
17 | const chartTypes: (keyof ChartTypeRegistry)[] = [
18 | 'line',
19 | 'bar',
20 | 'doughnut',
21 | 'radar',
22 | 'polarArea',
23 | 'bubble',
24 | 'pie',
25 | 'scatter',
26 | ]
27 | const [chartType, setChartType] = createSignal(chartTypes[0])
28 |
29 | const onRandomizeClick = () => {
30 | setChartData((prev) => generateRandomChartData(prev.datasets.length))
31 | }
32 | const onAddDatasetClick = () => {
33 | setChartData((prev) => {
34 | const datasets = prev.datasets
35 | datasets.push(generateRandomDataset(prev.labels as string[], prev.datasets.length + 1))
36 | return { ...prev, datasets }
37 | })
38 | }
39 | const onRemoveDatasetClick = () => {
40 | setChartData((prev) => {
41 | const datasets = prev.datasets
42 | datasets.pop()
43 | return { ...prev, datasets }
44 | })
45 | }
46 |
47 | const onDimensionsInput = (type: 'width' | 'height', event: any) => {
48 | setChartConfig(type, () => +event.target.value)
49 | }
50 |
51 | const onTypeSelect = (event: any) => {
52 | setChartType(event.target.value as keyof ChartTypeRegistry)
53 | }
54 |
55 | const fallback = () => {
56 | return (
57 |
58 |
Chart is not available
59 |
60 | )
61 | }
62 |
63 | return (
64 |
65 |
66 |
86 |
87 |
88 |
89 |
90 | {(type) => (
91 |
92 | {type}
93 |
94 | )}
95 |
96 |
97 | onDimensionsInput('width', event)}
103 | />
104 | onDimensionsInput('height', event)}
110 | />
111 |
112 |
113 | Randomize
114 | Add Dataset
115 | Remove Dataset
116 |
117 |
118 | )
119 | }
120 |
121 | export default DefaultChartPage
122 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # solid-chartjs
6 |
7 |
8 |
9 | [](https://npmjs.com/package/solid-chartjs)
10 | [](https://www.npmjs.com/package/solid-chartjs)
11 | [](https://t.me/solid_chartjs)
12 |
13 | The `solid-chartjs` library is a SolidJS wrapper around the [`Chart.js`](https://www.chartjs.org) library, allowing you to easily create interactive charts in your SolidJS applications.
14 |
15 | - [Quick start](#quick-start)
16 | - [Chart Props](#chart-props)
17 | - [Examples](#examples)
18 | - [Credits](#credits)
19 | - [Contributing](#contributing)
20 | - [Contribution Guidelines](#contribution-guidelines)
21 | - [Code and Commit Standards](#code-and-commit-standards)
22 | - [License](#license)
23 |
24 | ## Quick start
25 |
26 | Installation:
27 |
28 | ```bash
29 | pnpm add solid-chartjs chart.js
30 | # or
31 | yarn add solid-chartjs chart.js
32 | # or
33 | npm i solid-chartjs chart.js
34 | ```
35 |
36 | Demo: [solid-chartjs.vychs.com](https://solid-chartjs.vychs.com/)
37 |
38 | Usage:
39 |
40 | ```tsx
41 | import { onMount } from 'solid-js'
42 | import { Chart, Title, Tooltip, Legend, Colors } from 'chart.js'
43 | import { Line } from 'solid-chartjs'
44 |
45 | const MyChart = () => {
46 | /**
47 | * You must register optional elements before using the chart,
48 | * otherwise you will have the most primitive UI
49 | */
50 | onMount(() => {
51 | Chart.register(Title, Tooltip, Legend, Colors)
52 | })
53 |
54 | const chartData = {
55 | labels: ['January', 'February', 'March', 'April', 'May'],
56 | datasets: [
57 | {
58 | label: 'Sales',
59 | data: [50, 60, 70, 80, 90],
60 | },
61 | ],
62 | }
63 | const chartOptions = {
64 | responsive: true,
65 | maintainAspectRatio: false,
66 | }
67 |
68 | return (
69 |
70 |
71 |
72 | )
73 | }
74 | ```
75 |
76 | If you don't want to import and register the controllers, elements, scales, and plugins you want to use, you can use the following solution:
77 |
78 | > [!NOTE]\
79 | > It is considered to better use the tree-shakable way, to decrease the bundle size.
80 |
81 | ```tsx
82 | import 'chart.js/auto'
83 | import { DefaultChart } from 'solid-chartjs'
84 |
85 |
86 | ```
87 |
88 | ## Chart Props
89 |
90 | | Prop | Description | Type |
91 | | -------- | ----------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
92 | | width | The width of the chart canvas in pixels. | number \| undefined |
93 | | height | The height of the chart canvas in pixels. | number \| undefined |
94 | | fallback | A fallback element to display when chart fails. | JSX.Element |
95 | | type | The type of the chart. | keyof [ChartTypeRegistry](https://www.chartjs.org/docs/latest/api/interfaces/ChartTypeRegistry.html) |
96 | | data | The chart data object. | [ChartData](https://www.chartjs.org/docs/latest/api/interfaces/ChartData.html) \| undefined |
97 | | options | The chart options object. | [ChartOptions](https://www.chartjs.org/docs/latest/api/interfaces/CoreChartOptions.html) \| undefined |
98 | | plugins | The chart plugins object. | [Plugin](https://www.chartjs.org/docs/latest/api/interfaces/Plugin.html)[] \| undefined |
99 |
100 | ## Examples
101 |
102 | Check out `/dev` folder and run the SolidJs application to see how it works.
103 |
104 | You can also use the `DefaultChart` components:
105 |
106 | > [!NOTE]\
107 | > `DefaultChart` is a wrapper around `Chart` component, so you can use all the props from `Chart` component.
108 | > `DefaultChart` component does _not_ have its registrable elements registered by default, so you need to register them yourself unless you use `chart.js/auto`.
109 |
110 | ```tsx
111 | import { onMount } from 'solid-js'
112 | import {
113 | Chart,
114 | LineController,
115 | CategoryScale,
116 | PointElement,
117 | LineElement,
118 | LinearScale,
119 | } from 'chart.js'
120 | import { DefaultChart } from 'solid-chartjs'
121 |
122 | const MyChart = () => {
123 | onMount(() => {
124 | Chart.register(LineController, CategoryScale, PointElement, LineElement, LinearScale)
125 | })
126 |
127 | // ... your data and options objects
128 |
129 | return
130 | }
131 | ```
132 |
133 | Usage of `fallback` prop:
134 |
135 | ```tsx
136 | const fallback = () => {
137 | return (
138 |
139 |
Chart is not available
140 |
141 | )
142 | }
143 |
144 |
145 | ```
146 |
147 | ## Credits
148 |
149 | - This library is _heavily_ inspired by [react-chartjs-2](https://react-chartjs-2.js.org/)
150 | - Awesome charting library [Chart.js](https://www.chartjs.org)
151 | - Flexible library for building user interfaces [SolidJs](https://www.solidjs.com/)
152 |
--------------------------------------------------------------------------------