diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts new file mode 100644 index 0000000..bfa7a35 --- /dev/null +++ b/docs/.vitepress/config.ts @@ -0,0 +1,61 @@ +import { defineConfig } from 'vitepress'; +import { name, description, repository, license, author } from '../../package.json'; +import typedocSidebar from '../api/typedoc-sidebar.json'; + +const cleanName = name.replace('@sgratzl/', ''); + +// https://vitepress.dev/reference/site-config +export default defineConfig({ + title: cleanName, + description, + base: `/${cleanName}/`, + useWebFonts: false, + themeConfig: { + // https://vitepress.dev/reference/default-theme-config + nav: [ + { text: 'Home', link: '/' }, + { text: 'Getting Started', link: '/getting-started' }, + { text: 'Examples', link: '/examples/' }, + { text: 'API', link: '/api/' }, + { text: 'Related Plugins', link: '/related' }, + ], + + sidebar: [ + { + text: 'Examples', + items: [ + { text: 'Basic', link: '/examples/' }, + { text: 'Dendrogram', link: '/examples/dendrogram' }, + { text: 'Tree', link: '/examples/tree' }, + { text: 'Force Directed Graph', link: '/examples/force' }, + { text: 'Tree with Labels', link: '/examples/label' }, + { text: 'Directed Tree', link: '/examples/directed' }, + { text: 'Tree Orientations', link: '/examples/orientation' }, + ], + }, + { + text: 'API', + collapsed: true, + items: typedocSidebar, + }, + ], + + socialLinks: [{ icon: 'github', link: repository.url.replace('.git', '') }], + + footer: { + message: `Released under the ${license} license.`, + copyright: `Copyright © 2019-present ${author.name}`, + }, + + editLink: { + pattern: `${repository.url.replace('.git', '')}/edit/main/docs/:path`, + }, + + search: { + provider: 'local', + }, + }, +}); diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts new file mode 100644 index 0000000..1b67129 --- /dev/null +++ b/docs/.vitepress/theme/index.ts @@ -0,0 +1,25 @@ +import Theme from 'vitepress/theme'; +import { createTypedChart } from 'vue-chartjs'; +import { Tooltip, LineElement, PointElement, LinearScale } from 'chart.js'; +import { DendrogramController, ForceDirectedGraphController, EdgeLine, TreeController } from '../../../src'; +import ChartPluginDataLabel from 'chartjs-plugin-datalabels'; + +export default { + ...Theme, + enhanceApp({ app }) { + const deps = [ + Tooltip, + LineElement, + PointElement, + DendrogramController, + ForceDirectedGraphController, + EdgeLine, + TreeController, + LinearScale, + ChartPluginDataLabel, + ]; + app.component('DendrogramChart', createTypedChart('dendrogram', deps)); + app.component('TreeChart', createTypedChart('tree', deps)); + app.component('ForceDirectedGraphChart', createTypedChart('forceDirectedGraph', deps)); + }, +}; diff --git a/docs/examples/dendrogram.md b/docs/examples/dendrogram.md new file mode 100644 index 0000000..1fd1e55 --- /dev/null +++ b/docs/examples/dendrogram.md @@ -0,0 +1,24 @@ +--- +title: Dendrogram +--- + +# Dendrogram + + + + + +### Code + +:::code-group + +<<< ./dendrogram.ts#config [config] + +<<< ./dendrogram.ts#data [data] + +::: diff --git a/docs/examples/dendrogram.ts b/docs/examples/dendrogram.ts new file mode 100644 index 0000000..c2cedd4 --- /dev/null +++ b/docs/examples/dendrogram.ts @@ -0,0 +1,31 @@ +import type { ChartConfiguration } from 'chart.js'; +import type {} from '../../src'; +import 'chartjs-plugin-datalabels'; + +// #region data +import nodes from './tree.json'; + +export const data: ChartConfiguration<'dendrogram'>['data'] = { + labels: nodes.map((d) => d.name), + datasets: [ + { + pointBackgroundColor: 'steelblue', + pointRadius: 5, + data: nodes.map((d) => Object.assign({}, d)), + }, + ], +}; +// #endregion data +// #region config +export const config: ChartConfiguration<'dendrogram'> = { + type: 'dendrogram', + data, + options: { + plugins: { + datalabels: { + display: false, + }, + }, + }, +}; +// #endregion config diff --git a/docs/examples/directed.md b/docs/examples/directed.md new file mode 100644 index 0000000..d90b040 --- /dev/null +++ b/docs/examples/directed.md @@ -0,0 +1,24 @@ +--- +title: Directed Tree +--- + +# Directed Tree + + + + + +### Code + +:::code-group + +<<< ./directed.ts#config [config] + +<<< ./directed.ts#data [data] + +::: diff --git a/docs/examples/directed.ts b/docs/examples/directed.ts new file mode 100644 index 0000000..b40f00f --- /dev/null +++ b/docs/examples/directed.ts @@ -0,0 +1,32 @@ +import type { ChartConfiguration } from 'chart.js'; +import type {} from '../../src'; +import 'chartjs-plugin-datalabels'; + +// #region data +import nodes from './tree.json'; + +export const data: ChartConfiguration<'tree'>['data'] = { + labels: nodes.map((d) => d.name), + datasets: [ + { + pointBackgroundColor: 'steelblue', + pointRadius: 5, + directed: true, + data: nodes.map((d) => Object.assign({}, d)), + }, + ], +}; +// #endregion data +// #region config +export const config: ChartConfiguration<'tree'> = { + type: 'tree', + data, + options: { + plugins: { + datalabels: { + display: false, + }, + }, + }, +}; +// #endregion config diff --git a/docs/examples/force.md b/docs/examples/force.md new file mode 100644 index 0000000..6dae2a5 --- /dev/null +++ b/docs/examples/force.md @@ -0,0 +1,24 @@ +--- +title: Force Directed Graph +--- + +# Force Directed Graph + + + + + +### Code + +:::code-group + +<<< ./force.ts#config [config] + +<<< ./force.ts#data [data] + +::: diff --git a/docs/examples/force.ts b/docs/examples/force.ts new file mode 100644 index 0000000..19d6eb1 --- /dev/null +++ b/docs/examples/force.ts @@ -0,0 +1,31 @@ +import type { ChartConfiguration } from 'chart.js'; +import type {} from '../../src'; +import 'chartjs-plugin-datalabels'; + +// #region data +import miserables from './miserables.json'; +export const data: ChartConfiguration<'forceDirectedGraph'>['data'] = { + labels: miserables.nodes.map((d) => d.id), + datasets: [ + { + pointBackgroundColor: 'steelblue', + pointRadius: 5, + data: miserables.nodes, + edges: miserables.links, + }, + ], +}; +// #endregion data +// #region config +export const config: ChartConfiguration<'forceDirectedGraph'> = { + type: 'forceDirectedGraph', + data, + options: { + plugins: { + datalabels: { + display: false, + }, + }, + }, +}; +// #endregion config diff --git a/docs/examples/index.md b/docs/examples/index.md new file mode 100644 index 0000000..6e9db63 --- /dev/null +++ b/docs/examples/index.md @@ -0,0 +1,62 @@ +--- +title: Examples +--- + +# Examples + + + +## Force Directed Graph + + + +### Code + +:::code-group + +<<< ./force.ts#config [config] + +<<< ./force.ts#data [data] + +::: + +## Dendrogram + + + +### Code + +:::code-group + +<<< ./dendrogram.ts#config [config] + +<<< ./dendrogram.ts#data [data] + +::: + +## Tree + + + +### Code + +:::code-group + +<<< ./tree.ts#tree [config] + +<<< ./tree.ts#data [data] + +::: diff --git a/docs/examples/label.md b/docs/examples/label.md new file mode 100644 index 0000000..6354735 --- /dev/null +++ b/docs/examples/label.md @@ -0,0 +1,41 @@ +--- +title: Tree with Labels +--- + +# Tree with Labels + + + + + +### Code + +:::code-group + +<<< ./label.ts#config [config] + +<<< ./label.ts#data [data] + +::: + +## Radial Tree with Labels + + + +### Code + +:::code-group + +<<< ./label.ts#radial [config] + +<<< ./label.ts#data [data] + +::: diff --git a/docs/examples/label.ts b/docs/examples/label.ts new file mode 100644 index 0000000..05c3f25 --- /dev/null +++ b/docs/examples/label.ts @@ -0,0 +1,91 @@ +import type { ChartConfiguration } from 'chart.js'; +import type {} from '../../src'; +import ChartDataLabels from 'chartjs-plugin-datalabels'; +// #region data +import nodes from './tree.json'; + +export const data: ChartConfiguration<'tree'>['data'] = { + labels: nodes.map((d) => d.name), + datasets: [ + { + pointBackgroundColor: ['#002838', '#ed7d00', '#395c6b', '#d94d15', '#889da6'], + pointRadius: 10, + data: nodes.map((d) => Object.assign({}, d)), + }, + ], +}; +// #endregion data +// #region config +export const config: ChartConfiguration<'tree'> = { + type: 'tree', + data, + options: { + tree: { + orientation: 'radial', + }, + layout: { + padding: { + left: 20, + top: 20, + bottom: 20, + right: 20, + }, + }, + plugins: { + legend: { + display: false, + }, + datalabels: { + // display: true, + align: 'right', + offset: 6, + formatter: function (value, context) { + return '' + value.name + ''; + }, + color: 'black', + backgroundColor: 'steelblue', + }, + }, + }, + plugins: [ChartDataLabels], +}; +// #endregion config + +// #region radial +export const radial: ChartConfiguration<'tree'> = { + type: 'tree', + data, + options: { + tree: { + orientation: 'radial', + }, + layout: { + padding: 40, + }, + plugins: { + datalabels: { + display: (context) => { + // const index = context.dataIndex; + // const value = context.dataset.data[index]; + return true; //value.children.length === 0; + }, + align: (context) => { + const index = context.dataIndex; + const value = context.dataset.data[index] as { angle: number }; + return (-value.angle / Math.PI) * 180; + }, + rotation: (context) => { + const index = context.dataIndex; + const value = context.dataset.data[index] as { angle: number }; + return (-value.angle / Math.PI) * 180; + }, + backgroundColor: 'white', + formatter: (v) => { + return v.name; + }, + }, + }, + }, + plugins: [ChartDataLabels], +}; +// #endregion radial diff --git a/docs/examples/miserables.json b/docs/examples/miserables.json new file mode 100644 index 0000000..046a058 --- /dev/null +++ b/docs/examples/miserables.json @@ -0,0 +1,337 @@ +{ + "nodes": [ + { "id": "Myriel", "group": 1 }, + { "id": "Napoleon", "group": 1 }, + { "id": "Mlle.Baptistine", "group": 1 }, + { "id": "Mme.Magloire", "group": 1 }, + { "id": "CountessdeLo", "group": 1 }, + { "id": "Geborand", "group": 1 }, + { "id": "Champtercier", "group": 1 }, + { "id": "Cravatte", "group": 1 }, + { "id": "Count", "group": 1 }, + { "id": "OldMan", "group": 1 }, + { "id": "Labarre", "group": 2 }, + { "id": "Valjean", "group": 2 }, + { "id": "Marguerite", "group": 3 }, + { "id": "Mme.deR", "group": 2 }, + { "id": "Isabeau", "group": 2 }, + { "id": "Gervais", "group": 2 }, + { "id": "Tholomyes", "group": 3 }, + { "id": "Listolier", "group": 3 }, + { "id": "Fameuil", "group": 3 }, + { "id": "Blacheville", "group": 3 }, + { "id": "Favourite", "group": 3 }, + { "id": "Dahlia", "group": 3 }, + { "id": "Zephine", "group": 3 }, + { "id": "Fantine", "group": 3 }, + { "id": "Mme.Thenardier", "group": 4 }, + { "id": "Thenardier", "group": 4 }, + { "id": "Cosette", "group": 5 }, + { "id": "Javert", "group": 4 }, + { "id": "Fauchelevent", "group": 0 }, + { "id": "Bamatabois", "group": 2 }, + { "id": "Perpetue", "group": 3 }, + { "id": "Simplice", "group": 2 }, + { "id": "Scaufflaire", "group": 2 }, + { "id": "Woman1", "group": 2 }, + { "id": "Judge", "group": 2 }, + { "id": "Champmathieu", "group": 2 }, + { "id": "Brevet", "group": 2 }, + { "id": "Chenildieu", "group": 2 }, + { "id": "Cochepaille", "group": 2 }, + { "id": "Pontmercy", "group": 4 }, + { "id": "Boulatruelle", "group": 6 }, + { "id": "Eponine", "group": 4 }, + { "id": "Anzelma", "group": 4 }, + { "id": "Woman2", "group": 5 }, + { "id": "MotherInnocent", "group": 0 }, + { "id": "Gribier", "group": 0 }, + { "id": "Jondrette", "group": 7 }, + { "id": "Mme.Burgon", "group": 7 }, + { "id": "Gavroche", "group": 8 }, + { "id": "Gillenormand", "group": 5 }, + { "id": "Magnon", "group": 5 }, + { "id": "Mlle.Gillenormand", "group": 5 }, + { "id": "Mme.Pontmercy", "group": 5 }, + { "id": "Mlle.Vaubois", "group": 5 }, + { "id": "Lt.Gillenormand", "group": 5 }, + { "id": "Marius", "group": 8 }, + { "id": "BaronessT", "group": 5 }, + { "id": "Mabeuf", "group": 8 }, + { "id": "Enjolras", "group": 8 }, + { "id": "Combeferre", "group": 8 }, + { "id": "Prouvaire", "group": 8 }, + { "id": "Feuilly", "group": 8 }, + { "id": "Courfeyrac", "group": 8 }, + { "id": "Bahorel", "group": 8 }, + { "id": "Bossuet", "group": 8 }, + { "id": "Joly", "group": 8 }, + { "id": "Grantaire", "group": 8 }, + { "id": "MotherPlutarch", "group": 9 }, + { "id": "Gueulemer", "group": 4 }, + { "id": "Babet", "group": 4 }, + { "id": "Claquesous", "group": 4 }, + { "id": "Montparnasse", "group": 4 }, + { "id": "Toussaint", "group": 5 }, + { "id": "Child1", "group": 10 }, + { "id": "Child2", "group": 10 }, + { "id": "Brujon", "group": 4 }, + { "id": "Mme.Hucheloup", "group": 8 } + ], + "links": [ + { "source": "Napoleon", "target": "Myriel", "value": 1 }, + { "source": "Mlle.Baptistine", "target": "Myriel", "value": 8 }, + { "source": "Mme.Magloire", "target": "Myriel", "value": 10 }, + { "source": "Mme.Magloire", "target": "Mlle.Baptistine", "value": 6 }, + { "source": "CountessdeLo", "target": "Myriel", "value": 1 }, + { "source": "Geborand", "target": "Myriel", "value": 1 }, + { "source": "Champtercier", "target": "Myriel", "value": 1 }, + { "source": "Cravatte", "target": "Myriel", "value": 1 }, + { "source": "Count", "target": "Myriel", "value": 2 }, + { "source": "OldMan", "target": "Myriel", "value": 1 }, + { "source": "Valjean", "target": "Labarre", "value": 1 }, + { "source": "Valjean", "target": "Mme.Magloire", "value": 3 }, + { "source": "Valjean", "target": "Mlle.Baptistine", "value": 3 }, + { "source": "Valjean", "target": "Myriel", "value": 5 }, + { "source": "Marguerite", "target": "Valjean", "value": 1 }, + { "source": "Mme.deR", "target": "Valjean", "value": 1 }, + { "source": "Isabeau", "target": "Valjean", "value": 1 }, + { "source": "Gervais", "target": "Valjean", "value": 1 }, + { "source": "Listolier", "target": "Tholomyes", "value": 4 }, + { "source": "Fameuil", "target": "Tholomyes", "value": 4 }, + { "source": "Fameuil", "target": "Listolier", "value": 4 }, + { "source": "Blacheville", "target": "Tholomyes", "value": 4 }, + { "source": "Blacheville", "target": "Listolier", "value": 4 }, + { "source": "Blacheville", "target": "Fameuil", "value": 4 }, + { "source": "Favourite", "target": "Tholomyes", "value": 3 }, + { "source": "Favourite", "target": "Listolier", "value": 3 }, + { "source": "Favourite", "target": "Fameuil", "value": 3 }, + { "source": "Favourite", "target": "Blacheville", "value": 4 }, + { "source": "Dahlia", "target": "Tholomyes", "value": 3 }, + { "source": "Dahlia", "target": "Listolier", "value": 3 }, + { "source": "Dahlia", "target": "Fameuil", "value": 3 }, + { "source": "Dahlia", "target": "Blacheville", "value": 3 }, + { "source": "Dahlia", "target": "Favourite", "value": 5 }, + { "source": "Zephine", "target": "Tholomyes", "value": 3 }, + { "source": "Zephine", "target": "Listolier", "value": 3 }, + { "source": "Zephine", "target": "Fameuil", "value": 3 }, + { "source": "Zephine", "target": "Blacheville", "value": 3 }, + { "source": "Zephine", "target": "Favourite", "value": 4 }, + { "source": "Zephine", "target": "Dahlia", "value": 4 }, + { "source": "Fantine", "target": "Tholomyes", "value": 3 }, + { "source": "Fantine", "target": "Listolier", "value": 3 }, + { "source": "Fantine", "target": "Fameuil", "value": 3 }, + { "source": "Fantine", "target": "Blacheville", "value": 3 }, + { "source": "Fantine", "target": "Favourite", "value": 4 }, + { "source": "Fantine", "target": "Dahlia", "value": 4 }, + { "source": "Fantine", "target": "Zephine", "value": 4 }, + { "source": "Fantine", "target": "Marguerite", "value": 2 }, + { "source": "Fantine", "target": "Valjean", "value": 9 }, + { "source": "Mme.Thenardier", "target": "Fantine", "value": 2 }, + { "source": "Mme.Thenardier", "target": "Valjean", "value": 7 }, + { "source": "Thenardier", "target": "Mme.Thenardier", "value": 13 }, + { "source": "Thenardier", "target": "Fantine", "value": 1 }, + { "source": "Thenardier", "target": "Valjean", "value": 12 }, + { "source": "Cosette", "target": "Mme.Thenardier", "value": 4 }, + { "source": "Cosette", "target": "Valjean", "value": 31 }, + { "source": "Cosette", "target": "Tholomyes", "value": 1 }, + { "source": "Cosette", "target": "Thenardier", "value": 1 }, + { "source": "Javert", "target": "Valjean", "value": 17 }, + { "source": "Javert", "target": "Fantine", "value": 5 }, + { "source": "Javert", "target": "Thenardier", "value": 5 }, + { "source": "Javert", "target": "Mme.Thenardier", "value": 1 }, + { "source": "Javert", "target": "Cosette", "value": 1 }, + { "source": "Fauchelevent", "target": "Valjean", "value": 8 }, + { "source": "Fauchelevent", "target": "Javert", "value": 1 }, + { "source": "Bamatabois", "target": "Fantine", "value": 1 }, + { "source": "Bamatabois", "target": "Javert", "value": 1 }, + { "source": "Bamatabois", "target": "Valjean", "value": 2 }, + { "source": "Perpetue", "target": "Fantine", "value": 1 }, + { "source": "Simplice", "target": "Perpetue", "value": 2 }, + { "source": "Simplice", "target": "Valjean", "value": 3 }, + { "source": "Simplice", "target": "Fantine", "value": 2 }, + { "source": "Simplice", "target": "Javert", "value": 1 }, + { "source": "Scaufflaire", "target": "Valjean", "value": 1 }, + { "source": "Woman1", "target": "Valjean", "value": 2 }, + { "source": "Woman1", "target": "Javert", "value": 1 }, + { "source": "Judge", "target": "Valjean", "value": 3 }, + { "source": "Judge", "target": "Bamatabois", "value": 2 }, + { "source": "Champmathieu", "target": "Valjean", "value": 3 }, + { "source": "Champmathieu", "target": "Judge", "value": 3 }, + { "source": "Champmathieu", "target": "Bamatabois", "value": 2 }, + { "source": "Brevet", "target": "Judge", "value": 2 }, + { "source": "Brevet", "target": "Champmathieu", "value": 2 }, + { "source": "Brevet", "target": "Valjean", "value": 2 }, + { "source": "Brevet", "target": "Bamatabois", "value": 1 }, + { "source": "Chenildieu", "target": "Judge", "value": 2 }, + { "source": "Chenildieu", "target": "Champmathieu", "value": 2 }, + { "source": "Chenildieu", "target": "Brevet", "value": 2 }, + { "source": "Chenildieu", "target": "Valjean", "value": 2 }, + { "source": "Chenildieu", "target": "Bamatabois", "value": 1 }, + { "source": "Cochepaille", "target": "Judge", "value": 2 }, + { "source": "Cochepaille", "target": "Champmathieu", "value": 2 }, + { "source": "Cochepaille", "target": "Brevet", "value": 2 }, + { "source": "Cochepaille", "target": "Chenildieu", "value": 2 }, + { "source": "Cochepaille", "target": "Valjean", "value": 2 }, + { "source": "Cochepaille", "target": "Bamatabois", "value": 1 }, + { "source": "Pontmercy", "target": "Thenardier", "value": 1 }, + { "source": "Boulatruelle", "target": "Thenardier", "value": 1 }, + { "source": "Eponine", "target": "Mme.Thenardier", "value": 2 }, + { "source": "Eponine", "target": "Thenardier", "value": 3 }, + { "source": "Anzelma", "target": "Eponine", "value": 2 }, + { "source": "Anzelma", "target": "Thenardier", "value": 2 }, + { "source": "Anzelma", "target": "Mme.Thenardier", "value": 1 }, + { "source": "Woman2", "target": "Valjean", "value": 3 }, + { "source": "Woman2", "target": "Cosette", "value": 1 }, + { "source": "Woman2", "target": "Javert", "value": 1 }, + { "source": "MotherInnocent", "target": "Fauchelevent", "value": 3 }, + { "source": "MotherInnocent", "target": "Valjean", "value": 1 }, + { "source": "Gribier", "target": "Fauchelevent", "value": 2 }, + { "source": "Mme.Burgon", "target": "Jondrette", "value": 1 }, + { "source": "Gavroche", "target": "Mme.Burgon", "value": 2 }, + { "source": "Gavroche", "target": "Thenardier", "value": 1 }, + { "source": "Gavroche", "target": "Javert", "value": 1 }, + { "source": "Gavroche", "target": "Valjean", "value": 1 }, + { "source": "Gillenormand", "target": "Cosette", "value": 3 }, + { "source": "Gillenormand", "target": "Valjean", "value": 2 }, + { "source": "Magnon", "target": "Gillenormand", "value": 1 }, + { "source": "Magnon", "target": "Mme.Thenardier", "value": 1 }, + { "source": "Mlle.Gillenormand", "target": "Gillenormand", "value": 9 }, + { "source": "Mlle.Gillenormand", "target": "Cosette", "value": 2 }, + { "source": "Mlle.Gillenormand", "target": "Valjean", "value": 2 }, + { "source": "Mme.Pontmercy", "target": "Mlle.Gillenormand", "value": 1 }, + { "source": "Mme.Pontmercy", "target": "Pontmercy", "value": 1 }, + { "source": "Mlle.Vaubois", "target": "Mlle.Gillenormand", "value": 1 }, + { "source": "Lt.Gillenormand", "target": "Mlle.Gillenormand", "value": 2 }, + { "source": "Lt.Gillenormand", "target": "Gillenormand", "value": 1 }, + { "source": "Lt.Gillenormand", "target": "Cosette", "value": 1 }, + { "source": "Marius", "target": "Mlle.Gillenormand", "value": 6 }, + { "source": "Marius", "target": "Gillenormand", "value": 12 }, + { "source": "Marius", "target": "Pontmercy", "value": 1 }, + { "source": "Marius", "target": "Lt.Gillenormand", "value": 1 }, + { "source": "Marius", "target": "Cosette", "value": 21 }, + { "source": "Marius", "target": "Valjean", "value": 19 }, + { "source": "Marius", "target": "Tholomyes", "value": 1 }, + { "source": "Marius", "target": "Thenardier", "value": 2 }, + { "source": "Marius", "target": "Eponine", "value": 5 }, + { "source": "Marius", "target": "Gavroche", "value": 4 }, + { "source": "BaronessT", "target": "Gillenormand", "value": 1 }, + { "source": "BaronessT", "target": "Marius", "value": 1 }, + { "source": "Mabeuf", "target": "Marius", "value": 1 }, + { "source": "Mabeuf", "target": "Eponine", "value": 1 }, + { "source": "Mabeuf", "target": "Gavroche", "value": 1 }, + { "source": "Enjolras", "target": "Marius", "value": 7 }, + { "source": "Enjolras", "target": "Gavroche", "value": 7 }, + { "source": "Enjolras", "target": "Javert", "value": 6 }, + { "source": "Enjolras", "target": "Mabeuf", "value": 1 }, + { "source": "Enjolras", "target": "Valjean", "value": 4 }, + { "source": "Combeferre", "target": "Enjolras", "value": 15 }, + { "source": "Combeferre", "target": "Marius", "value": 5 }, + { "source": "Combeferre", "target": "Gavroche", "value": 6 }, + { "source": "Combeferre", "target": "Mabeuf", "value": 2 }, + { "source": "Prouvaire", "target": "Gavroche", "value": 1 }, + { "source": "Prouvaire", "target": "Enjolras", "value": 4 }, + { "source": "Prouvaire", "target": "Combeferre", "value": 2 }, + { "source": "Feuilly", "target": "Gavroche", "value": 2 }, + { "source": "Feuilly", "target": "Enjolras", "value": 6 }, + { "source": "Feuilly", "target": "Prouvaire", "value": 2 }, + { "source": "Feuilly", "target": "Combeferre", "value": 5 }, + { "source": "Feuilly", "target": "Mabeuf", "value": 1 }, + { "source": "Feuilly", "target": "Marius", "value": 1 }, + { "source": "Courfeyrac", "target": "Marius", "value": 9 }, + { "source": "Courfeyrac", "target": "Enjolras", "value": 17 }, + { "source": "Courfeyrac", "target": "Combeferre", "value": 13 }, + { "source": "Courfeyrac", "target": "Gavroche", "value": 7 }, + { "source": "Courfeyrac", "target": "Mabeuf", "value": 2 }, + { "source": "Courfeyrac", "target": "Eponine", "value": 1 }, + { "source": "Courfeyrac", "target": "Feuilly", "value": 6 }, + { "source": "Courfeyrac", "target": "Prouvaire", "value": 3 }, + { "source": "Bahorel", "target": "Combeferre", "value": 5 }, + { "source": "Bahorel", "target": "Gavroche", "value": 5 }, + { "source": "Bahorel", "target": "Courfeyrac", "value": 6 }, + { "source": "Bahorel", "target": "Mabeuf", "value": 2 }, + { "source": "Bahorel", "target": "Enjolras", "value": 4 }, + { "source": "Bahorel", "target": "Feuilly", "value": 3 }, + { "source": "Bahorel", "target": "Prouvaire", "value": 2 }, + { "source": "Bahorel", "target": "Marius", "value": 1 }, + { "source": "Bossuet", "target": "Marius", "value": 5 }, + { "source": "Bossuet", "target": "Courfeyrac", "value": 12 }, + { "source": "Bossuet", "target": "Gavroche", "value": 5 }, + { "source": "Bossuet", "target": "Bahorel", "value": 4 }, + { "source": "Bossuet", "target": "Enjolras", "value": 10 }, + { "source": "Bossuet", "target": "Feuilly", "value": 6 }, + { "source": "Bossuet", "target": "Prouvaire", "value": 2 }, + { "source": "Bossuet", "target": "Combeferre", "value": 9 }, + { "source": "Bossuet", "target": "Mabeuf", "value": 1 }, + { "source": "Bossuet", "target": "Valjean", "value": 1 }, + { "source": "Joly", "target": "Bahorel", "value": 5 }, + { "source": "Joly", "target": "Bossuet", "value": 7 }, + { "source": "Joly", "target": "Gavroche", "value": 3 }, + { "source": "Joly", "target": "Courfeyrac", "value": 5 }, + { "source": "Joly", "target": "Enjolras", "value": 5 }, + { "source": "Joly", "target": "Feuilly", "value": 5 }, + { "source": "Joly", "target": "Prouvaire", "value": 2 }, + { "source": "Joly", "target": "Combeferre", "value": 5 }, + { "source": "Joly", "target": "Mabeuf", "value": 1 }, + { "source": "Joly", "target": "Marius", "value": 2 }, + { "source": "Grantaire", "target": "Bossuet", "value": 3 }, + { "source": "Grantaire", "target": "Enjolras", "value": 3 }, + { "source": "Grantaire", "target": "Combeferre", "value": 1 }, + { "source": "Grantaire", "target": "Courfeyrac", "value": 2 }, + { "source": "Grantaire", "target": "Joly", "value": 2 }, + { "source": "Grantaire", "target": "Gavroche", "value": 1 }, + { "source": "Grantaire", "target": "Bahorel", "value": 1 }, + { "source": "Grantaire", "target": "Feuilly", "value": 1 }, + { "source": "Grantaire", "target": "Prouvaire", "value": 1 }, + { "source": "MotherPlutarch", "target": "Mabeuf", "value": 3 }, + { "source": "Gueulemer", "target": "Thenardier", "value": 5 }, + { "source": "Gueulemer", "target": "Valjean", "value": 1 }, + { "source": "Gueulemer", "target": "Mme.Thenardier", "value": 1 }, + { "source": "Gueulemer", "target": "Javert", "value": 1 }, + { "source": "Gueulemer", "target": "Gavroche", "value": 1 }, + { "source": "Gueulemer", "target": "Eponine", "value": 1 }, + { "source": "Babet", "target": "Thenardier", "value": 6 }, + { "source": "Babet", "target": "Gueulemer", "value": 6 }, + { "source": "Babet", "target": "Valjean", "value": 1 }, + { "source": "Babet", "target": "Mme.Thenardier", "value": 1 }, + { "source": "Babet", "target": "Javert", "value": 2 }, + { "source": "Babet", "target": "Gavroche", "value": 1 }, + { "source": "Babet", "target": "Eponine", "value": 1 }, + { "source": "Claquesous", "target": "Thenardier", "value": 4 }, + { "source": "Claquesous", "target": "Babet", "value": 4 }, + { "source": "Claquesous", "target": "Gueulemer", "value": 4 }, + { "source": "Claquesous", "target": "Valjean", "value": 1 }, + { "source": "Claquesous", "target": "Mme.Thenardier", "value": 1 }, + { "source": "Claquesous", "target": "Javert", "value": 1 }, + { "source": "Claquesous", "target": "Eponine", "value": 1 }, + { "source": "Claquesous", "target": "Enjolras", "value": 1 }, + { "source": "Montparnasse", "target": "Javert", "value": 1 }, + { "source": "Montparnasse", "target": "Babet", "value": 2 }, + { "source": "Montparnasse", "target": "Gueulemer", "value": 2 }, + { "source": "Montparnasse", "target": "Claquesous", "value": 2 }, + { "source": "Montparnasse", "target": "Valjean", "value": 1 }, + { "source": "Montparnasse", "target": "Gavroche", "value": 1 }, + { "source": "Montparnasse", "target": "Eponine", "value": 1 }, + { "source": "Montparnasse", "target": "Thenardier", "value": 1 }, + { "source": "Toussaint", "target": "Cosette", "value": 2 }, + { "source": "Toussaint", "target": "Javert", "value": 1 }, + { "source": "Toussaint", "target": "Valjean", "value": 1 }, + { "source": "Child1", "target": "Gavroche", "value": 2 }, + { "source": "Child2", "target": "Gavroche", "value": 2 }, + { "source": "Child2", "target": "Child1", "value": 3 }, + { "source": "Brujon", "target": "Babet", "value": 3 }, + { "source": "Brujon", "target": "Gueulemer", "value": 3 }, + { "source": "Brujon", "target": "Thenardier", "value": 3 }, + { "source": "Brujon", "target": "Gavroche", "value": 1 }, + { "source": "Brujon", "target": "Eponine", "value": 1 }, + { "source": "Brujon", "target": "Claquesous", "value": 1 }, + { "source": "Brujon", "target": "Montparnasse", "value": 1 }, + { "source": "Mme.Hucheloup", "target": "Bossuet", "value": 1 }, + { "source": "Mme.Hucheloup", "target": "Joly", "value": 1 }, + { "source": "Mme.Hucheloup", "target": "Grantaire", "value": 1 }, + { "source": "Mme.Hucheloup", "target": "Bahorel", "value": 1 }, + { "source": "Mme.Hucheloup", "target": "Courfeyrac", "value": 1 }, + { "source": "Mme.Hucheloup", "target": "Gavroche", "value": 1 }, + { "source": "Mme.Hucheloup", "target": "Enjolras", "value": 1 } + ] +} diff --git a/docs/examples/orientation.md b/docs/examples/orientation.md new file mode 100644 index 0000000..1c69dde --- /dev/null +++ b/docs/examples/orientation.md @@ -0,0 +1,60 @@ +--- +title: Tree Orientations +--- + +# Tree Orientations + + + +## Horizontal + + + +### Code + +:::code-group + +<<< ./tree.ts#horizontal [config] + +<<< ./tree.ts#data [data] + +::: + +## Vertical + + + +### Code + +:::code-group + +<<< ./tree.ts#vertical [config] + +<<< ./tree.ts#data [data] + +::: + +## Radial + + + +### Code + +:::code-group + +<<< ./tree.ts#radial [config] + +<<< ./tree.ts#data [data] + +::: diff --git a/docs/examples/tree.json b/docs/examples/tree.json new file mode 100644 index 0000000..755b292 --- /dev/null +++ b/docs/examples/tree.json @@ -0,0 +1,25 @@ +[ + { "name": "1" }, + { "name": "11", "parent": 0 }, + { "name": "111", "parent": 1 }, + { "name": "1111", "parent": 2 }, + { "name": "1112", "parent": 2 }, + { "name": "112", "parent": 1 }, + { "name": "1121", "parent": 5 }, + { "name": "1122", "parent": 5 }, + { "name": "113", "parent": 1 }, + { "name": "1131", "parent": 8 }, + { "name": "1132", "parent": 8 }, + { "name": "12", "parent": 0 }, + { "name": "121", "parent": 11 }, + { "name": "1211", "parent": 12 }, + { "name": "1212", "parent": 12 }, + { "name": "122", "parent": 11 }, + { "name": "1221", "parent": 15 }, + { "name": "1222", "parent": 15 }, + { "name": "123", "parent": 11 }, + { "name": "1231", "parent": 18 }, + { "name": "1232", "parent": 18 }, + { "name": "13", "parent": 0 }, + { "name": "131", "parent": 21 } +] diff --git a/docs/examples/tree.md b/docs/examples/tree.md new file mode 100644 index 0000000..16a66d2 --- /dev/null +++ b/docs/examples/tree.md @@ -0,0 +1,24 @@ +--- +title: Tree +--- + +# Tree + + + + + +### Code + +:::code-group + +<<< ./tree.ts#config [config] + +<<< ./tree.ts#data [data] + +::: diff --git a/docs/examples/tree.ts b/docs/examples/tree.ts new file mode 100644 index 0000000..f9f944b --- /dev/null +++ b/docs/examples/tree.ts @@ -0,0 +1,85 @@ +import type { ChartConfiguration } from 'chart.js'; +import type {} from '../../src'; +import 'chartjs-plugin-datalabels'; + +// #region data +import nodes from './tree.json'; + +export const data: ChartConfiguration<'tree'>['data'] = { + labels: nodes.map((d) => d.name), + datasets: [ + { + pointBackgroundColor: 'steelblue', + pointRadius: 5, + data: nodes.map((d) => Object.assign({}, d)), + edgeLineBorderWidth: (ctx) => { + return ctx.dataIndex; + }, + }, + ], +}; +// #endregion data +// #region tree +export const config: ChartConfiguration<'tree'> = { + type: 'tree', + data, + options: { + plugins: { + datalabels: { + display: false, + }, + }, + tree: { + mode: 'tree', + }, + }, +}; +// #endregion tree +// #region horizontal +export const horizontal: ChartConfiguration<'tree'> = { + type: 'tree', + data, + options: { + plugins: { + datalabels: { + display: false, + }, + }, + tree: { + orientation: 'horizontal', + }, + }, +}; +// #endregion horizontal +// #region vertical +export const vertical: ChartConfiguration<'tree'> = { + type: 'tree', + data, + options: { + plugins: { + datalabels: { + display: false, + }, + }, + tree: { + orientation: 'vertical', + }, + }, +}; +// #endregion vertical +// #region radial +export const radial: ChartConfiguration<'tree'> = { + type: 'tree', + data, + options: { + plugins: { + datalabels: { + display: false, + }, + }, + tree: { + orientation: 'radial', + }, + }, +}; +// #endregion radial diff --git a/docs/getting-started.md b/docs/getting-started.md new file mode 100644 index 0000000..d21138b --- /dev/null +++ b/docs/getting-started.md @@ -0,0 +1,41 @@ +--- +title: Getting Started +--- + +Chart.js module for charting graphs. Adding new chart types: `graph`, `forceDirectedGraph`, `dendrogram`, and `tree`. + +![force](https://user-images.githubusercontent.com/4129778/65398353-9bc03f80-dd84-11e9-8f14-339635c1ba4e.png) + +![dend_h](https://user-images.githubusercontent.com/4129778/65398352-9bc03f80-dd84-11e9-9197-ecb66a872736.png) + +![tree_v](https://user-images.githubusercontent.com/4129778/65398350-9bc03f80-dd84-11e9-8c94-e93c07040ee7.png) + +![radial](https://user-images.githubusercontent.com/4129778/65398354-9bc03f80-dd84-11e9-9633-c4c80bd9c384.png) + +Works great with https://github.com/chartjs/chartjs-plugin-datalabels or https://github.com/chrispahm/chartjs-plugin-dragdata + +## Install + +```sh +npm install chart.js chartjs-chart-graph +``` + +## Usage + +see [Examples](./examples/) + +CodePens + +- [Force Directed Layout](https://codepen.io/sgratzl/pen/mdezvmL) +- [Tree Layouts](https://codepen.io/sgratzl/pen/jObedwg) +- [Tree With Data Labels](https://codepen.io/sgratzl/pen/vYNVbgd) + +## Configuration + +### Data Structure + +TODO + +### Styling + +The new chart types are based on the existing `line` controller. Tho, instead of showing a line per dataset it shows edges as lines. Therefore, the styling options for points and lines are the same. See also https://www.chartjs.org/docs/latest/charts/line.html. However, to avoid confusion, the line options have a default `line` prefix, e..g `lineBorderColor` to specify the edge border color and `pointBorderColor` to specify the node border color. diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..92cd4cd --- /dev/null +++ b/docs/index.md @@ -0,0 +1,22 @@ +--- +# https://vitepress.dev/reference/default-theme-home-page +layout: home + +hero: + name: 'chartjs-chart-graph' + text: 'chart.js plugin' + tagline: Chart.js module for charting graphs + actions: + - theme: brand + text: Getting Started + link: /getting-started + - theme: alt + text: Examples + link: /examples/ + - theme: alt + text: API + link: /api/ +# features: +# - title: Feature A +# details: Lorem ipsum dolor sit amet, consectetur adipiscing elit +--- diff --git a/docs/related.md b/docs/related.md new file mode 100644 index 0000000..bf01c2d --- /dev/null +++ b/docs/related.md @@ -0,0 +1,15 @@ +--- +title: Related Plugins +--- + +There are several related chart.js plugins providing additional functionality and chart types: + +- [chartjs-chart-boxplot](https://github.com/sgratzl/chartjs-chart-boxplot) for rendering boxplots and violin charts +- [chartjs-chart-error-bars](https://github.com/sgratzl/chartjs-chart-error-bars) for rendering errors bars to bars and line charts +- [chartjs-chart-funnel](https://github.com/sgratzl/chartjs-chart-funnel) for rendering funnel charts +- [chartjs-chart-geo](https://github.com/sgratzl/chartjs-chart-geo) for rendering map, bubble maps, and choropleth charts +- [chartjs-chart-graph](https://github.com/sgratzl/chartjs-chart-graph) for rendering graphs, trees, and networks +- [chartjs-chart-pcp](https://github.com/sgratzl/chartjs-chart-pcp) for rendering parallel coordinate plots +- [chartjs-chart-venn](https://github.com/sgratzl/chartjs-chart-venn) for rendering venn and euler diagrams +- [chartjs-chart-wordcloud](https://github.com/sgratzl/chartjs-chart-wordcloud) for rendering word clouds +- [chartjs-plugin-hierarchical](https://github.com/sgratzl/chartjs-plugin-hierarchical) for rendering hierarchical categorical axes which can be expanded and collapsed