From: Evert Timberg Date: Fri, 2 Apr 2021 12:04:39 +0000 (-0400) Subject: Vuepress samples (#8756) X-Git-Tag: v3.0.0-rc.7~2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=66ee0fecafe14867808429f1dbf3e6f49d453e8f;p=thirdparty%2FChart.js.git Vuepress samples (#8756) * Generate API docs with vuepress-plugin-typedoc * Links, fixes, cleanup * Convert bar samples to Vuepress * Some line chart samples moved over * Fix lint issues * Derived axis type sample * LineAreaStacked chart created in vuepress * added radar area axample * Line dataset added sample * final area example added * Add derived-chart-type * Bar scriptable sample * Scriptable samples * Clean lint errors * added linear axis samples to vuepress * change tab to spaces to fix lint error * Convert the rest of the scale samples * Scale option samples * Fix typo * Fixes * Legend samples * Title samples * Change the title of the tip block to Note (#8758) * Convert bar samples to Vuepress * Some line chart samples moved over * Fix lint issues * Derived axis type sample * LineAreaStacked chart created in vuepress * added radar area axample * Line dataset added sample * final area example added * Add derived-chart-type * Bar scriptable sample * Scriptable samples * Clean lint errors * added linear axis samples to vuepress * change tab to spaces to fix lint error * Convert the rest of the scale samples * Scale option samples * Fix typo * Fixes * Legend samples * Advanced samples * Remove extra section * Animation samples * Hide legend from progressive line * Add a comment on what `from` does * Tooltip samples * Ädd other charts to vuepress samples * enable plugin again since all samples have been converted * fix skip radar example, middle skip was not calculated correctly * lint error * Progressive-line: add 2nd line * Fix lint errors Co-authored-by: Jukka Kurkela Co-authored-by: Jacco van den Berg Co-authored-by: Jacco van den Berg <39033624+LeeLenaleee@users.noreply.github.com> --- diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 48d3fbc41..f538e291f 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -21,7 +21,6 @@ module.exports = { ], [ 'vuepress-plugin-typedoc', - { entryPoints: ['../../types/index.esm.d.ts'], hideInPageTOC: true, @@ -58,13 +57,15 @@ module.exports = { imports: [ ['scripts/register.js'], ['scripts/utils.js', 'Utils'], + ['scripts/helpers.js', 'helpers'], + ['scripts/components.js', 'components'] ] }, nav: [ {text: 'Home', link: '/'}, {text: 'API', link: '/api/'}, // TODO: Make local when samples moved to vuepress - {text: 'Samples', link: `https://www.chartjs.org/samples/${docsVersion}/`}, + {text: 'Samples', link: `/samples/`}, { text: 'Ecosystem', ariaLabel: 'Community Menu', @@ -79,6 +80,131 @@ module.exports = { '/api/': { title: 'API' }, + '/samples/': [ + '', + { + title: 'Bar Charts', + children: [ + 'bar/vertical', + 'bar/horizontal', + 'bar/stacked', + 'bar/stacked-groups', + 'bar/floating', + 'bar/border-radius', + ] + }, + { + title: 'Line Charts', + children: [ + 'line/line', + 'line/multi-axis', + 'line/stepped', + 'line/interpolation', + 'line/styling', + // 'line/point-styling', + ] + }, + { + title: 'Other charts', + children: [ + 'other-charts/bubble', + 'other-charts/scatter', + 'other-charts/scatter-multi-axis', + 'other-charts/doughnut', + 'other-charts/pie', + 'other-charts/multi-series-pie', + 'other-charts/polar-area', + 'other-charts/radar', + 'other-charts/radar-skip-points', + 'other-charts/combo-bar-line', + ] + }, + { + title: 'Area charts', + children: [ + 'area/line-boundaries', + 'area/line-datasets', + 'area/line-stacked', + 'area/radar' + ] + }, + { + title: 'Scales', + children: [ + 'scales/linear-min-max', + 'scales/linear-min-max-suggested', + 'scales/linear-step-size', + 'scales/log', + 'scales/time-line', + 'scales/time-max-span', + 'scales/time-combo', + ] + }, + { + title: 'Scale Options', + children: [ + 'scale-options/grid', + 'scale-options/ticks', + 'scale-options/titles', + 'scale-options/center' + ] + }, + { + title: 'Legend', + children: [ + 'legend/position', + 'legend/title', + 'legend/point-style', + ] + }, + { + title: 'Title', + children: [ + 'title/alignment', + ] + }, + { + title: 'Tooltip', + children: [ + 'tooltip/position', + 'tooltip/interactions', + 'tooltip/point-style', + 'tooltip/content', + 'tooltip/html', + ] + }, + { + title: 'Scriptable Options', + children: [ + 'scriptable/bar', + 'scriptable/bubble', + 'scriptable/pie', + 'scriptable/line', + 'scriptable/polar', + 'scriptable/radar', + ] + }, + { + title: 'Animations', + children: [ + 'animations/delay', + 'animations/drop', + 'animations/loop', + 'animations/progressive-line', + ] + }, + { + title: 'Advanced', + children: [ + 'advanced/progress-bar', + 'advanced/radial-gradient', + 'advanced/linear-gradient', + 'advanced/programmatic-events', + 'advanced/derived-axis-type', + 'advanced/derived-chart-type', + ] + }, + ], '/': [ '', { diff --git a/docs/samples/advanced/derived-axis-type.md b/docs/samples/advanced/derived-axis-type.md new file mode 100644 index 000000000..178cce776 --- /dev/null +++ b/docs/samples/advanced/derived-axis-type.md @@ -0,0 +1,50 @@ +# Derived Axis Type + +```js chart-editor +// +const DATA_COUNT = 12; +const NUMBER_CFG = {count: DATA_COUNT, min: 0, max: 1000}; +const labels = Utils.months({count: DATA_COUNT}); +const data = { + labels: labels, + datasets: [ + { + label: 'My First dataset', + data: Utils.numbers(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red, 0.5), + fill: false, + } + ], +}; +// + +// +const config = { + type: 'line', + data, + options: { + responsive: true, + scales: { + x: { + display: true, + }, + y: { + display: true, + type: 'log2', + } + } + } +}; + +// + +module.exports = { + actions: [], + config: config, +}; +``` + +## Log2 axis implementation + +<<< @/docs/scripts/log2.js diff --git a/docs/samples/advanced/derived-chart-type.md b/docs/samples/advanced/derived-chart-type.md new file mode 100644 index 000000000..9633f07f6 --- /dev/null +++ b/docs/samples/advanced/derived-chart-type.md @@ -0,0 +1,46 @@ +# Derived Chart Type + +```js chart-editor +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100, rmin: 1, rmax: 20}; +const data = { + datasets: [ + { + label: 'My First dataset', + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.blue, 0.5), + borderColor: Utils.CHART_COLORS.blue, + borderWidth: 1, + boxStrokeStyle: 'red', + data: Utils.bubbles(NUMBER_CFG) + } + ], +}; +// + +// +const config = { + type: 'derivedBubble', + data: data, + options: { + responsive: true, + plugins: { + title: { + display: true, + text: 'Derived Chart Type' + }, + } + } +}; + +// + +module.exports = { + actions: [], + config: config, +}; +``` + +## DerivedBubble Implementation + +<<< @/docs/scripts/derived-bubble.js diff --git a/docs/samples/advanced/linear-gradient.md b/docs/samples/advanced/linear-gradient.md new file mode 100644 index 000000000..47fa7b50b --- /dev/null +++ b/docs/samples/advanced/linear-gradient.md @@ -0,0 +1,110 @@ +# Linear Gradient + +```js chart-editor +// +const actions = [ + { + name: 'Randomize', + handler(chart) { + chart.data.datasets.forEach(dataset => { + dataset.data = Utils.numbers({count: chart.data.labels.length, min: -100, max: 100}); + }); + chart.update(); + } + }, + { + name: 'Add Data', + handler(chart) { + const data = chart.data; + if (data.datasets.length > 0) { + data.labels = Utils.months({count: data.labels.length + 1}); + + for (var index = 0; index < data.datasets.length; ++index) { + data.datasets[index].data.push(Utils.rand(-100, 100)); + } + + chart.update(); + } + } + }, + { + name: 'Remove Data', + handler(chart) { + chart.data.labels.splice(-1, 1); // remove the label first + + chart.data.datasets.forEach(dataset => { + dataset.data.pop(); + }); + + chart.update(); + } + } +]; +// + +// +let width, height, gradient; +function getGradient(ctx, chartArea) { + const chartWidth = chartArea.right - chartArea.left; + const chartHeight = chartArea.bottom - chartArea.top; + if (gradient === null || width !== chartWidth || height !== chartHeight) { + // Create the gradient because this is either the first render + // or the size of the chart has changed + width = chartWidth; + height = chartHeight; + gradient = ctx.createLinearGradient(0, chartArea.bottom, 0, chartArea.top); + gradient.addColorStop(0, Utils.CHART_COLORS.blue); + gradient.addColorStop(0.5, Utils.CHART_COLORS.yellow); + gradient.addColorStop(1, Utils.CHART_COLORS.red); + } + + return gradient; +} +// + +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100}; +const labels = Utils.months({count: 7}); + +const data = { + labels: labels, + datasets: [ + { + label: 'Dataset 1', + data: Utils.numbers(NUMBER_CFG), + borderColor: function(context) { + const chart = context.chart; + const {ctx, chartArea} = chart; + + if (!chartArea) { + // This case happens on initial chart load + return null; + } + return getGradient(ctx, chartArea); + }, + }, + ] +}; +// + +// +const config = { + type: 'line', + data: data, + options: { + responsive: true, + plugins: { + legend: { + position: 'top', + }, + } + }, +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` diff --git a/docs/samples/advanced/programmatic-events.md b/docs/samples/advanced/programmatic-events.md new file mode 100644 index 000000000..63a29f066 --- /dev/null +++ b/docs/samples/advanced/programmatic-events.md @@ -0,0 +1,103 @@ +# Programmatic Event Triggers + +```js chart-editor +// +function triggerHover(chart) { + if (chart.getActiveElements().length > 0) { + chart.setActiveElements([]); + } else { + chart.setActiveElements([ + { + datasetIndex: 0, + index: 0, + }, { + datasetIndex: 1, + index: 0, + } + ]); + } + chart.update(); +} +// + +// +function triggerTooltip(chart) { + const tooltip = chart.tooltip; + if (tooltip.getActiveElements().length > 0) { + tooltip.setActiveElements([], {x: 0, y: 0}); + } else { + const chartArea = chart.chartArea; + tooltip.setActiveElements([ + { + datasetIndex: 0, + index: 2, + }, { + datasetIndex: 1, + index: 2, + } + ], + { + x: (chartArea.left + chartArea.right) / 2, + y: (chartArea.top + chartArea.bottom) / 2, + }); + } + + chart.update(); +} +// + +// +const actions = [ + { + name: 'Trigger Hover', + handler: triggerHover + }, + { + name: 'Trigger Tooltip', + handler: triggerTooltip + } +]; +// + +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100}; + +const labels = Utils.months({count: 7}); +const data = { + labels: labels, + datasets: [ + { + label: 'Dataset 1', + data: Utils.numbers(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red, 0.5), + hoverBorderWidth: 5, + hoverBorderColor: 'green', + }, + { + label: 'Dataset 2', + data: Utils.numbers(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.blue, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.blue, 0.5), + hoverBorderWidth: 5, + hoverBorderColor: 'green', + } + ] +}; +// + +// +const config = { + type: 'bar', + data: data, + options: { + }, +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` diff --git a/docs/samples/advanced/progress-bar.md b/docs/samples/advanced/progress-bar.md new file mode 100644 index 000000000..ff86c759c --- /dev/null +++ b/docs/samples/advanced/progress-bar.md @@ -0,0 +1,129 @@ +# Animation Progress Bar + + + +```js chart-editor +// +const actions = [ + { + name: 'Randomize', + handler(chart) { + chart.data.datasets.forEach(dataset => { + dataset.data = Utils.numbers({count: chart.data.labels.length, min: -100, max: 100}); + }); + chart.update(); + } + }, + { + name: 'Add Dataset', + handler(chart) { + const data = chart.data; + const dsColor = Utils.namedColor(chart.data.datasets.length); + const newDataset = { + label: 'Dataset ' + (data.datasets.length + 1), + backgroundColor: Utils.transparentize(dsColor, 0.5), + borderColor: dsColor, + borderWidth: 1, + data: Utils.numbers({count: data.labels.length, min: -100, max: 100}), + }; + chart.data.datasets.push(newDataset); + chart.update(); + } + }, + { + name: 'Add Data', + handler(chart) { + const data = chart.data; + if (data.datasets.length > 0) { + data.labels = Utils.months({count: data.labels.length + 1}); + + for (var index = 0; index < data.datasets.length; ++index) { + data.datasets[index].data.push(Utils.rand(-100, 100)); + } + + chart.update(); + } + } + }, + { + name: 'Remove Dataset', + handler(chart) { + chart.data.datasets.pop(); + chart.update(); + } + }, + { + name: 'Remove Data', + handler(chart) { + chart.data.labels.splice(-1, 1); // remove the label first + + chart.data.datasets.forEach(dataset => { + dataset.data.pop(); + }); + + chart.update(); + } + } +]; +// + +// +const progress = document.getElementById('animationProgress'); + +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100}; + +const labels = Utils.months({count: 7}); +const data = { + labels: labels, + datasets: [ + { + label: 'Dataset 1', + data: Utils.numbers(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red, 0.5), + }, + { + label: 'Dataset 2', + data: Utils.numbers(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.blue, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.blue, 0.5), + } + ] +}; +// + +// +const config = { + type: 'line', + data: data, + options: { + animation: { + duration: 2000, + onProgress: function(animation) { + progress.value = animation.currentStep / animation.numSteps; + }, + onComplete: function() { + // + } + }, + interaction: { + mode: 'nearest', + axis: 'x', + intersect: false + }, + plugins: { + title: { + display: true, + text: 'Chart.js Line Chart - Animation Progress Bar' + } + }, + }, +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` diff --git a/docs/samples/advanced/radial-gradient.md b/docs/samples/advanced/radial-gradient.md new file mode 100644 index 000000000..cf8d0d63d --- /dev/null +++ b/docs/samples/advanced/radial-gradient.md @@ -0,0 +1,116 @@ +# Radial Gradient + +```js chart-editor +// +const DATA_COUNT = 5; +Utils.srand(110); + +const chartColors = Utils.CHART_COLORS; +const colors = [chartColors.red, chartColors.orange, chartColors.yellow, chartColors.green, chartColors.blue]; + +const cache = new Map(); +let width = null; +let height = null; + +const actions = [ + { + name: 'Randomize', + handler(chart) { + chart.data.datasets.forEach(dataset => { + dataset.data = generateData(); + }); + chart.update(); + } + }, +]; +// + +// +function createRadialGradient3(context, c1, c2, c3) { + const chartArea = context.chart.chartArea; + if (!chartArea) { + // This case happens on initial chart load + return null; + } + + const chartWidth = chartArea.right - chartArea.left; + const chartHeight = chartArea.bottom - chartArea.top; + if (width !== chartWidth || height !== chartHeight) { + cache.clear(); + } + var gradient = cache.get(c1 + c2 + c3); + if (!gradient) { + // Create the gradient because this is either the first render + // or the size of the chart has changed + width = chartWidth; + height = chartHeight; + const centerX = (chartArea.left + chartArea.right) / 2; + const centerY = (chartArea.top + chartArea.bottom) / 2; + const r = Math.min( + (chartArea.right - chartArea.left) / 2, + (chartArea.bottom - chartArea.top) / 2 + ); + var ctx = context.chart.ctx; + gradient = ctx.createRadialGradient(centerX, centerY, 0, centerX, centerY, r); + gradient.addColorStop(0, c1); + gradient.addColorStop(0.5, c2); + gradient.addColorStop(1, c3); + cache.set(c1 + c2 + c3, gradient); + } + + return gradient; +} +// + +// +function generateData() { + return Utils.numbers({ + count: DATA_COUNT, + min: 0, + max: 100 + }); +} + +const data = { + labels: Utils.months({count: DATA_COUNT}), + datasets: [{ + data: generateData() + }] +}; +// + +// +const config = { + type: 'polarArea', + data: data, + options: { + plugins: { + legend: false, + tooltip: false, + }, + elements: { + arc: { + backgroundColor: function(context) { + let c = colors[context.dataIndex]; + if (!c) { + return; + } + if (context.active) { + c = helpers.getHoverColor(c); + } + const mid = helpers.color(c).desaturate(0.2).darken(0.2).rgbString(); + const start = helpers.color(c).lighten(0.2).rotate(270).rgbString(); + const end = helpers.color(c).lighten(0.1).rgbString(); + return createRadialGradient3(context, start, mid, end); + }, + } + } + } +}; +// + +module.exports = { + actions, + config, +}; +``` diff --git a/docs/samples/animations/delay.md b/docs/samples/animations/delay.md new file mode 100644 index 000000000..3e7a8d4f9 --- /dev/null +++ b/docs/samples/animations/delay.md @@ -0,0 +1,79 @@ +# Delay + +```js chart-editor +// +const actions = [ + { + name: 'Randomize', + handler(chart) { + chart.data.datasets.forEach(dataset => { + dataset.data = Utils.numbers({count: chart.data.labels.length, min: -100, max: 100}); + }); + chart.update(); + } + }, +]; +// + +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100}; + +const labels = Utils.months({count: 7}); +const data = { + labels: labels, + datasets: [ + { + label: 'Dataset 1', + data: Utils.numbers(NUMBER_CFG), + backgroundColor: Utils.CHART_COLORS.red, + }, + { + label: 'Dataset 2', + data: Utils.numbers(NUMBER_CFG), + backgroundColor: Utils.CHART_COLORS.blue, + }, + { + label: 'Dataset 3', + data: Utils.numbers(NUMBER_CFG), + backgroundColor: Utils.CHART_COLORS.green, + }, + ] +}; +// + +// +var delayed; +const config = { + type: 'bar', + data: data, + options: { + animation: { + onComplete: () => { + delayed = true; + }, + delay: (context) => { + let delay = 0; + if (context.type === 'data' && context.mode === 'default' && !delayed) { + delay = context.dataIndex * 300 + context.datasetIndex * 100; + } + return delay; + }, + }, + scales: { + x: { + stacked: true, + }, + y: { + stacked: true + } + } + } +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` diff --git a/docs/samples/animations/drop.md b/docs/samples/animations/drop.md new file mode 100644 index 000000000..ced951733 --- /dev/null +++ b/docs/samples/animations/drop.md @@ -0,0 +1,126 @@ +# Drop + +```js chart-editor +// +const actions = [ + { + name: 'Randomize', + handler(chart) { + chart.data.datasets.forEach(dataset => { + dataset.data = Utils.numbers({count: chart.data.labels.length, min: -100, max: 100}); + }); + chart.update(); + } + }, + { + name: 'Add Dataset', + handler(chart) { + const data = chart.data; + const dsColor = Utils.namedColor(chart.data.datasets.length); + const newDataset = { + label: 'Dataset ' + (data.datasets.length + 1), + backgroundColor: Utils.transparentize(dsColor, 0.5), + borderColor: dsColor, + borderWidth: 1, + data: Utils.numbers({count: data.labels.length, min: -100, max: 100}), + }; + chart.data.datasets.push(newDataset); + chart.update(); + } + }, + { + name: 'Add Data', + handler(chart) { + const data = chart.data; + if (data.datasets.length > 0) { + data.labels = Utils.months({count: data.labels.length + 1}); + + for (var index = 0; index < data.datasets.length; ++index) { + data.datasets[index].data.push(Utils.rand(-100, 100)); + } + + chart.update(); + } + } + }, + { + name: 'Remove Dataset', + handler(chart) { + chart.data.datasets.pop(); + chart.update(); + } + }, + { + name: 'Remove Data', + handler(chart) { + chart.data.labels.splice(-1, 1); // remove the label first + + chart.data.datasets.forEach(dataset => { + dataset.data.pop(); + }); + + chart.update(); + } + } +]; +// + +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100}; + +const labels = Utils.months({count: 7}); +const data = { + labels: labels, + datasets: [ + { + label: 'Dataset 1', + animations: { + y: { + duration: 2000, + delay: 500 + } + }, + data: Utils.numbers(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red, 0.5), + fill: 1, + tension: 0.5 + }, + { + label: 'Dataset 2', + data: Utils.numbers(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.blue, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.blue, 0.5), + } + ] +}; +// + +// +const config = { + type: 'line', + data: data, + options: { + animations: { + y: { + easing: 'easeInOutElastic', + from: (ctx) => { + if (ctx.type === 'data') { + if (ctx.mode === 'default' && !ctx.dropped) { + ctx.dropped = true; + return 0; + } + } + } + } + }, + }, +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` diff --git a/docs/samples/animations/loop.md b/docs/samples/animations/loop.md new file mode 100644 index 000000000..943962cd7 --- /dev/null +++ b/docs/samples/animations/loop.md @@ -0,0 +1,126 @@ +# Loop + +```js chart-editor +// +const actions = [ + { + name: 'Randomize', + handler(chart) { + chart.data.datasets.forEach(dataset => { + dataset.data = Utils.numbers({count: chart.data.labels.length, min: -100, max: 100}); + }); + chart.update(); + } + }, + { + name: 'Add Dataset', + handler(chart) { + const data = chart.data; + const dsColor = Utils.namedColor(chart.data.datasets.length); + const newDataset = { + label: 'Dataset ' + (data.datasets.length + 1), + backgroundColor: Utils.transparentize(dsColor, 0.5), + borderColor: dsColor, + borderWidth: 1, + data: Utils.numbers({count: data.labels.length, min: -100, max: 100}), + }; + chart.data.datasets.push(newDataset); + chart.update(); + } + }, + { + name: 'Add Data', + handler(chart) { + const data = chart.data; + if (data.datasets.length > 0) { + data.labels = Utils.months({count: data.labels.length + 1}); + + for (var index = 0; index < data.datasets.length; ++index) { + data.datasets[index].data.push(Utils.rand(-100, 100)); + } + + chart.update(); + } + } + }, + { + name: 'Remove Dataset', + handler(chart) { + chart.data.datasets.pop(); + chart.update(); + } + }, + { + name: 'Remove Data', + handler(chart) { + chart.data.labels.splice(-1, 1); // remove the label first + + chart.data.datasets.forEach(dataset => { + dataset.data.pop(); + }); + + chart.update(); + } + } +]; +// + +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100}; + +const labels = Utils.months({count: DATA_COUNT}); +const data = { + labels: labels, + datasets: [ + { + label: 'Dataset 1', + data: Utils.numbers(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red, 0.5), + tension: 0.4, + }, + { + label: 'Dataset 2', + data: Utils.numbers(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.blue, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.blue, 0.5), + tension: 0.2, + } + ] +}; +// + +// +const config = { + type: 'line', + data: data, + options: { + animations: { + radius: { + duration: 400, + easing: 'linear', + loop: (context) => context.active + } + }, + hoverRadius: 12, + hoverBackgroundColor: 'yellow', + interaction: { + mode: 'nearest', + intersect: false, + axis: 'x' + }, + plugins: { + tooltip: { + enabled: false + } + } + }, +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` diff --git a/docs/samples/animations/progressive-line.md b/docs/samples/animations/progressive-line.md new file mode 100644 index 000000000..632355545 --- /dev/null +++ b/docs/samples/animations/progressive-line.md @@ -0,0 +1,90 @@ +# Progressive Line + +```js chart-editor + +// +const data = []; +const data2 = []; +let prev = 100; +let prev2 = 80; +for (let i = 0; i < 1000; i++) { + prev += 5 - Math.random() * 10; + data.push({x: i, y: prev}); + prev2 += 5 - Math.random() * 10; + data2.push({x: i, y: prev2}); +} +// + +// +const totalDuration = 10000; +const delayBetweenPoints = totalDuration / data.length; +const previousY = (ctx) => ctx.index === 0 ? ctx.chart.scales.y.getPixelForValue(100) : ctx.chart.getDatasetMeta(ctx.datasetIndex).data[ctx.index - 1].getProps(['y'], true).y; +const animation = { + x: { + type: 'number', + easing: 'linear', + duration: delayBetweenPoints, + from: NaN, // the point is initially skipped + delay(ctx) { + if (ctx.type !== 'data' || ctx.xStarted) { + return 0; + } + ctx.xStarted = true; + return ctx.index * delayBetweenPoints; + } + }, + y: { + type: 'number', + easing: 'linear', + duration: delayBetweenPoints, + from: previousY, + delay(ctx) { + if (ctx.type !== 'data' || ctx.yStarted) { + return 0; + } + ctx.yStarted = true; + return ctx.index * delayBetweenPoints; + } + } +}; +// + +// +const config = { + type: 'line', + data: { + datasets: [{ + borderColor: Utils.CHART_COLORS.red, + borderWidth: 1, + radius: 0, + data: data, + }, + { + borderColor: Utils.CHART_COLORS.blue, + borderWidth: 1, + radius: 0, + data: data2, + }] + }, + options: { + animation, + interaction: { + intersect: false + }, + plugins: { + legend: false + }, + scales: { + x: { + type: 'linear' + } + } + } +}; +// + +module.exports = { + config +}; + +``` diff --git a/docs/samples/area/line-boundaries.md b/docs/samples/area/line-boundaries.md new file mode 100644 index 000000000..1f2c2fc05 --- /dev/null +++ b/docs/samples/area/line-boundaries.md @@ -0,0 +1,341 @@ +# Line Chart Boundaries + +:::: tabs + +::: tab "Fill: false" + +```js chart-editor +// +const inputs = { + min: -100, + max: 100, + count: 8, + decimals: 2, + continuity: 1 +}; + +const generateLabels = () => { + return Utils.months({count: inputs.count}); +}; + +const generateData = () => (Utils.numbers(inputs)); +// + +// +const data = { + labels: generateLabels(), + datasets: [ + { + label: 'Dataset', + data: generateData(), + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red), + fill: false + } + ] +}; +// + +// +let smooth = false; + +const actions = [ + { + name: 'Randomize', + handler(chart) { + // Utils.srand(Utils.rand()) + chart.data.datasets.forEach(dataset => { + dataset.data = generateData(); + }); + chart.update(); + } + }, + { + name: 'Smooth', + handler(chart) { + smooth = !smooth; + chart.options.elements.line.tension = smooth ? 0.4 : 0; + chart.update(); + } + } +]; +// + +// +const config = { + type: 'line', + data: data, + options: { + plugins: { + filler: { + propagate: false, + } + }, + interaction: { + intersect: false, + } + }, +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` + +::: + +::: tab "Fill: origin" + +```js chart-editor +// +const inputs = { + min: -100, + max: 100, + count: 8, + decimals: 2, + continuity: 1 +}; + +const generateLabels = () => { + return Utils.months({count: inputs.count}); +}; + +const generateData = () => (Utils.numbers(inputs)); +// + +// +const data = { + labels: generateLabels(), + datasets: [ + { + label: 'Dataset', + data: generateData(), + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red), + fill: 'origin' + } + ] +}; +// + +// +let smooth = false; + +const actions = [ + { + name: 'Randomize', + handler(chart) { + // Utils.srand(Utils.rand()) + chart.data.datasets.forEach(dataset => { + dataset.data = generateData(); + }); + chart.update(); + } + }, + { + name: 'Smooth', + handler(chart) { + smooth = !smooth; + chart.options.elements.line.tension = smooth ? 0.4 : 0; + chart.update(); + } + } +]; +// + +// +const config = { + type: 'line', + data: data, + options: { + plugins: { + filler: { + propagate: false, + } + }, + interaction: { + intersect: false + }, + }, +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` + +::: + +::: tab "Fill: start" + +```js chart-editor +// +const inputs = { + min: -100, + max: 100, + count: 8, + decimals: 2, + continuity: 1 +}; + +const generateLabels = () => { + return Utils.months({count: inputs.count}); +}; + +const generateData = () => (Utils.numbers(inputs)); +// + +// +const data = { + labels: generateLabels(), + datasets: [ + { + label: 'Dataset', + data: generateData(), + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red), + fill: 'start' + } + ] +}; +// + +// +let smooth = false; + +const actions = [ + { + name: 'Randomize', + handler(chart) { + // Utils.srand(Utils.rand()) + chart.data.datasets.forEach(dataset => { + dataset.data = generateData(); + }); + chart.update(); + } + }, + { + name: 'Smooth', + handler(chart) { + smooth = !smooth; + chart.options.elements.line.tension = smooth ? 0.4 : 0; + chart.update(); + } + } +]; +// + +// +const config = { + type: 'line', + data: data, + options: { + plugins: { + filler: { + propagate: false, + } + }, + interaction: { + intersect: false, + }, + }, +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` + +::: + +::: tab "Fill: end" + +```js chart-editor +// +const inputs = { + min: -100, + max: 100, + count: 8, + decimals: 2, + continuity: 1 +}; + +const generateLabels = () => { + return Utils.months({count: inputs.count}); +}; + +const generateData = () => (Utils.numbers(inputs)); +// + +// +const data = { + labels: generateLabels(), + datasets: [ + { + label: 'Dataset', + data: generateData(), + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red), + fill: 'end' + } + ] +}; +// + +// +let smooth = false; + +const actions = [ + { + name: 'Randomize', + handler(chart) { + // Utils.srand(Utils.rand()) + chart.data.datasets.forEach(dataset => { + dataset.data = generateData(); + }); + chart.update(); + } + }, + { + name: 'Smooth', + handler(chart) { + smooth = !smooth; + chart.options.elements.line.tension = smooth ? 0.4 : 0; + chart.update(); + } + } +]; +// + +// +const config = { + type: 'line', + data: data, + options: { + plugins: { + filler: { + propagate: false, + } + }, + interaction: { + intersect: false, + }, + }, +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` + +::: + +:::: diff --git a/docs/samples/area/line-datasets.md b/docs/samples/area/line-datasets.md new file mode 100644 index 000000000..12d89b6e0 --- /dev/null +++ b/docs/samples/area/line-datasets.md @@ -0,0 +1,166 @@ +# Line Chart Datasets + +```js chart-editor +// +const inputs = { + min: 20, + max: 80, + count: 8, + decimals: 2, + continuity: 1 +}; + +const generateLabels = () => { + return Utils.months({count: inputs.count}); +}; + +const generateData = () => (Utils.numbers(inputs)); + +Utils.srand(42); +// + +// +const data = { + labels: generateLabels(), + datasets: [ + { + label: 'D0', + data: generateData(), + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red), + hidden: true + }, + { + label: 'D1', + data: generateData(), + borderColor: Utils.CHART_COLORS.orange, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.orange), + fill: '-1' + }, + { + label: 'D2', + data: generateData(), + borderColor: Utils.CHART_COLORS.yellow, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.yellow), + hidden: true, + fill: 1 + }, + { + label: 'D3', + data: generateData(), + borderColor: Utils.CHART_COLORS.green, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.green), + fill: '-1' + }, + { + label: 'D4', + data: generateData(), + borderColor: Utils.CHART_COLORS.blue, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.blue), + fill: '-1' + }, + { + label: 'D5', + data: generateData(), + borderColor: Utils.CHART_COLORS.grey, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.grey), + fill: '+2' + }, + { + label: 'D6', + data: generateData(), + borderColor: Utils.CHART_COLORS.purple, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.purple), + fill: false + }, + { + label: 'D7', + data: generateData(), + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red), + fill: 8 + }, + { + label: 'D8', + data: generateData(), + borderColor: Utils.CHART_COLORS.orange, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.orange), + fill: 'end', + hidden: true + }, + { + label: 'D9', + data: generateData(), + borderColor: Utils.CHART_COLORS.yellow, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.yellow), + fill: {above: 'blue', below: 'red', target: {value: 350}} + } + ] +}; +// + +// +let smooth = false; +let propagate = false; + +const actions = [ + { + name: 'Randomize', + handler(chart) { + chart.data.datasets.forEach(dataset => { + dataset.data = generateData(); + }); + chart.update(); + } + }, + { + name: 'Propagate', + handler(chart) { + propagate = !propagate; + chart.options.plugins.filler.propagate = propagate; + chart.update(); + } + }, + { + name: 'Smooth', + handler(chart) { + smooth = !smooth; + chart.options.elements.line.tension = smooth ? 0.4 : 0; + chart.update(); + } + } +]; +// + +// +const config = { + type: 'line', + data: data, + options: { + scales: { + y: { + stacked: true + } + }, + plugins: { + filler: { + propagate: false + }, + 'samples-filler-analyser': { + target: 'chart-analyser' + } + }, + interaction: { + intersect: false, + }, + }, +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` + +
diff --git a/docs/samples/area/line-stacked.md b/docs/samples/area/line-stacked.md new file mode 100644 index 000000000..5f5924944 --- /dev/null +++ b/docs/samples/area/line-stacked.md @@ -0,0 +1,313 @@ +# Line Chart Stacked + +:::: tabs + +::: tab Stacked + +```js chart-editor +// +const actions = [ + { + name: 'Randomize', + handler(chart) { + chart.data.datasets.forEach(dataset => { + dataset.data = Utils.numbers({count: chart.data.labels.length, min: -100, max: 100}); + }); + chart.update(); + } + }, + { + name: 'Add Dataset', + handler(chart) { + const data = chart.data; + const dsColor = Utils.namedColor(chart.data.datasets.length); + const newDataset = { + label: 'Dataset ' + (data.datasets.length + 1), + backgroundColor: dsColor, + borderColor: dsColor, + fill: true, + data: Utils.numbers({count: data.labels.length, min: -100, max: 100}), + }; + chart.data.datasets.push(newDataset); + chart.update(); + } + }, + { + name: 'Add Data', + handler(chart) { + const data = chart.data; + if (data.datasets.length > 0) { + data.labels = Utils.months({count: data.labels.length + 1}); + + for (var index = 0; index < data.datasets.length; ++index) { + data.datasets[index].data.push(Utils.rand(-100, 100)); + } + + chart.update(); + } + } + }, + { + name: 'Remove Dataset', + handler(chart) { + chart.data.datasets.pop(); + chart.update(); + } + }, + { + name: 'Remove Data', + handler(chart) { + chart.data.labels.splice(-1, 1); // remove the label first + + chart.data.datasets.forEach(dataset => { + dataset.data.pop(); + }); + + chart.update(); + } + } +]; +// + +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100}; + +const labels = Utils.months({count: 7}); +const data = { + labels: labels, + datasets: [ + { + label: 'My First dataset', + data: Utils.numbers(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.CHART_COLORS.red, + fill: true + }, + { + label: 'My Second dataset', + data: Utils.numbers(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.blue, + backgroundColor: Utils.CHART_COLORS.blue, + fill: true + }, + { + label: 'My Third dataset', + data: Utils.numbers(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.green, + backgroundColor: Utils.CHART_COLORS.green, + fill: true + }, + { + label: 'My Fourth dataset', + data: Utils.numbers(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.yellow, + backgroundColor: Utils.CHART_COLORS.yellow, + fill: true + } + ] +}; +// + +// +const config = { + type: 'line', + data: data, + options: { + responsive: true, + plugins: { + title: { + display: true, + text: 'Chart.js Line Chart - stacked=true' + }, + tooltip: { + mode: 'index' + }, + }, + interaction: { + mode: 'nearest', + axis: 'x', + intersect: false + }, + scales: { + x: { + title: { + display: true, + text: 'Month' + } + }, + y: { + stacked: true, + title: { + display: true, + text: 'Value' + } + } + } + } +}; +// + +module.exports = { + actions: actions, + config: config +}; +``` + +::: + +::: tab "Stacked single" + +```js chart-editor +// +const actions = [ + { + name: 'Randomize', + handler(chart) { + chart.data.datasets.forEach(dataset => { + dataset.data = Utils.numbers({count: chart.data.labels.length, min: -100, max: 100}); + }); + chart.update(); + } + }, + { + name: 'Add Dataset', + handler(chart) { + const data = chart.data; + const dsColor = Utils.namedColor(chart.data.datasets.length); + const newDataset = { + label: 'Dataset ' + (data.datasets.length + 1), + backgroundColor: dsColor, + borderColor: dsColor, + fill: true, + data: Utils.numbers({count: data.labels.length, min: -100, max: 100}), + }; + chart.data.datasets.push(newDataset); + chart.update(); + } + }, + { + name: 'Add Data', + handler(chart) { + const data = chart.data; + if (data.datasets.length > 0) { + data.labels = Utils.months({count: data.labels.length + 1}); + + for (var index = 0; index < data.datasets.length; ++index) { + data.datasets[index].data.push(Utils.rand(-100, 100)); + } + + chart.update(); + } + } + }, + { + name: 'Remove Dataset', + handler(chart) { + chart.data.datasets.pop(); + chart.update(); + } + }, + { + name: 'Remove Data', + handler(chart) { + chart.data.labels.splice(-1, 1); // remove the label first + + chart.data.datasets.forEach(dataset => { + dataset.data.pop(); + }); + + chart.update(); + } + } +]; +// + +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100}; + +const labels = Utils.months({count: 7}); +const data = { + labels: labels, + datasets: [ + { + label: 'My First dataset', + data: Utils.numbers(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.CHART_COLORS.red, + fill: true + }, + { + label: 'My Second dataset', + data: Utils.numbers(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.blue, + backgroundColor: Utils.CHART_COLORS.blue, + fill: true + }, + { + label: 'My Third dataset', + data: Utils.numbers(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.green, + backgroundColor: Utils.CHART_COLORS.green, + fill: true + }, + { + label: 'My Fourth dataset', + data: Utils.numbers(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.yellow, + backgroundColor: Utils.CHART_COLORS.yellow, + fill: true + } + ] +}; +// + +// +const config = { + type: 'line', + data: data, + options: { + responsive: true, + plugins: { + title: { + display: true, + text: 'Chart.js Line Chart - stacked=single' + }, + tooltip: { + mode: 'index' + }, + }, + interaction: { + mode: 'nearest', + axis: 'x', + intersect: false + }, + scales: { + x: { + title: { + display: true, + text: 'Month' + } + }, + y: { + stacked: 'single', + title: { + display: true, + text: 'Value' + } + } + } + } +}; +// + +module.exports = { + actions: actions, + config: config +}; +``` + +::: + +:::: diff --git a/docs/samples/area/radar.md b/docs/samples/area/radar.md new file mode 100644 index 000000000..48011d0db --- /dev/null +++ b/docs/samples/area/radar.md @@ -0,0 +1,141 @@ +# Radar Chart Stacked + +```js chart-editor +// +const inputs = { + min: 8, + max: 16, + count: 8, + decimals: 2, + continuity: 1 +}; + +const generateLabels = () => { + return Utils.months({count: inputs.count}); +}; + +const generateData = () => { + const values = Utils.numbers(inputs); + inputs.from = values; + return values; +}; + +const labels = Utils.months({count: 8}); +const data = { + labels: generateLabels(), + datasets: [ + { + label: 'D0', + data: generateData(), + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red), + }, + { + label: 'D1', + data: generateData(), + borderColor: Utils.CHART_COLORS.orange, + hidden: true, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.orange), + fill: '-1' + }, + { + label: 'D2', + data: generateData(), + borderColor: Utils.CHART_COLORS.yellow, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.yellow), + fill: 1 + }, + { + label: 'D3', + data: generateData(), + borderColor: Utils.CHART_COLORS.green, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.green), + fill: false + }, + { + label: 'D4', + data: generateData(), + borderColor: Utils.CHART_COLORS.blue, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.blue), + fill: '-1' + }, + { + label: 'D5', + data: generateData(), + borderColor: Utils.CHART_COLORS.purple, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.purple), + fill: '-1' + }, + { + label: 'D6', + data: generateData(), + borderColor: Utils.CHART_COLORS.grey, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.grey), + fill: {value: 85} + } + ] +}; +// + +// +let smooth = false; +let propagate = false; + +const actions = [ + { + name: 'Randomize', + handler(chart) { + inputs.from = []; + chart.data.datasets.forEach(dataset => { + dataset.data = generateData(); + }); + chart.update(); + } + }, + { + name: 'Propagate', + handler(chart) { + propagate = !propagate; + chart.options.plugins.filler.propagate = propagate; + chart.update(); + + } + }, + { + name: 'Smooth', + handler(chart) { + smooth = !smooth; + chart.options.elements.line.tension = smooth ? 0.4 : 0; + chart.update(); + } + } +]; +// + +// +const config = { + type: 'radar', + data: data, + options: { + plugins: { + filler: { + propagate: false + }, + 'samples-filler-analyser': { + target: 'chart-analyser' + } + }, + interaction: { + intersect: false + } + } +}; +// + +module.exports = { + actions: actions, + config: config +}; +``` + +
diff --git a/docs/samples/bar/border-radius.md b/docs/samples/bar/border-radius.md new file mode 100644 index 000000000..110a97bf0 --- /dev/null +++ b/docs/samples/bar/border-radius.md @@ -0,0 +1,71 @@ +# Bar Chart Border Radius + +```js chart-editor +// +const actions = [ + { + name: 'Randomize', + handler(chart) { + chart.data.datasets.forEach(dataset => { + dataset.data = Utils.numbers({count: chart.data.labels.length, min: -100, max: 100}); + }); + chart.update(); + } + }, +]; +// + +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100}; + +const labels = Utils.months({count: 7}); +const data = { + labels: labels, + datasets: [ + { + label: 'Fully Rounded', + data: Utils.numbers(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red, 0.5), + borderWidth: 2, + borderRadius: Number.MAX_VALUE, + borderSkipped: false, + }, + { + label: 'Small Radius', + data: Utils.numbers(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.blue, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.blue, 0.5), + borderWidth: 2, + borderRadius: 5, + borderSkipped: false, + } + ] +}; +// + +// +const config = { + type: 'bar', + data: data, + options: { + responsive: true, + plugins: { + legend: { + position: 'top', + }, + title: { + display: true, + text: 'Chart.js Bar Chart' + } + } + }, +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` diff --git a/docs/samples/bar/floating.md b/docs/samples/bar/floating.md new file mode 100644 index 000000000..b0fb7dbf6 --- /dev/null +++ b/docs/samples/bar/floating.md @@ -0,0 +1,69 @@ +# Floating Bars + +```js chart-editor +// +const actions = [ + { + name: 'Randomize', + handler(chart) { + chart.data.datasets.forEach(dataset => { + dataset.data = chart.data.labels.map(() => { + return [Utils.rand(-100, 100), Utils.rand(-100, 100)]; + }); + }); + chart.update(); + } + }, +]; +// + +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100}; + +const labels = Utils.months({count: 7}); +const data = { + labels: labels, + datasets: [ + { + label: 'Dataset 1', + data: labels.map(() => { + return [Utils.rand(-100, 100), Utils.rand(-100, 100)]; + }), + backgroundColor: Utils.CHART_COLORS.red, + }, + { + label: 'Dataset 2', + data: labels.map(() => { + return [Utils.rand(-100, 100), Utils.rand(-100, 100)]; + }), + backgroundColor: Utils.CHART_COLORS.blue, + }, + ] +}; +// + +// +const config = { + type: 'bar', + data: data, + options: { + responsive: true, + plugins: { + legend: { + position: 'top', + }, + title: { + display: true, + text: 'Chart.js Floating Bar Chart' + } + } + } +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` diff --git a/docs/samples/bar/horizontal.md b/docs/samples/bar/horizontal.md new file mode 100644 index 000000000..a56eaa027 --- /dev/null +++ b/docs/samples/bar/horizontal.md @@ -0,0 +1,123 @@ +# Horizontal Bar Chart + +```js chart-editor +// +const actions = [ + { + name: 'Randomize', + handler(chart) { + chart.data.datasets.forEach(dataset => { + dataset.data = Utils.numbers({count: chart.data.labels.length, min: -100, max: 100}); + }); + chart.update(); + } + }, + { + name: 'Add Dataset', + handler(chart) { + const data = chart.data; + const dsColor = Utils.namedColor(chart.data.datasets.length); + const newDataset = { + label: 'Dataset ' + (data.datasets.length + 1), + backgroundColor: Utils.transparentize(dsColor, 0.5), + borderColor: dsColor, + borderWidth: 1, + data: Utils.numbers({count: data.labels.length, min: -100, max: 100}), + }; + chart.data.datasets.push(newDataset); + chart.update(); + } + }, + { + name: 'Add Data', + handler(chart) { + const data = chart.data; + if (data.datasets.length > 0) { + data.labels = Utils.months({count: data.labels.length + 1}); + + for (var index = 0; index < data.datasets.length; ++index) { + data.datasets[index].data.push(Utils.rand(-100, 100)); + } + + chart.update(); + } + } + }, + { + name: 'Remove Dataset', + handler(chart) { + chart.data.datasets.pop(); + chart.update(); + } + }, + { + name: 'Remove Data', + handler(chart) { + chart.data.labels.splice(-1, 1); // remove the label first + + chart.data.datasets.forEach(dataset => { + dataset.data.pop(); + }); + + chart.update(); + } + } +]; +// + +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100}; + +const labels = Utils.months({count: 7}); +const data = { + labels: labels, + datasets: [ + { + label: 'Dataset 1', + data: Utils.numbers(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red, 0.5), + }, + { + label: 'Dataset 2', + data: Utils.numbers(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.blue, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.blue, 0.5), + } + ] +}; +// + +// +const config = { + type: 'bar', + data: data, + options: { + indexAxis: 'y', + // Elements options apply to all of the options unless overridden in a dataset + // In this case, we are setting the border of each horizontal bar to be 2px wide + elements: { + bar: { + borderWidth: 2, + } + }, + responsive: true, + plugins: { + legend: { + position: 'right', + }, + title: { + display: true, + text: 'Chart.js Horizontal Bar Chart' + } + } + }, +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` diff --git a/docs/samples/bar/stacked-groups.md b/docs/samples/bar/stacked-groups.md new file mode 100644 index 000000000..515900bd3 --- /dev/null +++ b/docs/samples/bar/stacked-groups.md @@ -0,0 +1,79 @@ +# Stacked Bar Chart with Groups + +```js chart-editor +// +const actions = [ + { + name: 'Randomize', + handler(chart) { + chart.data.datasets.forEach(dataset => { + dataset.data = Utils.numbers({count: chart.data.labels.length, min: -100, max: 100}); + }); + chart.update(); + } + }, +]; +// + +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100}; + +const labels = Utils.months({count: 7}); +const data = { + labels: labels, + datasets: [ + { + label: 'Dataset 1', + data: Utils.numbers(NUMBER_CFG), + backgroundColor: Utils.CHART_COLORS.red, + stack: 'Stack 0', + }, + { + label: 'Dataset 2', + data: Utils.numbers(NUMBER_CFG), + backgroundColor: Utils.CHART_COLORS.blue, + stack: 'Stack 0', + }, + { + label: 'Dataset 3', + data: Utils.numbers(NUMBER_CFG), + backgroundColor: Utils.CHART_COLORS.green, + stack: 'Stack 1', + }, + ] +}; +// + +// +const config = { + type: 'bar', + data: data, + options: { + plugins: { + title: { + display: true, + text: 'Chart.js Bar Chart - Stacked' + }, + }, + responsive: true, + interaction: { + intersect: false, + }, + scales: { + x: { + stacked: true, + }, + y: { + stacked: true + } + } + } +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` diff --git a/docs/samples/bar/stacked.md b/docs/samples/bar/stacked.md new file mode 100644 index 000000000..855a55758 --- /dev/null +++ b/docs/samples/bar/stacked.md @@ -0,0 +1,73 @@ +# Stacked Bar Chart + +```js chart-editor +// +const actions = [ + { + name: 'Randomize', + handler(chart) { + chart.data.datasets.forEach(dataset => { + dataset.data = Utils.numbers({count: chart.data.labels.length, min: -100, max: 100}); + }); + chart.update(); + } + }, +]; +// + +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100}; + +const labels = Utils.months({count: 7}); +const data = { + labels: labels, + datasets: [ + { + label: 'Dataset 1', + data: Utils.numbers(NUMBER_CFG), + backgroundColor: Utils.CHART_COLORS.red, + }, + { + label: 'Dataset 2', + data: Utils.numbers(NUMBER_CFG), + backgroundColor: Utils.CHART_COLORS.blue, + }, + { + label: 'Dataset 3', + data: Utils.numbers(NUMBER_CFG), + backgroundColor: Utils.CHART_COLORS.green, + }, + ] +}; +// + +// +const config = { + type: 'bar', + data: data, + options: { + plugins: { + title: { + display: true, + text: 'Chart.js Bar Chart - Stacked' + }, + }, + responsive: true, + scales: { + x: { + stacked: true, + }, + y: { + stacked: true + } + } + } +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` diff --git a/docs/samples/bar/vertical.md b/docs/samples/bar/vertical.md new file mode 100644 index 000000000..e5540565e --- /dev/null +++ b/docs/samples/bar/vertical.md @@ -0,0 +1,115 @@ +# Vertical Bar Chart + +```js chart-editor +// +const actions = [ + { + name: 'Randomize', + handler(chart) { + chart.data.datasets.forEach(dataset => { + dataset.data = Utils.numbers({count: chart.data.labels.length, min: -100, max: 100}); + }); + chart.update(); + } + }, + { + name: 'Add Dataset', + handler(chart) { + const data = chart.data; + const dsColor = Utils.namedColor(chart.data.datasets.length); + const newDataset = { + label: 'Dataset ' + (data.datasets.length + 1), + backgroundColor: Utils.transparentize(dsColor, 0.5), + borderColor: dsColor, + borderWidth: 1, + data: Utils.numbers({count: data.labels.length, min: -100, max: 100}), + }; + chart.data.datasets.push(newDataset); + chart.update(); + } + }, + { + name: 'Add Data', + handler(chart) { + const data = chart.data; + if (data.datasets.length > 0) { + data.labels = Utils.months({count: data.labels.length + 1}); + + for (var index = 0; index < data.datasets.length; ++index) { + data.datasets[index].data.push(Utils.rand(-100, 100)); + } + + chart.update(); + } + } + }, + { + name: 'Remove Dataset', + handler(chart) { + chart.data.datasets.pop(); + chart.update(); + } + }, + { + name: 'Remove Data', + handler(chart) { + chart.data.labels.splice(-1, 1); // remove the label first + + chart.data.datasets.forEach(dataset => { + dataset.data.pop(); + }); + + chart.update(); + } + } +]; +// + +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100}; + +const labels = Utils.months({count: 7}); +const data = { + labels: labels, + datasets: [ + { + label: 'Dataset 1', + data: Utils.numbers(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red, 0.5), + }, + { + label: 'Dataset 2', + data: Utils.numbers(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.blue, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.blue, 0.5), + } + ] +}; +// + +// +const config = { + type: 'bar', + data: data, + options: { + responsive: true, + plugins: { + legend: { + position: 'top', + }, + title: { + display: true, + text: 'Chart.js Bar Chart' + } + } + }, +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` diff --git a/docs/samples/index.md b/docs/samples/index.md new file mode 100644 index 000000000..9a7f330f3 --- /dev/null +++ b/docs/samples/index.md @@ -0,0 +1 @@ +# Samples diff --git a/docs/samples/legend/point-style.md b/docs/samples/legend/point-style.md new file mode 100644 index 000000000..c8f4d6f66 --- /dev/null +++ b/docs/samples/legend/point-style.md @@ -0,0 +1,59 @@ +# Point Style + +This sample show how to use the dataset point style in the legend instead of a rectangle to identify each dataset.. + +```js chart-editor +// +const actions = [ + { + name: 'Toggle Point Style', + handler(chart) { + chart.options.plugins.legend.labels.usePointStyle = !chart.options.plugins.legend.labels.usePointStyle; + chart.update(); + } + }, +]; +// + +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100}; +const data = { + labels: Utils.months({count: DATA_COUNT}), + datasets: [ + { + label: 'Dataset 1', + data: Utils.numbers(NUMBER_CFG), + fill: false, + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red, 0.5), + borderWidth: 1, + pointStyle: 'rectRot', + pointRadius: 5, + pointBorderColor: 'rgb(0, 0, 0)' + }, + ] +}; +// + +// +const config = { + type: 'line', + data: data, + options: { + plugins: { + legend: { + labels: { + usePointStyle: true, + }, + } + } + } +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` \ No newline at end of file diff --git a/docs/samples/legend/position.md b/docs/samples/legend/position.md new file mode 100644 index 000000000..a729c77f1 --- /dev/null +++ b/docs/samples/legend/position.md @@ -0,0 +1,68 @@ +# Position + +This sample show how to change the position of the chart legend. + +```js chart-editor +// +const actions = [ + { + name: 'Position: top', + handler(chart) { + chart.options.plugins.legend.position = 'top'; + chart.update(); + } + }, + { + name: 'Position: right', + handler(chart) { + chart.options.plugins.legend.position = 'right'; + chart.update(); + } + }, + { + name: 'Position: bottom', + handler(chart) { + chart.options.plugins.legend.position = 'bottom'; + chart.update(); + } + }, + { + name: 'Position: left', + handler(chart) { + chart.options.plugins.legend.position = 'left'; + chart.update(); + } + }, +]; +// + + +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100}; +const data = { + labels: Utils.months({count: DATA_COUNT}), + datasets: [ + { + label: 'Dataset 1', + data: Utils.numbers(NUMBER_CFG), + fill: false, + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red, 0.5), + }, + ] +}; +// + +// +const config = { + type: 'line', + data: data, +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` \ No newline at end of file diff --git a/docs/samples/legend/title.md b/docs/samples/legend/title.md new file mode 100644 index 000000000..ebc6856f4 --- /dev/null +++ b/docs/samples/legend/title.md @@ -0,0 +1,74 @@ +# Alignment and Title Position + +This sample show how to configure the alignment and title position of the chart legend. + +```js chart-editor +// +const actions = [ + { + name: 'Title Position: start', + handler(chart) { + chart.options.plugins.legend.align = 'start'; + chart.options.plugins.legend.title.position = 'start'; + chart.update(); + } + }, + { + name: 'Title Position: center (default)', + handler(chart) { + chart.options.plugins.legend.align = 'center'; + chart.options.plugins.legend.title.position = 'center'; + chart.update(); + } + }, + { + name: 'Title Position: end', + handler(chart) { + chart.options.plugins.legend.align = 'end'; + chart.options.plugins.legend.title.position = 'end'; + chart.update(); + } + }, +]; +// + + +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100}; +const data = { + labels: Utils.months({count: DATA_COUNT}), + datasets: [ + { + label: 'Dataset 1', + data: Utils.numbers(NUMBER_CFG), + fill: false, + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red, 0.5), + }, + ] +}; +// + +// +const config = { + type: 'line', + data: data, + options: { + plugins: { + legend: { + title: { + display: true, + text: 'Legend Title', + } + } + } + } +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` \ No newline at end of file diff --git a/docs/samples/line/interpolation.md b/docs/samples/line/interpolation.md new file mode 100644 index 000000000..77f287b78 --- /dev/null +++ b/docs/samples/line/interpolation.md @@ -0,0 +1,77 @@ +# Interpolation Modes + +```js chart-editor +// +const DATA_COUNT = 12; +const labels = []; +for (let i = 0; i < DATA_COUNT; ++i) { + labels.push(i.toString()); +} +const datapoints = [0, 20, 20, 60, 60, 120, NaN, 180, 120, 125, 105, 110, 170]; +const data = { + labels: labels, + datasets: [ + { + label: 'Cubic interpolation (monotone)', + data: datapoints, + borderColor: Utils.CHART_COLORS.red, + fill: false, + cubicInterpolationMode: 'monotone', + tension: 0.4 + }, { + label: 'Cubic interpolation', + data: datapoints, + borderColor: Utils.CHART_COLORS.blue, + fill: false, + tension: 0.4 + }, { + label: 'Linear interpolation (default)', + data: datapoints, + borderColor: Utils.CHART_COLORS.green, + fill: false + } + ] +}; +// + +// +const config = { + type: 'line', + data: data, + options: { + responsive: true, + plugins: { + title: { + display: true, + text: 'Chart.js Line Chart - Cubic interpolation mode' + }, + }, + interaction: { + intersect: false, + }, + scales: { + x: { + display: true, + title: { + display: true + } + }, + y: { + display: true, + title: { + display: true, + text: 'Value' + }, + suggestedMin: -10, + suggestedMax: 200 + } + } + }, +}; +// + +module.exports = { + actions: [], + config: config, +}; +``` diff --git a/docs/samples/line/line.md b/docs/samples/line/line.md new file mode 100644 index 000000000..9510ccf0a --- /dev/null +++ b/docs/samples/line/line.md @@ -0,0 +1,115 @@ +# Line Chart + +```js chart-editor +// +const actions = [ + { + name: 'Randomize', + handler(chart) { + chart.data.datasets.forEach(dataset => { + dataset.data = Utils.numbers({count: chart.data.labels.length, min: -100, max: 100}); + }); + chart.update(); + } + }, + { + name: 'Add Dataset', + handler(chart) { + const data = chart.data; + const dsColor = Utils.namedColor(chart.data.datasets.length); + const newDataset = { + label: 'Dataset ' + (data.datasets.length + 1), + backgroundColor: Utils.transparentize(dsColor, 0.5), + borderColor: dsColor, + borderWidth: 1, + data: Utils.numbers({count: data.labels.length, min: -100, max: 100}), + }; + chart.data.datasets.push(newDataset); + chart.update(); + } + }, + { + name: 'Add Data', + handler(chart) { + const data = chart.data; + if (data.datasets.length > 0) { + data.labels = Utils.months({count: data.labels.length + 1}); + + for (var index = 0; index < data.datasets.length; ++index) { + data.datasets[index].data.push(Utils.rand(-100, 100)); + } + + chart.update(); + } + } + }, + { + name: 'Remove Dataset', + handler(chart) { + chart.data.datasets.pop(); + chart.update(); + } + }, + { + name: 'Remove Data', + handler(chart) { + chart.data.labels.splice(-1, 1); // remove the label first + + chart.data.datasets.forEach(dataset => { + dataset.data.pop(); + }); + + chart.update(); + } + } +]; +// + +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100}; + +const labels = Utils.months({count: 7}); +const data = { + labels: labels, + datasets: [ + { + label: 'Dataset 1', + data: Utils.numbers(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red, 0.5), + }, + { + label: 'Dataset 2', + data: Utils.numbers(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.blue, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.blue, 0.5), + } + ] +}; +// + +// +const config = { + type: 'line', + data: data, + options: { + responsive: true, + plugins: { + legend: { + position: 'top', + }, + title: { + display: true, + text: 'Chart.js Line Chart' + } + } + }, +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` diff --git a/docs/samples/line/multi-axis.md b/docs/samples/line/multi-axis.md new file mode 100644 index 000000000..b9850c29a --- /dev/null +++ b/docs/samples/line/multi-axis.md @@ -0,0 +1,86 @@ +# Multi Axis Line Chart + +```js chart-editor +// +const actions = [ + { + name: 'Randomize', + handler(chart) { + chart.data.datasets.forEach(dataset => { + dataset.data = Utils.numbers({count: chart.data.labels.length, min: -100, max: 100}); + }); + chart.update(); + } + }, +]; +// + +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100}; + +const labels = Utils.months({count: 7}); +const data = { + labels: labels, + datasets: [ + { + label: 'Dataset 1', + data: Utils.numbers(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red, 0.5), + yAxisID: 'y', + }, + { + label: 'Dataset 2', + data: Utils.numbers(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.blue, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.blue, 0.5), + yAxisID: 'y1', + } + ] +}; +// + +// +const config = { + type: 'line', + data: data, + options: { + responsive: true, + interaction: { + mode: 'index', + intersect: false, + }, + stacked: false, + plugins: { + title: { + display: true, + text: 'Chart.js Line Chart - Multi Axis' + } + }, + scales: { + y: { + type: 'linear', + display: true, + position: 'left', + }, + y1: { + type: 'linear', + display: true, + position: 'right', + + // grid line settings + grid: { + drawOnChartArea: false, // only want the grid lines for one axis to show up + }, + }, + } + }, +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` diff --git a/docs/samples/line/stepped.md b/docs/samples/line/stepped.md new file mode 100644 index 000000000..9c5c6ac04 --- /dev/null +++ b/docs/samples/line/stepped.md @@ -0,0 +1,50 @@ +# Stepped Line Charts + +```js chart-editor +// +const data = { + labels: ['Day 1', 'Day 2', 'Day 3', 'Day 4', 'Day 5', 'Day 6'], + datasets: [ + { + label: 'Dataset', + data: Utils.numbers({count: 6, min: -100, max: 100}), + borderColor: Utils.CHART_COLORS.red, + fill: false, + + // Change the stepped mode to explore different stepped chart options + // false: no stepping + // true: stepped before interpolation + // 'before': step before interpolation + // 'after': step after interpolation + // 'middle': step middle interpolation + stepped: true, + } + ] +}; +// + +// +const config = { + type: 'line', + data: data, + options: { + responsive: true, + interaction: { + intersect: false, + axis: 'x' + }, + plugins: { + title: { + display: true, + text: (ctx) => 'Step ' + ctx.chart.data.datasets[0].stepped + ' Interpolation', + } + } + } +}; +// + +module.exports = { + actions: [], + config: config, +}; +``` diff --git a/docs/samples/line/styling.md b/docs/samples/line/styling.md new file mode 100644 index 000000000..c506166b2 --- /dev/null +++ b/docs/samples/line/styling.md @@ -0,0 +1,76 @@ +# Line Styling + +```js chart-editor +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100}; + +const labels = Utils.months({count: DATA_COUNT}); +const data = { + labels: labels, + datasets: [ + { + label: 'Unfilled', + fill: false, + backgroundColor: Utils.CHART_COLORS.blue, + borderColor: Utils.CHART_COLORS.blue, + data: Utils.numbers(NUMBER_CFG), + }, { + label: 'Dashed', + fill: false, + backgroundColor: Utils.CHART_COLORS.green, + borderColor: Utils.CHART_COLORS.green, + borderDash: [5, 5], + data: Utils.numbers(NUMBER_CFG), + }, { + label: 'Filled', + backgroundColor: Utils.CHART_COLORS.red, + borderColor: Utils.CHART_COLORS.red, + data: Utils.numbers(NUMBER_CFG), + fill: true, + } + ] +}; +// + +// +const config = { + type: 'line', + data: data, + options: { + responsive: true, + plugins: { + title: { + display: true, + text: 'Chart.js Line Chart' + }, + }, + interaction: { + mode: 'index', + intersect: false + }, + scales: { + x: { + display: true, + title: { + display: true, + text: 'Month' + } + }, + y: { + display: true, + title: { + display: true, + text: 'Value' + } + } + } + }, +}; +// + +module.exports = { + actions: [], + config: config, +}; +``` diff --git a/docs/samples/other-charts/bubble.md b/docs/samples/other-charts/bubble.md new file mode 100644 index 000000000..3d330fb19 --- /dev/null +++ b/docs/samples/other-charts/bubble.md @@ -0,0 +1,113 @@ +# Bubble + +```js chart-editor +// +const actions = [ + { + name: 'Randomize', + handler(chart) { + chart.data.datasets.forEach(dataset => { + dataset.data = Utils.bubbles({count: chart.data.labels.length, rmin: 5, rmax: 15, min: 0, max: 100}); + }); + chart.update(); + } + }, + { + name: 'Add Dataset', + handler(chart) { + const data = chart.data; + const dsColor = Utils.namedColor(chart.data.datasets.length); + const newDataset = { + label: 'Dataset ' + (data.datasets.length + 1), + backgroundColor: Utils.transparentize(dsColor, 0.5), + borderColor: dsColor, + data: Utils.bubbles({count: data.labels.length, rmin: 5, rmax: 15, min: 0, max: 100}), + }; + chart.data.datasets.push(newDataset); + chart.update(); + } + }, + { + name: 'Add Data', + handler(chart) { + const data = chart.data; + if (data.datasets.length > 0) { + + for (var index = 0; index < data.datasets.length; ++index) { + data.datasets[index].data.push(Utils.bubbles({count: 1, rmin: 5, rmax: 15, min: 0, max: 100})[0]); + } + + chart.update(); + } + } + }, + { + name: 'Remove Dataset', + handler(chart) { + chart.data.datasets.pop(); + chart.update(); + } + }, + { + name: 'Remove Data', + handler(chart) { + chart.data.labels.splice(-1, 1); // remove the label first + + chart.data.datasets.forEach(dataset => { + dataset.data.pop(); + }); + + chart.update(); + } + } +]; +// + +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, rmin: 5, rmax: 15, min: 0, max: 100}; + +const labels = Utils.months({count: 7}); +const data = { + labels: labels, + datasets: [ + { + label: 'Dataset 1', + data: Utils.bubbles(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red, 0.5), + }, + { + label: 'Dataset 2', + data: Utils.bubbles(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.orange, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.orange, 0.5), + } + ] +}; +// + +// +const config = { + type: 'bubble', + data: data, + options: { + responsive: true, + plugins: { + legend: { + position: 'top', + }, + title: { + display: true, + text: 'Chart.js Bubble Chart' + } + } + }, +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` diff --git a/docs/samples/other-charts/combo-bar-line.md b/docs/samples/other-charts/combo-bar-line.md new file mode 100644 index 000000000..16013d9ad --- /dev/null +++ b/docs/samples/other-charts/combo-bar-line.md @@ -0,0 +1,118 @@ +# Combo bar/line + +```js chart-editor +// +const actions = [ + { + name: 'Randomize', + handler(chart) { + chart.data.datasets.forEach(dataset => { + dataset.data = Utils.numbers({count: chart.data.labels.length, min: -100, max: 100}); + }); + chart.update(); + } + }, + { + name: 'Add Dataset', + handler(chart) { + const data = chart.data; + const dsColor = Utils.namedColor(chart.data.datasets.length); + const newDataset = { + label: 'Dataset ' + (data.datasets.length + 1), + backgroundColor: Utils.transparentize(dsColor, 0.5), + borderColor: dsColor, + borderWidth: 1, + data: Utils.numbers({count: data.labels.length, min: -100, max: 100}), + }; + chart.data.datasets.push(newDataset); + chart.update(); + } + }, + { + name: 'Add Data', + handler(chart) { + const data = chart.data; + if (data.datasets.length > 0) { + data.labels = Utils.months({count: data.labels.length + 1}); + + for (var index = 0; index < data.datasets.length; ++index) { + data.datasets[index].data.push(Utils.rand(-100, 100)); + } + + chart.update(); + } + } + }, + { + name: 'Remove Dataset', + handler(chart) { + chart.data.datasets.pop(); + chart.update(); + } + }, + { + name: 'Remove Data', + handler(chart) { + chart.data.labels.splice(-1, 1); // remove the label first + + chart.data.datasets.forEach(dataset => { + dataset.data.pop(); + }); + + chart.update(); + } + } +]; +// + +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100}; + +const labels = Utils.months({count: 7}); +const data = { + labels: labels, + datasets: [ + { + label: 'Dataset 1', + data: Utils.numbers(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red, 0.5), + order: 1 + }, + { + label: 'Dataset 2', + data: Utils.numbers(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.blue, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.blue, 0.5), + type: 'line', + order: 0 + } + ] +}; +// + +// +const config = { + type: 'bar', + data: data, + options: { + responsive: true, + plugins: { + legend: { + position: 'top', + }, + title: { + display: true, + text: 'Chart.js Combined Line/Bar Chart' + } + } + }, +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` diff --git a/docs/samples/other-charts/doughnut.md b/docs/samples/other-charts/doughnut.md new file mode 100644 index 000000000..bbe7d9f89 --- /dev/null +++ b/docs/samples/other-charts/doughnut.md @@ -0,0 +1,112 @@ +# Doughnut + +```js chart-editor +// +const actions = [ + { + name: 'Randomize', + handler(chart) { + chart.data.datasets.forEach(dataset => { + dataset.data = Utils.numbers({count: chart.data.labels.length, min: 0, max: 100}); + }); + chart.update(); + } + }, + { + name: 'Add Dataset', + handler(chart) { + const data = chart.data; + const newDataset = { + label: 'Dataset ' + (data.datasets.length + 1), + backgroundColor: [], + data: [], + }; + + for (let i = 0; i < data.labels.length; i++) { + newDataset.data.push(Utils.numbers({count: 1, min: 0, max: 100})); + + const colorIndex = i % Object.keys(Utils.CHART_COLORS).length; + newDataset.backgroundColor.push(Object.values(Utils.CHART_COLORS)[colorIndex]); + } + + chart.data.datasets.push(newDataset); + chart.update(); + } + }, + { + name: 'Add Data', + handler(chart) { + const data = chart.data; + if (data.datasets.length > 0) { + data.labels.push('data #' + (data.labels.length + 1)); + + for (var index = 0; index < data.datasets.length; ++index) { + data.datasets[index].data.push(Utils.rand(0, 100)); + } + + chart.update(); + } + } + }, + { + name: 'Remove Dataset', + handler(chart) { + chart.data.datasets.pop(); + chart.update(); + } + }, + { + name: 'Remove Data', + handler(chart) { + chart.data.labels.splice(-1, 1); // remove the label first + + chart.data.datasets.forEach(dataset => { + dataset.data.pop(); + }); + + chart.update(); + } + } +]; +// + +// +const DATA_COUNT = 5; +const NUMBER_CFG = {count: DATA_COUNT, min: 0, max: 100}; + +const data = { + labels: ['Red', 'Orange', 'Yellow', 'Green', 'Blue'], + datasets: [ + { + label: 'Dataset 1', + data: Utils.numbers(NUMBER_CFG), + backgroundColor: Object.values(Utils.CHART_COLORS), + } + ] +}; +// + +// +const config = { + type: 'doughnut', + data: data, + options: { + responsive: true, + plugins: { + legend: { + position: 'top', + }, + title: { + display: true, + text: 'Chart.js Doughnut Chart' + } + } + }, +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` diff --git a/docs/samples/other-charts/multi-series-pie.md b/docs/samples/other-charts/multi-series-pie.md new file mode 100644 index 000000000..4a7d11e47 --- /dev/null +++ b/docs/samples/other-charts/multi-series-pie.md @@ -0,0 +1,91 @@ +# Multi Series Pie + +```js chart-editor +// +const DATA_COUNT = 5; +const NUMBER_CFG = {count: DATA_COUNT, min: 0, max: 100}; + +const labels = Utils.months({count: 7}); +const data = { + labels: ['Overall Yay', 'Overall Nay', 'Group A Yay', 'Group A Nay', 'Group B Yay', 'Group B Nay', 'Group C Yay', 'Group C Nay'], + datasets: [ + { + backgroundColor: ['#AAA', '#777'], + data: [21, 79] + }, + { + backgroundColor: ['hsl(0, 100%, 60%)', 'hsl(0, 100%, 35%)'], + data: [33, 67] + }, + { + backgroundColor: ['hsl(100, 100%, 60%)', 'hsl(100, 100%, 35%)'], + data: [20, 80] + }, + { + backgroundColor: ['hsl(180, 100%, 60%)', 'hsl(180, 100%, 35%)'], + data: [10, 90] + } + ] +}; +// + +// +const config = { + type: 'pie', + data: data, + options: { + responsive: true, + plugins: { + legend: { + labels: { + generateLabels: function(chart) { + // Get the default label list + const original = Chart.overrides.pie.plugins.legend.labels.generateLabels; + const labelsOriginal = original.call(this, chart); + + // Build an array of colors used in the datasets of the chart + var datasetColors = chart.data.datasets.map(function(e) { + return e.backgroundColor; + }); + datasetColors = datasetColors.flat(); + + // Modify the color and hide state of each label + labelsOriginal.forEach(label => { + // There are twice as many labels as there are datasets. This converts the label index into the corresponding dataset index + label.datasetIndex = (label.index - label.index % 2) / 2; + + // The hidden state must match the dataset's hidden state + label.hidden = !chart.isDatasetVisible(label.datasetIndex); + + // Change the color to match the dataset + label.fillStyle = datasetColors[label.index]; + }); + + return labelsOriginal; + } + }, + onClick: function(mouseEvent, legendItem, legend) { + // toggle the visibility of the dataset from what it currently is + legend.chart.getDatasetMeta( + legendItem.datasetIndex + ).hidden = legend.chart.isDatasetVisible(legendItem.datasetIndex); + legend.chart.update(); + } + }, + tooltip: { + callbacks: { + label: function(context) { + const labelIndex = (context.datasetIndex * 2) + context.dataIndex; + return context.chart.data.labels[labelIndex] + ': ' + context.formattedValue; + } + } + } + } + }, +}; +// + +module.exports = { + config: config, +}; +``` diff --git a/docs/samples/other-charts/pie.md b/docs/samples/other-charts/pie.md new file mode 100644 index 000000000..357862507 --- /dev/null +++ b/docs/samples/other-charts/pie.md @@ -0,0 +1,112 @@ +# Pie + +```js chart-editor +// +const actions = [ + { + name: 'Randomize', + handler(chart) { + chart.data.datasets.forEach(dataset => { + dataset.data = Utils.numbers({count: chart.data.labels.length, min: 0, max: 100}); + }); + chart.update(); + } + }, + { + name: 'Add Dataset', + handler(chart) { + const data = chart.data; + const newDataset = { + label: 'Dataset ' + (data.datasets.length + 1), + backgroundColor: [], + data: [], + }; + + for (let i = 0; i < data.labels.length; i++) { + newDataset.data.push(Utils.numbers({count: 1, min: 0, max: 100})); + + const colorIndex = i % Object.keys(Utils.CHART_COLORS).length; + newDataset.backgroundColor.push(Object.values(Utils.CHART_COLORS)[colorIndex]); + } + + chart.data.datasets.push(newDataset); + chart.update(); + } + }, + { + name: 'Add Data', + handler(chart) { + const data = chart.data; + if (data.datasets.length > 0) { + data.labels.push('data #' + (data.labels.length + 1)); + + for (var index = 0; index < data.datasets.length; ++index) { + data.datasets[index].data.push(Utils.rand(0, 100)); + } + + chart.update(); + } + } + }, + { + name: 'Remove Dataset', + handler(chart) { + chart.data.datasets.pop(); + chart.update(); + } + }, + { + name: 'Remove Data', + handler(chart) { + chart.data.labels.splice(-1, 1); // remove the label first + + chart.data.datasets.forEach(dataset => { + dataset.data.pop(); + }); + + chart.update(); + } + } +]; +// + +// +const DATA_COUNT = 5; +const NUMBER_CFG = {count: DATA_COUNT, min: 0, max: 100}; + +const data = { + labels: ['Red', 'Orange', 'Yellow', 'Green', 'Blue'], + datasets: [ + { + label: 'Dataset 1', + data: Utils.numbers(NUMBER_CFG), + backgroundColor: Object.values(Utils.CHART_COLORS), + } + ] +}; +// + +// +const config = { + type: 'pie', + data: data, + options: { + responsive: true, + plugins: { + legend: { + position: 'top', + }, + title: { + display: true, + text: 'Chart.js Pie Chart' + } + } + }, +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` diff --git a/docs/samples/other-charts/polar-area.md b/docs/samples/other-charts/polar-area.md new file mode 100644 index 000000000..826a39991 --- /dev/null +++ b/docs/samples/other-charts/polar-area.md @@ -0,0 +1,91 @@ +# Polar area + +```js chart-editor +// +const actions = [ + { + name: 'Randomize', + handler(chart) { + chart.data.datasets.forEach(dataset => { + dataset.data = Utils.numbers({count: chart.data.labels.length, min: 0, max: 100}); + }); + chart.update(); + } + }, + { + name: 'Add Data', + handler(chart) { + const data = chart.data; + if (data.datasets.length > 0) { + data.labels.push('data #' + (data.labels.length + 1)); + + for (var index = 0; index < data.datasets.length; ++index) { + data.datasets[index].data.push(Utils.rand(0, 100)); + } + + chart.update(); + } + } + }, + { + name: 'Remove Data', + handler(chart) { + chart.data.labels.splice(-1, 1); // remove the label first + + chart.data.datasets.forEach(dataset => { + dataset.data.pop(); + }); + + chart.update(); + } + } +]; +// + +// +const DATA_COUNT = 5; +const NUMBER_CFG = {count: DATA_COUNT, min: 0, max: 100}; + +const labels = ['Red', 'Orange', 'Yellow', 'Green', 'Blue']; +const data = { + labels: labels, + datasets: [ + { + label: 'Dataset 1', + data: Utils.numbers(NUMBER_CFG), + backgroundColor: [ + Utils.transparentize(Utils.CHART_COLORS.red, 0.5), + Utils.transparentize(Utils.CHART_COLORS.orange, 0.5), + Utils.transparentize(Utils.CHART_COLORS.yellow, 0.5), + Utils.transparentize(Utils.CHART_COLORS.green, 0.5), + Utils.transparentize(Utils.CHART_COLORS.blue, 0.5), + ] + } + ] +}; +// + +// +const config = { + type: 'polarArea', + data: data, + options: { + responsive: true, + plugins: { + legend: { + position: 'top', + }, + title: { + display: true, + text: 'Chart.js Polar Area Chart' + } + } + }, +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` diff --git a/docs/samples/other-charts/radar-skip-points.md b/docs/samples/other-charts/radar-skip-points.md new file mode 100644 index 000000000..33f0bf175 --- /dev/null +++ b/docs/samples/other-charts/radar-skip-points.md @@ -0,0 +1,87 @@ +# Radar skip points + +```js chart-editor +// +const actions = [ + { + name: 'Randomize', + handler(chart) { + chart.data.datasets.forEach((dataset, i) => { + const data = Utils.numbers({count: chart.data.labels.length, min: 0, max: 100}); + + if (i === 0) { + data[0] = null; + } else if (i === 1) { + data[Number.parseInt(data.length / 2, 10)] = null; + } else { + data[data.length - 1] = null; + } + + dataset.data = data; + }); + chart.update(); + } + } +]; +// + +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, min: 0, max: 100}; + +const labels = Utils.months({count: 7}); +const dataFirstSkip = Utils.numbers(NUMBER_CFG); +const dataMiddleSkip = Utils.numbers(NUMBER_CFG); +const dataLastSkip = Utils.numbers(NUMBER_CFG); + +dataFirstSkip[0] = null; +dataMiddleSkip[Number.parseInt(dataMiddleSkip.length / 2, 10)] = null; +dataLastSkip[dataLastSkip.length - 1] = null; + +const data = { + labels: labels, + datasets: [ + { + label: 'Skip first dataset', + data: dataFirstSkip, + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red, 0.5), + }, + { + label: 'Skip mid dataset', + data: dataMiddleSkip, + borderColor: Utils.CHART_COLORS.blue, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.blue, 0.5), + }, + { + label: 'Skip last dataset', + data: dataLastSkip, + borderColor: Utils.CHART_COLORS.green, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.green, 0.5), + } + ] +}; +// + +// +const config = { + type: 'radar', + data: data, + options: { + responsive: true, + plugins: { + title: { + display: true, + text: 'Chart.js Radar Skip Points Chart' + } + } + }, +}; +// + +module.exports = { + actions: actions, + config: config +}; +``` + diff --git a/docs/samples/other-charts/radar.md b/docs/samples/other-charts/radar.md new file mode 100644 index 000000000..e6fc7a890 --- /dev/null +++ b/docs/samples/other-charts/radar.md @@ -0,0 +1,111 @@ +# Radar + +```js chart-editor +// +const actions = [ + { + name: 'Randomize', + handler(chart) { + chart.data.datasets.forEach(dataset => { + dataset.data = Utils.numbers({count: chart.data.labels.length, min: 0, max: 100}); + }); + chart.update(); + } + }, + { + name: 'Add Dataset', + handler(chart) { + const data = chart.data; + const dsColor = Utils.namedColor(chart.data.datasets.length); + const newDataset = { + label: 'Dataset ' + (data.datasets.length + 1), + backgroundColor: Utils.transparentize(dsColor, 0.5), + borderColor: dsColor, + data: Utils.numbers({count: data.labels.length, min: 0, max: 100}), + }; + chart.data.datasets.push(newDataset); + chart.update(); + } + }, + { + name: 'Add Data', + handler(chart) { + const data = chart.data; + if (data.datasets.length > 0) { + data.labels = Utils.months({count: data.labels.length + 1}); + + for (var index = 0; index < data.datasets.length; ++index) { + data.datasets[index].data.push(Utils.rand(0, 100)); + } + + chart.update(); + } + } + }, + { + name: 'Remove Dataset', + handler(chart) { + chart.data.datasets.pop(); + chart.update(); + } + }, + { + name: 'Remove Data', + handler(chart) { + chart.data.labels.splice(-1, 1); // remove the label first + + chart.data.datasets.forEach(dataset => { + dataset.data.pop(); + }); + + chart.update(); + } + } +]; +// + +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, min: 0, max: 100}; + +const labels = Utils.months({count: 7}); +const data = { + labels: labels, + datasets: [ + { + label: 'Dataset 1', + data: Utils.numbers(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red, 0.5), + }, + { + label: 'Dataset 2', + data: Utils.numbers(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.blue, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.blue, 0.5), + } + ] +}; +// + +// +const config = { + type: 'radar', + data: data, + options: { + responsive: true, + plugins: { + title: { + display: true, + text: 'Chart.js Radar Chart' + } + } + }, +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` diff --git a/docs/samples/other-charts/scatter-multi-axis.md b/docs/samples/other-charts/scatter-multi-axis.md new file mode 100644 index 000000000..9241d720d --- /dev/null +++ b/docs/samples/other-charts/scatter-multi-axis.md @@ -0,0 +1,135 @@ +# Scatter - Multi axis + +```js chart-editor +// +const actions = [ + { + name: 'Randomize', + handler(chart) { + chart.data.datasets.forEach(dataset => { + dataset.data = Utils.bubbles({count: chart.data.labels.length, rmin: 1, rmax: 1, min: -100, max: 100}); + }); + chart.update(); + } + }, + { + name: 'Add Dataset', + handler(chart) { + const data = chart.data; + const dsColor = Utils.namedColor(chart.data.datasets.length); + const newDataset = { + label: 'Dataset ' + (data.datasets.length + 1), + backgroundColor: Utils.transparentize(dsColor, 0.5), + borderColor: dsColor, + data: Utils.bubbles({count: data.labels.length, rmin: 1, rmax: 1, min: -100, max: 100}), + }; + chart.data.datasets.push(newDataset); + chart.update(); + } + }, + { + name: 'Add Data', + handler(chart) { + const data = chart.data; + if (data.datasets.length > 0) { + + for (var index = 0; index < data.datasets.length; ++index) { + data.datasets[index].data.push(Utils.bubbles({count: 1, rmin: 1, rmax: 1, min: -100, max: 100})[0]); + } + + chart.update(); + } + } + }, + { + name: 'Remove Dataset', + handler(chart) { + chart.data.datasets.pop(); + chart.update(); + } + }, + { + name: 'Remove Data', + handler(chart) { + chart.data.labels.splice(-1, 1); // remove the label first + + chart.data.datasets.forEach(dataset => { + dataset.data.pop(); + }); + + chart.update(); + } + } +]; +// + +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, rmin: 1, rmax: 1, min: -100, max: 100}; + +const labels = Utils.months({count: 7}); +const data = { + labels: labels, + datasets: [ + { + label: 'Dataset 1', + data: Utils.bubbles(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red, 0.5), + yAxisID: 'y', + }, + { + label: 'Dataset 2', + data: Utils.bubbles(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.orange, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.orange, 0.5), + yAxisID: 'y2', + } + ] +}; +// + +// +const config = { + type: 'scatter', + data: data, + options: { + responsive: true, + plugins: { + legend: { + position: 'top', + }, + title: { + display: true, + text: 'Chart.js Scatter Multi Axis Chart' + } + }, + scales: { + y: { + type: 'linear', // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance + position: 'left', + ticks: { + color: Utils.CHART_COLORS.red + } + }, + y2: { + type: 'linear', // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance + position: 'right', + reverse: true, + ticks: { + color: Utils.CHART_COLORS.blue + }, + grid: { + drawOnChartArea: false // only want the grid lines for one axis to show up + } + } + } + }, +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` diff --git a/docs/samples/other-charts/scatter.md b/docs/samples/other-charts/scatter.md new file mode 100644 index 000000000..d6690575e --- /dev/null +++ b/docs/samples/other-charts/scatter.md @@ -0,0 +1,113 @@ +# Scatter + +```js chart-editor +// +const actions = [ + { + name: 'Randomize', + handler(chart) { + chart.data.datasets.forEach(dataset => { + dataset.data = Utils.bubbles({count: chart.data.labels.length, rmin: 1, rmax: 1, min: 0, max: 100}); + }); + chart.update(); + } + }, + { + name: 'Add Dataset', + handler(chart) { + const data = chart.data; + const dsColor = Utils.namedColor(chart.data.datasets.length); + const newDataset = { + label: 'Dataset ' + (data.datasets.length + 1), + backgroundColor: Utils.transparentize(dsColor, 0.5), + borderColor: dsColor, + data: Utils.bubbles({count: data.labels.length, rmin: 1, rmax: 1, min: 0, max: 100}), + }; + chart.data.datasets.push(newDataset); + chart.update(); + } + }, + { + name: 'Add Data', + handler(chart) { + const data = chart.data; + if (data.datasets.length > 0) { + + for (var index = 0; index < data.datasets.length; ++index) { + data.datasets[index].data.push(Utils.bubbles({count: 1, rmin: 1, rmax: 1, min: 0, max: 100})[0]); + } + + chart.update(); + } + } + }, + { + name: 'Remove Dataset', + handler(chart) { + chart.data.datasets.pop(); + chart.update(); + } + }, + { + name: 'Remove Data', + handler(chart) { + chart.data.labels.splice(-1, 1); // remove the label first + + chart.data.datasets.forEach(dataset => { + dataset.data.pop(); + }); + + chart.update(); + } + } +]; +// + +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, rmin: 1, rmax: 1, min: 0, max: 100}; + +const labels = Utils.months({count: 7}); +const data = { + labels: labels, + datasets: [ + { + label: 'Dataset 1', + data: Utils.bubbles(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red, 0.5), + }, + { + label: 'Dataset 2', + data: Utils.bubbles(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.orange, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.orange, 0.5), + } + ] +}; +// + +// +const config = { + type: 'scatter', + data: data, + options: { + responsive: true, + plugins: { + legend: { + position: 'top', + }, + title: { + display: true, + text: 'Chart.js Scatter Chart' + } + } + }, +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` diff --git a/docs/samples/scale-options/center.md b/docs/samples/scale-options/center.md new file mode 100644 index 000000000..19a509c3e --- /dev/null +++ b/docs/samples/scale-options/center.md @@ -0,0 +1,89 @@ +# Center Positioning + +This sample show how to place the axis in the center of the chart area, instead of at the edges. + +```js chart-editor +// +const actions = [ + { + name: 'Default Positions', + handler(chart) { + chart.options.scales.x.position = 'bottom'; + chart.options.scales.y.position = 'left'; + chart.update(); + } + }, + { + name: 'Position: center', + handler(chart) { + chart.options.scales.x.position = 'center'; + chart.options.scales.y.position = 'center'; + chart.update(); + } + }, + { + name: 'Position: Vertical: x=-60, Horizontal: y=30', + handler(chart) { + chart.options.scales.x.position = {y: 30}; + chart.options.scales.y.position = {x: -60}; + chart.update(); + } + }, +]; +// + + +// +const DATA_COUNT = 6; +const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100}; +const data = { + datasets: [ + { + label: 'Dataset 1', + data: Utils.points(NUMBER_CFG), + fill: false, + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red, 0.5), + }, + { + label: 'Dataset 2', + data: Utils.points(NUMBER_CFG), + fill: false, + borderColor: Utils.CHART_COLORS.blue, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.blue, 0.5), + } + ] +}; +// + +// +const config = { + type: 'scatter', + data: data, + options: { + responsive: true, + plugins: { + title: { + display: true, + text: 'Axis Center Positioning' + } + }, + scales: { + x: { + min: -100, + max: 100, + }, + y: { + min: -100, + max: 100, + } + } + }, +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` \ No newline at end of file diff --git a/docs/samples/scale-options/grid.md b/docs/samples/scale-options/grid.md new file mode 100644 index 000000000..68c030a99 --- /dev/null +++ b/docs/samples/scale-options/grid.md @@ -0,0 +1,94 @@ +# Grid Configuration + +This sample shows how to use scriptable grid options for an axis to control styling. In this case, the Y axis grid lines are colored based on their value. In addition, booleans are provided to toggle different parts of the X axis grid visibility. + +```js chart-editor +// +const actions = [ + { + name: 'Randomize', + handler(chart) { + chart.data.datasets.forEach(dataset => { + dataset.data = Utils.numbers({count: chart.data.labels.length, min: -100, max: 100}); + }); + chart.update(); + } + }, +]; +// + +// +const DATA_COUNT = 7; +const data = { + labels: Utils.months({count: DATA_COUNT}), + datasets: [ + { + label: 'Dataset 1', + data: [10, 30, 39, 20, 25, 34, -10], + fill: false, + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red, 0.5), + }, + { + label: 'Dataset 2', + data: [18, 33, 22, 19, 11, -39, 30], + fill: false, + borderColor: Utils.CHART_COLORS.blue, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.blue, 0.5), + } + ] +}; +// + +// +// Change these settings to change the display for different parts of the X axis +// grid configuiration +const DISPLAY = true; +const BORDER = true; +const CHART_AREA = true; +const TICKS = true; + +const config = { + type: 'line', + data: data, + options: { + responsive: true, + plugins: { + title: { + display: true, + text: 'Grid Line Settings' + } + }, + scales: { + x: { + grid: { + display: DISPLAY, + drawBorder: BORDER, + drawOnChartArea: CHART_AREA, + drawTicks: TICKS, + } + }, + y: { + grid: { + drawBorder: false, + color: function(context) { + if (context.tick.value > 0) { + return Utils.CHART_COLORS.green; + } else if (context.tick.value < 0) { + return Utils.CHART_COLORS.red; + } + + return '#000000'; + }, + }, + } + } + }, +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` diff --git a/docs/samples/scale-options/ticks.md b/docs/samples/scale-options/ticks.md new file mode 100644 index 000000000..110047e4c --- /dev/null +++ b/docs/samples/scale-options/ticks.md @@ -0,0 +1,94 @@ +# Tick Configuration + +This sample shows how to use different tick features to control how tick labels are shown on the X axis. These features include: + +* Multi-line labels +* Filtering labels +* Changing the tick color +* Changing the tick alignment for the X axis + +```js chart-editor +// +const actions = [ + { + name: 'Alignment: start', + handler(chart) { + chart.options.scales.x.ticks.align = 'start'; + chart.update(); + } + }, + { + name: 'Alignment: center (default)', + handler(chart) { + chart.options.scales.x.ticks.align = 'center'; + chart.update(); + } + }, + { + name: 'Alignment: end', + handler(chart) { + chart.options.scales.x.ticks.align = 'end'; + chart.update(); + } + }, +]; +// + + +// +const DATA_COUNT = 12; +const NUMBER_CFG = {count: DATA_COUNT, min: 0, max: 100}; +const data = { + labels: [['June', '2015'], 'July', 'August', 'September', 'October', 'November', 'December', ['January', '2016'], 'February', 'March', 'April', 'May'], + datasets: [ + { + label: 'Dataset 1', + data: Utils.numbers(NUMBER_CFG), + fill: false, + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red, 0.5), + }, + { + label: 'Dataset 2', + data: Utils.numbers(NUMBER_CFG), + fill: false, + borderColor: Utils.CHART_COLORS.blue, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.blue, 0.5), + } + ] +}; +// + +// +const config = { + type: 'line', + data: data, + options: { + responsive: true, + plugins: { + title: { + display: true, + text: 'Chart with Tick Configuration' + } + }, + scales: { + x: { + ticks: { + // For a category axis, the val is the index so the lookup via getLabelForValue is needed + callback: function(val, index) { + // Hide the label of every 2nd dataset + return index % 2 === 0 ? this.getLabelForValue(val) : ''; + }, + color: 'red', + } + } + } + }, +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` diff --git a/docs/samples/scale-options/titles.md b/docs/samples/scale-options/titles.md new file mode 100644 index 000000000..fdb1ea9a6 --- /dev/null +++ b/docs/samples/scale-options/titles.md @@ -0,0 +1,76 @@ +# Title Configuration + +This sample shows how to configure the title of an axis including alignment, font, and color. + +```js chart-editor +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, min: 0, max: 100}; +const data = { + labels: Utils.months({count: DATA_COUNT}), + datasets: [ + { + label: 'Dataset 1', + data: Utils.numbers(NUMBER_CFG), + fill: false, + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red, 0.5), + }, + { + label: 'Dataset 2', + data: Utils.numbers(NUMBER_CFG), + fill: false, + borderColor: Utils.CHART_COLORS.blue, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.blue, 0.5), + } + ] +}; +// + +// +const config = { + type: 'line', + data: data, + options: { + responsive: true, + scales: { + x: { + display: true, + title: { + display: true, + text: 'Month', + color: '#911', + font: { + family: 'Comic Sans MS', + size: 20, + style: 'bold', + lineHeight: 1.2, + }, + padding: {top: 20, left: 0, right: 0, bottom: 0} + } + }, + y: { + display: true, + title: { + display: true, + text: 'Value', + color: '#191', + font: { + family: 'Times', + size: 20, + style: 'normal', + lineHeight: 1.2, + }, + padding: {top: 30, left: 0, right: 0, bottom: 0} + } + } + } + }, +}; +// + +module.exports = { + actions: [], + config: config, +}; +``` diff --git a/docs/samples/scales/linear-min-max-suggested.md b/docs/samples/scales/linear-min-max-suggested.md new file mode 100644 index 000000000..a297fd913 --- /dev/null +++ b/docs/samples/scales/linear-min-max-suggested.md @@ -0,0 +1,56 @@ +# Linear Scale - Suggested Min-Max + +```js chart-editor +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, min: 0, max: 100}; + +const labels = Utils.months({count: 7}); +const data = { + labels: labels, + datasets: [ + { + label: 'Dataset 1', + data: [10, 30, 39, 20, 25, 34, -10], + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.CHART_COLORS.red, + }, + { + label: 'Dataset 2', + data: [18, 33, 22, 19, 11, 39, 30], + borderColor: Utils.CHART_COLORS.blue, + backgroundColor: Utils.CHART_COLORS.blue, + } + ] +}; +// + +// +const config = { + type: 'line', + data: data, + options: { + responsive: true, + plugins: { + title: { + display: true, + text: 'Suggested Min and Max Settings' + } + }, + scales: { + y: { + // the data minimum used for determining the ticks is Math.min(dataMin, suggestedMin) + suggestedMin: 30, + + // the data maximum used for determining the ticks is Math.max(dataMax, suggestedMax) + suggestedMax: 50, + } + } + }, +}; +// + +module.exports = { + config: config, +}; +``` diff --git a/docs/samples/scales/linear-min-max.md b/docs/samples/scales/linear-min-max.md new file mode 100644 index 000000000..6a8910b52 --- /dev/null +++ b/docs/samples/scales/linear-min-max.md @@ -0,0 +1,53 @@ +# Linear Scale - Min-Max + +```js chart-editor +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, min: 0, max: 100}; + +const labels = Utils.months({count: 7}); +const data = { + labels: labels, + datasets: [ + { + label: 'Dataset 1', + data: [10, 30, 50, 20, 25, 44, -10], + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.CHART_COLORS.red, + }, + { + label: 'Dataset 2', + data: [100, 33, 22, 19, 11, 49, 30], + borderColor: Utils.CHART_COLORS.blue, + backgroundColor: Utils.CHART_COLORS.blue, + } + ] +}; +// + +// +const config = { + type: 'line', + data: data, + options: { + responsive: true, + plugins: { + title: { + display: true, + text: 'Min and Max Settings' + } + }, + scales: { + y: { + min: 10, + max: 50, + } + } + }, +}; +// + +module.exports = { + config: config, +}; +``` diff --git a/docs/samples/scales/linear-step-size.md b/docs/samples/scales/linear-step-size.md new file mode 100644 index 000000000..049b0ae5d --- /dev/null +++ b/docs/samples/scales/linear-step-size.md @@ -0,0 +1,139 @@ +# Linear Scale - Step Size + +```js chart-editor +// +const actions = [ + { + name: 'Randomize', + handler(chart) { + chart.data.datasets.forEach(dataset => { + dataset.data = Utils.numbers({count: chart.data.labels.length, min: 0, max: 100}); + }); + chart.update(); + } + }, + { + name: 'Add Dataset', + handler(chart) { + const data = chart.data; + const dsColor = Utils.namedColor(chart.data.datasets.length); + const newDataset = { + label: 'Dataset ' + (data.datasets.length + 1), + backgroundColor: dsColor, + borderColor: dsColor, + data: Utils.numbers({count: data.labels.length, min: 0, max: 100}), + }; + chart.data.datasets.push(newDataset); + chart.update(); + } + }, + { + name: 'Add Data', + handler(chart) { + const data = chart.data; + if (data.datasets.length > 0) { + data.labels = Utils.months({count: data.labels.length + 1}); + + for (var index = 0; index < data.datasets.length; ++index) { + data.datasets[index].data.push(Utils.rand(0, 100)); + } + + chart.update(); + } + } + }, + { + name: 'Remove Dataset', + handler(chart) { + chart.data.datasets.pop(); + chart.update(); + } + }, + { + name: 'Remove Data', + handler(chart) { + chart.data.labels.splice(-1, 1); // remove the label first + + chart.data.datasets.forEach(dataset => { + dataset.data.pop(); + }); + + chart.update(); + } + } +]; +// + +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, min: 0, max: 100}; + +const labels = Utils.months({count: 7}); +const data = { + labels: labels, + datasets: [ + { + label: 'Dataset 1', + data: Utils.numbers(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.CHART_COLORS.red, + }, + { + label: 'Dataset 2', + data: Utils.numbers(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.blue, + backgroundColor: Utils.CHART_COLORS.blue, + } + ] +}; +// + +// +const config = { + type: 'line', + data: data, + options: { + responsive: true, + plugins: { + tooltip: { + mode: 'index', + intersect: false + }, + title: { + display: true, + text: 'Chart.js Line Chart' + } + }, + hover: { + mode: 'index', + intersec: false + }, + scales: { + x: { + title: { + display: true, + text: 'Month' + } + }, + y: { + title: { + display: true, + text: 'Value' + }, + min: 0, + max: 100, + ticks: { + // forces step size to be 50 units + stepSize: 50 + } + } + } + }, +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` diff --git a/docs/samples/scales/log.md b/docs/samples/scales/log.md new file mode 100644 index 000000000..d08f55e40 --- /dev/null +++ b/docs/samples/scales/log.md @@ -0,0 +1,76 @@ +# Log Scale + +```js chart-editor +// +const logNumbers = (num) => { + const data = []; + + for (let i = 0; i < num; ++i) { + data.push(Math.ceil(Math.random() * 10.0) * Math.pow(10, Math.ceil(Math.random() * 5))); + } + + return data; +}; + +const actions = [ + { + name: 'Randomize', + handler(chart) { + chart.data.datasets.forEach(dataset => { + dataset.data = logNumbers(chart.data.labels.length); + }); + chart.update(); + } + }, +]; +// + +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, min: 0, max: 100}; + +const labels = Utils.months({count: 7}); +const data = { + labels: labels, + datasets: [ + { + label: 'Dataset 1', + data: logNumbers(DATA_COUNT), + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.CHART_COLORS.red, + fill: false, + }, + ] +}; +// + +// +const config = { + type: 'line', + data: data, + options: { + responsive: true, + plugins: { + title: { + display: true, + text: 'Chart.js Line Chart - Logarithmic' + } + }, + scales: { + x: { + display: true, + }, + y: { + display: true, + type: 'logarithmic', + } + } + }, +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` diff --git a/docs/samples/scales/time-combo.md b/docs/samples/scales/time-combo.md new file mode 100644 index 000000000..abd726a3e --- /dev/null +++ b/docs/samples/scales/time-combo.md @@ -0,0 +1,82 @@ +# Time Scale - Combo Chart + +```js chart-editor +// +const actions = [ + { + name: 'Randomize', + handler(chart) { + chart.data.datasets.forEach(dataset => { + dataset.data = Utils.numbers({count: chart.data.labels.length, min: 0, max: 100}); + }); + chart.update(); + } + }, +]; +// + +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, min: 0, max: 100}; + +const labels = []; + +for (let i = 0; i < DATA_COUNT; ++i) { + labels.push(Utils.newDate(i)); +} + +const data = { + labels: labels, + datasets: [{ + type: 'bar', + label: 'Dataset 1', + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red, 0.5), + borderColor: Utils.CHART_COLORS.red, + data: Utils.numbers(NUMBER_CFG), + }, { + type: 'bar', + label: 'Dataset 2', + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.blue, 0.5), + borderColor: Utils.CHART_COLORS.blue, + data: Utils.numbers(NUMBER_CFG), + }, { + type: 'line', + label: 'Dataset 3', + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.green, 0.5), + borderColor: Utils.CHART_COLORS.green, + fill: false, + data: Utils.numbers(NUMBER_CFG), + }] +}; +// + +// +const config = { + type: 'line', + data: data, + options: { + plugins: { + title: { + text: 'Chart.js Combo Time Scale', + display: true + } + }, + scales: { + x: { + type: 'time', + display: true, + offset: true, + time: { + unit: 'day' + } + }, + }, + }, +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` diff --git a/docs/samples/scales/time-line.md b/docs/samples/scales/time-line.md new file mode 100644 index 000000000..a981b96cd --- /dev/null +++ b/docs/samples/scales/time-line.md @@ -0,0 +1,112 @@ +# Time Scale + +```js chart-editor +// +const actions = [ + { + name: 'Randomize', + handler(chart) { + chart.data.datasets.forEach(dataset => { + dataset.data.forEach(function(dataObj, j) { + const newVal = Utils.rand(0, 100); + + if (typeof dataObj === 'object') { + dataObj.y = newVal; + } else { + dataset.data[j] = newVal; + } + }); + }); + chart.update(); + } + }, +]; +// + +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, min: 0, max: 100}; + +const data = { + labels: [ // Date Objects + Utils.newDate(0), + Utils.newDate(1), + Utils.newDate(2), + Utils.newDate(3), + Utils.newDate(4), + Utils.newDate(5), + Utils.newDate(6) + ], + datasets: [{ + label: 'My First dataset', + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red, 0.5), + borderColor: Utils.CHART_COLORS.red, + fill: false, + data: Utils.numbers(NUMBER_CFG), + }, { + label: 'My Second dataset', + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.blue, 0.5), + borderColor: Utils.CHART_COLORS.blue, + fill: false, + data: Utils.numbers(NUMBER_CFG), + }, { + label: 'Dataset with point data', + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.green, 0.5), + borderColor: Utils.CHART_COLORS.green, + fill: false, + data: [{ + x: Utils.newDateString(0), + y: Utils.rand(0, 100) + }, { + x: Utils.newDateString(5), + y: Utils.rand(0, 100) + }, { + x: Utils.newDateString(7), + y: Utils.rand(0, 100) + }, { + x: Utils.newDateString(15), + y: Utils.rand(0, 100) + }], + }] +}; +// + +// +const config = { + type: 'line', + data: data, + options: { + plugins: { + title: { + text: 'Chart.js Time Scale', + display: true + } + }, + scales: { + x: { + type: 'time', + time: { + // Luxon format string + tooltipFormat: 'DD T' + }, + title: { + display: true, + text: 'Date' + } + }, + y: { + title: { + display: true, + text: 'value' + } + } + }, + }, +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` diff --git a/docs/samples/scales/time-max-span.md b/docs/samples/scales/time-max-span.md new file mode 100644 index 000000000..dc144c95e --- /dev/null +++ b/docs/samples/scales/time-max-span.md @@ -0,0 +1,126 @@ +# Time Scale - Max Span + +```js chart-editor +// +const actions = [ + { + name: 'Randomize', + handler(chart) { + chart.data.datasets.forEach(dataset => { + dataset.data.forEach(function(dataObj, j) { + const newVal = Utils.rand(0, 100); + + if (typeof dataObj === 'object') { + dataObj.y = newVal; + } else { + dataset.data[j] = newVal; + } + }); + }); + chart.update(); + } + }, +]; +// + +// +const data = { + datasets: [{ + label: 'Dataset with string point data', + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red, 0.5), + borderColor: Utils.CHART_COLORS.red, + fill: false, + data: [{ + x: Utils.newDateString(0), + y: Utils.rand(0, 100) + }, { + x: Utils.newDateString(2), + y: Utils.rand(0, 100) + }, { + x: Utils.newDateString(4), + y: Utils.rand(0, 100) + }, { + x: Utils.newDateString(6), + y: Utils.rand(0, 100) + }], + }, { + label: 'Dataset with date object point data', + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.blue, 0.5), + borderColor: Utils.CHART_COLORS.blue, + fill: false, + data: [{ + x: Utils.newDate(0), + y: Utils.rand(0, 100) + }, { + x: Utils.newDate(2), + y: Utils.rand(0, 100) + }, { + x: Utils.newDate(5), + y: Utils.rand(0, 100) + }, { + x: Utils.newDate(6), + y: Utils.rand(0, 100) + }] + }] +}; +// + +// +const config = { + type: 'line', + data: data, + options: { + spanGaps: 1000 * 60 * 60 * 24 * 2, // 2 days + responsive: true, + interaction: { + mode: 'nearest', + }, + plugins: { + title: { + display: true, + text: 'Chart.js Time - spanGaps: 172800000 (2 days in ms)' + }, + }, + scales: { + x: { + type: 'time', + display: true, + title: { + display: true, + text: 'Date' + }, + ticks: { + autoSkip: false, + maxRotation: 0, + major: { + enabled: true + }, + // color: function(context) { + // return context.tick && context.tick.major ? '#FF0000' : 'rgba(0,0,0,0.1)'; + // }, + font: function(context) { + if (context.tick && context.tick.major) { + return { + style: 'bold', + }; + } + } + } + }, + y: { + display: true, + title: { + display: true, + text: 'value' + } + } + } + }, +}; +// + +module.exports = { + actions: [], + config: config, +}; +``` diff --git a/docs/samples/scriptable/bar.md b/docs/samples/scriptable/bar.md new file mode 100644 index 000000000..8842c6a9c --- /dev/null +++ b/docs/samples/scriptable/bar.md @@ -0,0 +1,73 @@ +# Bar Chart + +```js chart-editor +// +var DATA_COUNT = 16; +Utils.srand(110); + +const actions = [ + { + name: 'Randomize', + handler(chart) { + chart.data.datasets.forEach(dataset => { + dataset.data = generateData(); + }); + chart.update(); + } + }, +]; +// + +// +function generateData() { + return Utils.numbers({ + count: DATA_COUNT, + min: -100, + max: 100 + }); +} + +const data = { + labels: Utils.months({count: DATA_COUNT}), + datasets: [{ + data: generateData(), + }] +}; +// + +// +function colorize(opaque) { + return (ctx) => { + var v = ctx.parsed.y; + var c = v < -50 ? '#D60000' + : v < 0 ? '#F46300' + : v < 50 ? '#0358B6' + : '#44DE28'; + + return opaque ? c : Utils.transparentize(c, 1 - Math.abs(v / 150)); + }; +} + +const config = { + type: 'bar', + data: data, + options: { + plugins: { + legend: false, + }, + elements: { + bar: { + backgroundColor: colorize(false), + borderColor: colorize(true), + borderWidth: 2 + } + } + } +}; +// + +module.exports = { + actions, + config, +}; +``` diff --git a/docs/samples/scriptable/bubble.md b/docs/samples/scriptable/bubble.md new file mode 100644 index 000000000..d466172cd --- /dev/null +++ b/docs/samples/scriptable/bubble.md @@ -0,0 +1,109 @@ +# Bubble Chart + +```js chart-editor +// +const DATA_COUNT = 16; +const MIN_XY = -150; +const MAX_XY = 100; +Utils.srand(110); + +const actions = [ + { + name: 'Randomize', + handler(chart) { + chart.data.datasets.forEach(dataset => { + dataset.data = generateData(); + }); + chart.update(); + } + }, +]; +// + +// +function generateData() { + var data = []; + var i; + + for (i = 0; i < DATA_COUNT; ++i) { + data.push({ + x: Utils.rand(MIN_XY, MAX_XY), + y: Utils.rand(MIN_XY, MAX_XY), + v: Utils.rand(0, 1000) + }); + } + + return data; +} + +const data = { + datasets: [{ + data: generateData() + }, { + data: generateData() + }] +}; +// + +// +function channelValue(x, y, values) { + return x < 0 && y < 0 ? values[0] : x < 0 ? values[1] : y < 0 ? values[2] : values[3]; +} + +function colorize(opaque, context) { + var value = context.raw; + var x = value.x / 100; + var y = value.y / 100; + var r = channelValue(x, y, [250, 150, 50, 0]); + var g = channelValue(x, y, [0, 50, 150, 250]); + var b = channelValue(x, y, [0, 150, 150, 250]); + var a = opaque ? 1 : 0.5 * value.v / 1000; + + return 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; +} + +const config = { + type: 'bubble', + data: data, + options: { + aspectRatio: 1, + plugins: { + legend: false, + tooltip: false, + }, + elements: { + point: { + backgroundColor: colorize.bind(null, false), + + borderColor: colorize.bind(null, true), + + borderWidth: function(context) { + return Math.min(Math.max(1, context.datasetIndex + 1), 8); + }, + + hoverBackgroundColor: 'transparent', + + hoverBorderColor: function(context) { + return Utils.color(context.datasetIndex); + }, + + hoverBorderWidth: function(context) { + return Math.round(8 * context.raw.v / 1000); + }, + + radius: function(context) { + var size = context.chart.width; + var base = Math.abs(context.raw.v) / 1000; + return (size / 24) * base; + } + } + } + } +}; +// + +module.exports = { + actions, + config, +}; +``` diff --git a/docs/samples/scriptable/line.md b/docs/samples/scriptable/line.md new file mode 100644 index 000000000..beeff9ed4 --- /dev/null +++ b/docs/samples/scriptable/line.md @@ -0,0 +1,91 @@ +# Line Chart + +```js chart-editor +// +const DATA_COUNT = 12; +Utils.srand(110); + +const actions = [ + { + name: 'Randomize', + handler(chart) { + chart.data.datasets.forEach(dataset => { + dataset.data = generateData(); + }); + chart.update(); + } + }, +]; +// + +// +function generateData() { + return Utils.numbers({ + count: DATA_COUNT, + min: 0, + max: 100 + }); +} + +const data = { + labels: Utils.months({count: DATA_COUNT}), + datasets: [{ + data: generateData() + }] +}; +// + +// +function getLineColor(ctx) { + return Utils.color(ctx.datasetIndex); +} + +function alternatePointStyles(ctx) { + var index = ctx.dataIndex; + return index % 2 === 0 ? 'circle' : 'rect'; +} + +function makeHalfAsOpaque(ctx) { + return Utils.transparentize(getLineColor(ctx)); +} + +function adjustRadiusBasedOnData(ctx) { + var v = ctx.parsed.y; + return v < 10 ? 5 + : v < 25 ? 7 + : v < 50 ? 9 + : v < 75 ? 11 + : 15; +} + +const config = { + type: 'line', + data: data, + options: { + plugins: { + legend: false, + tooltip: true, + }, + elements: { + line: { + fill: false, + backgroundColor: getLineColor, + borderColor: getLineColor, + }, + point: { + backgroundColor: getLineColor, + hoverBackgroundColor: makeHalfAsOpaque, + radius: adjustRadiusBasedOnData, + pointStyle: alternatePointStyles, + hoverRadius: 15, + } + } + } +}; +// + +module.exports = { + actions, + config, +}; +``` diff --git a/docs/samples/scriptable/pie.md b/docs/samples/scriptable/pie.md new file mode 100644 index 000000000..423e213f8 --- /dev/null +++ b/docs/samples/scriptable/pie.md @@ -0,0 +1,87 @@ +# Pie Chart + +```js chart-editor +// +const DATA_COUNT = 5; +Utils.srand(110); + +const actions = [ + { + name: 'Randomize', + handler(chart) { + chart.data.datasets.forEach(dataset => { + dataset.data = generateData(); + }); + chart.update(); + } + }, + { + name: 'Toggle Doughnut View', + handler(chart) { + if (chart.options.cutout) { + chart.options.cutout = 0; + } else { + chart.options.cutout = '50%'; + } + chart.update(); + } + } +]; +// + +// +function generateData() { + return Utils.numbers({ + count: DATA_COUNT, + min: -100, + max: 100 + }); +} + +const data = { + datasets: [{ + data: generateData() + }] +}; +// + +// +function colorize(opaque, hover, ctx) { + var v = ctx.parsed; + var c = v < -50 ? '#D60000' + : v < 0 ? '#F46300' + : v < 50 ? '#0358B6' + : '#44DE28'; + + var opacity = hover ? 1 - Math.abs(v / 150) - 0.2 : 1 - Math.abs(v / 150); + + return opaque ? c : Utils.transparentize(c, opacity); +} + +function hoverColorize(ctx) { + return colorize(false, true, ctx); +} + +const config = { + type: 'pie', + data: data, + options: { + plugins: { + legend: false, + tooltip: false, + }, + elements: { + arc: { + backgroundColor: colorize.bind(null, false, false), + hoverBackgroundColor: hoverColorize + } + } + } +}; +// + +module.exports = { + actions, + config, +}; +``` diff --git a/docs/samples/scriptable/polar.md b/docs/samples/scriptable/polar.md new file mode 100644 index 000000000..4fe6e04b9 --- /dev/null +++ b/docs/samples/scriptable/polar.md @@ -0,0 +1,77 @@ +# Polar Area Chart + +```js chart-editor +// +const DATA_COUNT = 7; +Utils.srand(110); + +const actions = [ + { + name: 'Randomize', + handler(chart) { + chart.data.datasets.forEach(dataset => { + dataset.data = generateData(); + }); + chart.update(); + } + }, +]; +// + +// +function generateData() { + return Utils.numbers({ + count: DATA_COUNT, + min: 0, + max: 100 + }); +} + +const data = { + labels: Utils.months({count: DATA_COUNT}), + datasets: [{ + data: generateData() + }] +}; +// + +// +function colorize(opaque, hover, ctx) { + var v = ctx.raw; + var c = v < 35 ? '#D60000' + : v < 55 ? '#F46300' + : v < 75 ? '#0358B6' + : '#44DE28'; + + var opacity = hover ? 1 - Math.abs(v / 150) - 0.2 : 1 - Math.abs(v / 150); + + return opaque ? c : Utils.transparentize(c, opacity); +} + +function hoverColorize(ctx) { + return colorize(false, true, ctx); +} + +const config = { + type: 'polarArea', + data: data, + options: { + plugins: { + legend: false, + tooltip: false, + }, + elements: { + arc: { + backgroundColor: colorize.bind(null, false, false), + hoverBackgroundColor: hoverColorize + } + } + } +}; +// + +module.exports = { + actions, + config, +}; +``` diff --git a/docs/samples/scriptable/radar.md b/docs/samples/scriptable/radar.md new file mode 100644 index 000000000..fc3b686b8 --- /dev/null +++ b/docs/samples/scriptable/radar.md @@ -0,0 +1,94 @@ +# Radar Chart + +```js chart-editor +// +const DATA_COUNT = 7; +Utils.srand(110); + +const actions = [ + { + name: 'Randomize', + handler(chart) { + chart.data.datasets.forEach(dataset => { + dataset.data = generateData(); + }); + chart.update(); + } + }, +]; +// + +// +function generateData() { + return Utils.numbers({ + count: DATA_COUNT, + min: 0, + max: 100 + }); +} + +const data = { + labels: [['Eating', 'Dinner'], ['Drinking', 'Water'], 'Sleeping', ['Designing', 'Graphics'], 'Coding', 'Cycling', 'Running'], + datasets: [{ + data: generateData() + }] +}; +// + +// +function getLineColor(ctx) { + return Utils.color(ctx.datasetIndex); +} + +function alternatePointStyles(ctx) { + var index = ctx.dataIndex; + return index % 2 === 0 ? 'circle' : 'rect'; +} + +function makeHalfAsOpaque(ctx) { + return Utils.transparentize(getLineColor(ctx)); +} + +function make20PercentOpaque(ctx) { + return Utils.transparentize(getLineColor(ctx), 0.8); +} + +function adjustRadiusBasedOnData(ctx) { + var v = ctx.parsed.y; + return v < 10 ? 5 + : v < 25 ? 7 + : v < 50 ? 9 + : v < 75 ? 11 + : 15; +} + +const config = { + type: 'radar', + data: data, + options: { + plugins: { + legend: false, + tooltip: false, + }, + elements: { + line: { + backgroundColor: make20PercentOpaque, + borderColor: getLineColor, + }, + point: { + backgroundColor: getLineColor, + hoverBackgroundColor: makeHalfAsOpaque, + radius: adjustRadiusBasedOnData, + pointStyle: alternatePointStyles, + hoverRadius: 15, + } + } + } +}; +// + +module.exports = { + actions, + config, +}; +``` diff --git a/docs/samples/title/alignment.md b/docs/samples/title/alignment.md new file mode 100644 index 000000000..791a503de --- /dev/null +++ b/docs/samples/title/alignment.md @@ -0,0 +1,69 @@ +# Alignment + +This sample show how to configure the alignment of the chart title + +```js chart-editor +// +const actions = [ + { + name: 'Title Alignment: start', + handler(chart) { + chart.options.plugins.title.align = 'start'; + chart.update(); + } + }, + { + name: 'Title Alignment: center (default)', + handler(chart) { + chart.options.plugins.title.align = 'center'; + chart.update(); + } + }, + { + name: 'Title Alignment: end', + handler(chart) { + chart.options.plugins.title.align = 'end'; + chart.update(); + } + }, +]; +// + + +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100}; +const data = { + labels: Utils.months({count: DATA_COUNT}), + datasets: [ + { + label: 'Dataset 1', + data: Utils.numbers(NUMBER_CFG), + fill: false, + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red, 0.5), + }, + ] +}; +// + +// +const config = { + type: 'line', + data: data, + options: { + plugins: { + title: { + display: true, + text: 'Chart Title', + } + } + } +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` \ No newline at end of file diff --git a/docs/samples/tooltip/content.md b/docs/samples/tooltip/content.md new file mode 100644 index 000000000..26a3a8898 --- /dev/null +++ b/docs/samples/tooltip/content.md @@ -0,0 +1,66 @@ +# Custom Tooltip Content + +This sample shows how to use the tooltip callbacks to add additional content to the tooltip. + +```js chart-editor +// +const footer = (tooltipItems) => { + let sum = 0; + + tooltipItems.forEach(function(tooltipItem) { + sum += tooltipItem.parsed.y; + }); + return 'Sum: ' + sum; +}; + +// + +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100, decimals: 0}; +const data = { + labels: Utils.months({count: DATA_COUNT}), + datasets: [ + { + label: 'Dataset 1', + data: Utils.numbers(NUMBER_CFG), + fill: false, + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red, 0.5), + }, + { + label: 'Dataset 2', + data: Utils.numbers(NUMBER_CFG), + fill: false, + borderColor: Utils.CHART_COLORS.blue, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.blue, 0.5), + }, + ] +}; +// + +// +const config = { + type: 'line', + data: data, + options: { + interaction: { + intersect: false, + mode: 'index', + }, + plugins: { + tooltip: { + callbacks: { + footer: footer, + } + } + } + } +}; +// + +module.exports = { + actions: [], + config: config, +}; +``` \ No newline at end of file diff --git a/docs/samples/tooltip/html.md b/docs/samples/tooltip/html.md new file mode 100644 index 000000000..2a7cb9e52 --- /dev/null +++ b/docs/samples/tooltip/html.md @@ -0,0 +1,165 @@ +# External HTML Tooltip + +This sample shows how to use the external tooltip functionality to generate an HTML tooltip. + +```js chart-editor +// +const getOrCreateTooltip = (chart) => { + let tooltipEl = chart.canvas.parentNode.querySelector('div'); + + if (!tooltipEl) { + tooltipEl = document.createElement('div'); + tooltipEl.style.background = 'rgba(0, 0, 0, 0.7)'; + tooltipEl.style.borderRadius = '3px'; + tooltipEl.style.color = 'white'; + tooltipEl.style.opacity = 1; + tooltipEl.style.pointerEvents = 'none'; + tooltipEl.style.position = 'absolute'; + tooltipEl.style.transform = 'translate(-50%, 0)'; + tooltipEl.style.transition = 'all .1s ease'; + + const table = document.createElement('table'); + table.style.margin = '0px'; + + tooltipEl.appendChild(table); + chart.canvas.parentNode.appendChild(tooltipEl); + } + + return tooltipEl; +}; + +const externalTooltipHandler = (context) => { + // Tooltip Element + const {chart, tooltip} = context; + const tooltipEl = getOrCreateTooltip(chart); + + // Hide if no tooltip + if (tooltip.opacity === 0) { + tooltipEl.style.opacity = 0; + return; + } + + // Set Text + if (tooltip.body) { + const titleLines = tooltip.title || []; + const bodyLines = tooltip.body.map(b => b.lines); + + const tableHead = document.createElement('thead'); + + titleLines.forEach(title => { + const tr = document.createElement('tr'); + tr.style.borderWidth = 0; + + const th = document.createElement('th'); + th.style.borderWidth = 0; + const text = document.createTextNode(title); + + th.appendChild(text); + tr.appendChild(th); + tableHead.appendChild(tr); + }); + + const tableBody = document.createElement('tbody'); + bodyLines.forEach((body, i) => { + const colors = tooltip.labelColors[i]; + + const span = document.createElement('span'); + span.style.background = colors.backgroundColor; + span.style.borderColor = colors.borderColor; + span.style.borderWidth = '2px'; + span.style.marginRight = '10px'; + span.style.height = '10px'; + span.style.width = '10px'; + span.style.display = 'inline-block'; + + const tr = document.createElement('tr'); + tr.style.backgroundColor = 'inherit'; + tr.style.borderWidth = 0; + + const td = document.createElement('td'); + td.style.borderWidth = 0; + + const text = document.createTextNode(body); + + td.appendChild(span); + td.appendChild(text); + tr.appendChild(td); + tableBody.appendChild(tr); + }); + + const tableRoot = tooltipEl.querySelector('table'); + + // Remove old children + while (tableRoot.firstChild) { + tableRoot.firstChild.remove(); + } + + // Add new children + tableRoot.appendChild(tableHead); + tableRoot.appendChild(tableBody); + } + + const {offsetLeft: positionX, offsetTop: positionY} = chart.canvas; + + // Display, position, and set styles for font + tooltipEl.style.opacity = 1; + tooltipEl.style.left = positionX + tooltip.caretX + 'px'; + tooltipEl.style.top = positionY + tooltip.caretY + 'px'; + tooltipEl.style.font = tooltip.options.bodyFont.string; + tooltipEl.style.padding = tooltip.padding + 'px ' + tooltip.padding + 'px'; +}; +// + +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100, decimals: 0}; +const data = { + labels: Utils.months({count: DATA_COUNT}), + datasets: [ + { + label: 'Dataset 1', + data: Utils.numbers(NUMBER_CFG), + fill: false, + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red, 0.5), + }, + { + label: 'Dataset 2', + data: Utils.numbers(NUMBER_CFG), + fill: false, + borderColor: Utils.CHART_COLORS.blue, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.blue, 0.5), + }, + ] +}; +// + +// +const config = { + type: 'line', + data: data, + options: { + interaction: { + mode: 'index', + intersect: false, + }, + plugins: { + title: { + display: true, + text: 'Chart.js Line Chart - External Tooltips' + }, + tooltip: { + enabled: false, + position: 'nearest', + external: externalTooltipHandler + } + } + } +}; +// + +module.exports = { + actions: [], + config: config, +}; +``` diff --git a/docs/samples/tooltip/interactions.md b/docs/samples/tooltip/interactions.md new file mode 100644 index 000000000..0b1a995bc --- /dev/null +++ b/docs/samples/tooltip/interactions.md @@ -0,0 +1,110 @@ +# Interaction Modes + +This sample shows how to use the tooltip position mode setting. + +```js chart-editor +// +const actions = [ + { + name: 'Mode: index', + handler(chart) { + chart.options.interaction.mode = 'index'; + chart.update(); + } + }, + { + name: 'Mode: dataset', + handler(chart) { + chart.options.interaction.mode = 'dataset'; + chart.update(); + } + }, + { + name: 'Mode: point', + handler(chart) { + chart.options.interaction.mode = 'point'; + chart.update(); + } + }, + { + name: 'Mode: nearest', + handler(chart) { + chart.options.interaction.mode = 'nearest'; + chart.update(); + } + }, + { + name: 'Mode: x', + handler(chart) { + chart.options.interaction.mode = 'x'; + chart.update(); + } + }, + { + name: 'Mode: y', + handler(chart) { + chart.options.interaction.mode = 'y'; + chart.update(); + } + }, + { + name: 'Toggle Intersect', + handler(chart) { + chart.options.interaction.intersect = !chart.options.interaction.intersect; + chart.update(); + } + }, +]; +// + +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100}; +const data = { + labels: Utils.months({count: DATA_COUNT}), + datasets: [ + { + label: 'Dataset 1', + data: Utils.numbers(NUMBER_CFG), + fill: false, + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red, 0.5), + }, + { + label: 'Dataset 2', + data: Utils.numbers(NUMBER_CFG), + fill: false, + borderColor: Utils.CHART_COLORS.blue, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.blue, 0.5), + }, + ] +}; +// + +// +const config = { + type: 'line', + data: data, + options: { + interaction: { + intersect: false, + mode: 'index', + }, + plugins: { + title: { + display: true, + text: (ctx) => { + const {intersect, mode} = ctx.chart.options.interaction; + return 'Mode: ' + mode + ', intersect: ' + intersect; + } + }, + } + } +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` \ No newline at end of file diff --git a/docs/samples/tooltip/point-style.md b/docs/samples/tooltip/point-style.md new file mode 100644 index 000000000..cdeba4685 --- /dev/null +++ b/docs/samples/tooltip/point-style.md @@ -0,0 +1,80 @@ +# Point Style + +This sample shows how to use the dataset point style in the tooltip instead of a rectangle to identify each dataset. + +```js chart-editor +// +const actions = [ + { + name: 'Toggle Tooltip Point Style', + handler(chart) { + chart.options.plugins.tooltip.usePointStyle = !chart.options.plugins.tooltip.usePointStyle; + chart.update(); + } + }, +]; +// + +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100}; +const data = { + labels: Utils.months({count: DATA_COUNT}), + datasets: [ + { + label: 'Triangles', + data: Utils.numbers(NUMBER_CFG), + fill: false, + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red, 0.5), + pointStyle: 'triangle', + pointRadius: 6, + }, + { + label: 'Circles', + data: Utils.numbers(NUMBER_CFG), + fill: false, + borderColor: Utils.CHART_COLORS.blue, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.blue, 0.5), + pointStyle: 'circle', + pointRadius: 6, + }, + { + label: 'Stars', + data: Utils.numbers(NUMBER_CFG), + fill: false, + borderColor: Utils.CHART_COLORS.green, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.green, 0.5), + pointStyle: 'star', + pointRadius: 6, + } + ] +}; +// + +// +const config = { + type: 'line', + data: data, + options: { + interaction: { + mode: 'index', + }, + plugins: { + title: { + display: true, + text: (ctx) => 'Tooltip point style: ' + ctx.chart.options.plugins.tooltip.usePointStyle, + }, + tooltip: { + usePointStyle: true, + } + } + } +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` \ No newline at end of file diff --git a/docs/samples/tooltip/position.md b/docs/samples/tooltip/position.md new file mode 100644 index 000000000..e5e1d5fa3 --- /dev/null +++ b/docs/samples/tooltip/position.md @@ -0,0 +1,93 @@ +# Position + +This sample shows how to use the tooltip position mode setting. + +```js chart-editor +// +const actions = [ + { + name: 'Position: average', + handler(chart) { + chart.options.plugins.tooltip.position = 'average'; + chart.update(); + } + }, + { + name: 'Position: nearest', + handler(chart) { + chart.options.plugins.tooltip.position = 'nearest'; + chart.update(); + } + }, + { + name: 'Position: bottom (custom)', + handler(chart) { + chart.options.plugins.tooltip.position = 'bottom'; + chart.update(); + } + }, +]; +// + +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100}; +const data = { + labels: Utils.months({count: DATA_COUNT}), + datasets: [ + { + label: 'Dataset 1', + data: Utils.numbers(NUMBER_CFG), + fill: false, + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red, 0.5), + }, + { + label: 'Dataset 2', + data: Utils.numbers(NUMBER_CFG), + fill: false, + borderColor: Utils.CHART_COLORS.blue, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.blue, 0.5), + }, + ] +}; +// + +// +// Create a custom tooltip positioner to put at the bottom of the chart area +components.Tooltip.positioners.bottom = function(items) { + const pos = components.Tooltip.positioners.average(items); + const chart = this._chart; + + return { + x: pos.x, + y: chart.chartArea.bottom, + }; +}; + +// + +// +const config = { + type: 'line', + data: data, + options: { + interaction: { + intersect: false, + mode: 'index', + }, + plugins: { + title: { + display: true, + text: (ctx) => 'Tooltip position mode: ' + ctx.chart.options.plugins.tooltip.position, + }, + } + } +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` \ No newline at end of file diff --git a/docs/scripts/analyzer.js b/docs/scripts/analyzer.js new file mode 100644 index 000000000..8d71e8a06 --- /dev/null +++ b/docs/scripts/analyzer.js @@ -0,0 +1,60 @@ +export default { + id: 'samples-filler-analyser', + + beforeInit: function(chart, args, options) { + this.element = document.getElementById(options.target); + }, + + afterUpdate: function(chart) { + var datasets = chart.data.datasets; + var element = this.element; + var stats = []; + var meta, i, ilen, dataset; + + if (!element) { + return; + } + + for (i = 0, ilen = datasets.length; i < ilen; ++i) { + meta = chart.getDatasetMeta(i).$filler; + if (meta) { + dataset = datasets[i]; + stats.push({ + fill: dataset.fill, + target: meta.fill, + visible: meta.visible, + index: i + }); + } + } + + this.element.innerHTML = '' + + '' + + '' + + '' + + '' + + '' + + stats.map(function(stat) { + var target = stat.target; + var row = + '' + + ''; + + if (target === false) { + target = 'none'; + } else if (isFinite(target)) { + target = 'dataset ' + target; + } else { + target = 'boundary "' + target + '"'; + } + + if (stat.visible) { + row += ''; + } else { + row += ''; + } + + return '' + row + ''; + }).join('') + '
DatasetFillTarget (visibility)
' + stat.index + '' + JSON.stringify(stat.fill) + '' + target + '(hidden)
'; + } +}; diff --git a/docs/scripts/components.js b/docs/scripts/components.js new file mode 100644 index 000000000..5ed77b6af --- /dev/null +++ b/docs/scripts/components.js @@ -0,0 +1,3 @@ +// Add Chart components needed in samples here. +// Usable through `components[name]`. +export {Tooltip} from '../../dist/chart.esm'; diff --git a/docs/scripts/derived-bubble.js b/docs/scripts/derived-bubble.js new file mode 100644 index 000000000..2111ba9df --- /dev/null +++ b/docs/scripts/derived-bubble.js @@ -0,0 +1,34 @@ +import {Chart, BubbleController} from 'chart.js'; + +class Custom extends BubbleController { + draw() { + // Call bubble controller method to draw all the points + super.draw(arguments); + + // Now we can do some custom drawing for this dataset. + // Here we'll draw a box around the first point in each dataset, + // using `boxStrokeStyle` dataset option for color + var meta = this.getMeta(); + var pt0 = meta.data[0]; + + const {x, y} = pt0.getProps(['x', 'y']); + const {radius} = pt0.options; + + var ctx = this.chart.ctx; + ctx.save(); + ctx.strokeStyle = this.options.boxStrokeStyle; + ctx.lineWidth = 1; + ctx.strokeRect(x - radius, y - radius, 2 * radius, 2 * radius); + ctx.restore(); + } +} +Custom.id = 'derivedBubble'; +Custom.defaults = { + // Custom defaults. Bubble defaults are inherited. + boxStrokeStyle: 'red' +}; +// Overrides are only inherited, but not merged if defined +// Custom.overrides = Chart.overrides.bubble; + +// Stores the controller so that the chart initialization routine can look it up +Chart.register(Custom); diff --git a/docs/scripts/helpers.js b/docs/scripts/helpers.js new file mode 100644 index 000000000..a7c6036ec --- /dev/null +++ b/docs/scripts/helpers.js @@ -0,0 +1,4 @@ +// Add helpers needed in samples here. +// Usable through `helpers[name]`. +export {color, getHoverColor} from '../../dist/helpers.esm'; + diff --git a/docs/scripts/log2.js b/docs/scripts/log2.js new file mode 100644 index 000000000..8aec32ab9 --- /dev/null +++ b/docs/scripts/log2.js @@ -0,0 +1,67 @@ +import {Scale, LinearScale} from 'chart.js'; + +export default class Log2Axis extends Scale { + constructor(cfg) { + super(cfg); + this._startValue = undefined; + this._valueRange = 0; + } + + parse(raw, index) { + const value = LinearScale.prototype.parse.apply(this, [raw, index]); + return isFinite(value) && value > 0 ? value : null; + } + + determineDataLimits() { + const {min, max} = this.getMinMax(true); + this.min = isFinite(min) ? Math.max(0, min) : null; + this.max = isFinite(max) ? Math.max(0, max) : null; + } + + buildTicks() { + const ticks = []; + + let power = Math.floor(Math.log2(this.min)); + let maxPower = Math.ceil(Math.log2(this.max)); + while (power <= maxPower) { + ticks.push({value: Math.pow(2, power)}); + power += 1; + } + + this.min = ticks[0].value; + this.max = ticks[ticks.length - 1].value; + return ticks; + } + + /** + * @protected + */ + configure() { + const start = this.min; + + super.configure(); + + this._startValue = Math.log2(start); + this._valueRange = Math.log2(this.max) - Math.log2(start); + } + + getPixelForValue(value) { + if (value === undefined || value === 0) { + value = this.min; + } + + return this.getPixelForDecimal(value === this.min ? 0 + : (Math.log2(value) - this._startValue) / this._valueRange); + } + + getValueForPixel(pixel) { + const decimal = this.getDecimalForPixel(pixel); + return Math.pow(2, this._startValue + decimal * this._valueRange); + } +} + +Log2Axis.id = 'log2'; +Log2Axis.defaults = {}; + +// The derived axis is registered like this: +// Chart.register(Log2Axis); diff --git a/docs/scripts/register.js b/docs/scripts/register.js index 6ffea8894..43f0ebcdb 100644 --- a/docs/scripts/register.js +++ b/docs/scripts/register.js @@ -1,3 +1,8 @@ import {Chart, registerables} from '../../dist/chart.esm'; +import Log2Axis from './log2'; +import './derived-bubble'; +import analyzer from './analyzer'; Chart.register(...registerables); +Chart.register(Log2Axis); +Chart.register(analyzer); diff --git a/docs/scripts/utils.js b/docs/scripts/utils.js index 9cb8a73ce..772b27505 100644 --- a/docs/scripts/utils.js +++ b/docs/scripts/utils.js @@ -1,3 +1,6 @@ +import colorLib from '@kurkle/color'; +import {DateTime} from 'luxon'; +import 'chartjs-adapter-luxon'; import {valueOrDefault} from '../../dist/helpers.esm'; // Adapted from http://indiegamr.com/generate-repeatable-random-numbers-in-js/ @@ -44,6 +47,13 @@ export function points(config) { return xs.map((x, i) => ({x, y: ys[i]})); } +export function bubbles(config) { + return this.points(config).map(pt => { + pt.r = this.rand(config.rmin, config.rmax); + return pt; + }); +} + export function labels(config) { var cfg = config || {}; var min = cfg.min || 0; @@ -109,12 +119,12 @@ export function color(index) { return COLORS[index % COLORS.length]; } -export function transparentize(color, opacity) { +export function transparentize(value, opacity) { var alpha = opacity === undefined ? 0.5 : 1 - opacity; - return Color(color).alpha(alpha).rgbString(); + return colorLib(value).alpha(alpha).rgbString(); } -export const chartColors = { +export const CHART_COLORS = { red: 'rgb(255, 99, 132)', orange: 'rgb(255, 159, 64)', yellow: 'rgb(255, 205, 86)', @@ -123,3 +133,25 @@ export const chartColors = { purple: 'rgb(153, 102, 255)', grey: 'rgb(201, 203, 207)' }; + +const NAMED_COLORS = [ + CHART_COLORS.red, + CHART_COLORS.orange, + CHART_COLORS.yellow, + CHART_COLORS.green, + CHART_COLORS.blue, + CHART_COLORS.purple, + CHART_COLORS.grey, +]; + +export function namedColor(index) { + return NAMED_COLORS[index % NAMED_COLORS.length]; +} + +export function newDate(days) { + return DateTime.now().plus({days}).toJSDate(); +} + +export function newDateString(days) { + return DateTime.now().plus({days}).toISO(); +} diff --git a/package-lock.json b/package-lock.json index 9ddc90a56..6d36a3754 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5632,6 +5632,12 @@ "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", "dev": true }, + "chartjs-adapter-luxon": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/chartjs-adapter-luxon/-/chartjs-adapter-luxon-1.0.0-beta.2.tgz", + "integrity": "sha512-1DKxf5jMXs5b1n+NPtzkeM7iAWCSCwCOVI6mmh1/U4rTzprxDRyWQcA9SOdScdZQNFHuij/VbmerQpnrKEHUJA==", + "dev": true + }, "chartjs-adapter-moment": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/chartjs-adapter-moment/-/chartjs-adapter-moment-0.1.2.tgz", @@ -10448,6 +10454,12 @@ "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", "dev": true }, + "luxon": { + "version": "1.26.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.26.0.tgz", + "integrity": "sha512-+V5QIQ5f6CDXQpWNICELwjwuHdqeJM1UenlZWx5ujcRMc9venvluCjFb4t5NYLhb6IhkbMVOxzVuOqkgMxee2A==", + "dev": true + }, "magic-string": { "version": "0.25.7", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", diff --git a/package.json b/package.json index dd10d2cc6..9dee681f3 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "@typescript-eslint/eslint-plugin": "^4.18.0", "@typescript-eslint/parser": "^4.18.0", "@vuepress/plugin-google-analytics": "1.8.2", + "chartjs-adapter-luxon": "^1.0.0-beta.2", "chartjs-adapter-moment": "^0.1.2", "chartjs-test-utils": "^0.2.2", "concurrently": "^6.0.0", @@ -80,6 +81,7 @@ "karma-rollup-preprocessor": "^7.0.7", "karma-safari-private-launcher": "^1.0.0", "karma-spec-reporter": "0.0.32", + "luxon": "^1.26.0", "markdown-it-include": "^2.0.0", "moment": "^2.29.1", "pixelmatch": "^5.2.1",