From 7a89925ecc59fe4cebaf942cb67cc8571259dbce Mon Sep 17 00:00:00 2001 From: dqmrf Date: Wed, 24 Jun 2026 11:20:19 +0200 Subject: [PATCH 1/3] H06: Add chart with stacked bars and custom svg elements --- .../highcharts/06-stacked-bar/index.html | 13 +- .../highcharts/06-stacked-bar/main.js | 145 ++++++++++++++++++ 2 files changed, 152 insertions(+), 6 deletions(-) diff --git a/highcharts-api/highcharts/06-stacked-bar/index.html b/highcharts-api/highcharts/06-stacked-bar/index.html index 6c800fd..f3956ba 100644 --- a/highcharts-api/highcharts/06-stacked-bar/index.html +++ b/highcharts-api/highcharts/06-stacked-bar/index.html @@ -2,16 +2,17 @@ - - - + + + - + + -
+
- \ No newline at end of file + diff --git a/highcharts-api/highcharts/06-stacked-bar/main.js b/highcharts-api/highcharts/06-stacked-bar/main.js index e69de29..78d0c33 100644 --- a/highcharts-api/highcharts/06-stacked-bar/main.js +++ b/highcharts-api/highcharts/06-stacked-bar/main.js @@ -0,0 +1,145 @@ +const categories = ['Data', 'Emails', 'Duplicates', 'Support']; + +const buttonAttrs = { + height: 6, + zIndex: 10, + stroke: 'blue', +}; +const buttonCSS = { 'font-size': '13px' }; +const labelCSS = { 'font-size': '10px' }; + +const chartEvents = { + render: function() { + const chart = this; + + if (chart.svgElements) { + chart.svgElements.forEach(e => e.destroy()); + } + chart.svgElements = []; + + const buttons = addButtons(chart); + chart.svgElements.push(...buttons); + + const labels = addLabels(chart); + chart.svgElements.push(...labels); + } +} + +Highcharts.chart('container', { + dataTable: { + columns: { + Categories: ['Data', 'Emails', 'Duplicates', 'Support'], + Orange: [100, 130, 35, 30], + Green: [15, 0, 15, 10], + Blue: [110, 110, 30], + Red: [15, 0, 10, 5] + } + }, + chart: { + type: 'bar', + events: chartEvents, + marginTop: 20 + }, + title: { + text: null + }, + legend: { + enabled: false + }, + xAxis: { + type: 'category', + categories, + lineWidth: 0, + gridLineWidth: 1 + }, + yAxis: { + title: { text: 'Amount' }, + softMax: 400, + gridLineWidth: 0, + stackLabels: { + enabled: true, + formatter: function() { + return this.total + ' K'; + } + } + }, + plotOptions: { + series: { + dataMapping: { + name: 'Categories' + }, + stacking: 'normal' + } + }, + series: [{ + dataMapping: { + y: 'Orange' + } + }, { + dataMapping: { + y: 'Green' + } + }, { + dataMapping: { + y: 'Blue' + } + }, { + dataMapping: { + y: 'Red' + } + }] +}); + +function addLabels(chart) { + const labelY = -5; + const label1X = 3, + label2X = chart.plotLeft - 3, + label3X = chart.plotWidth; + + const label1 = chart.renderer + .label('Issue', label1X, labelY) + .css(labelCSS) + .add(); + + const label2 = chart.renderer + .label('Record Count', label2X, labelY) + .css(labelCSS) + .add(); + + const label3 = chart.renderer + .label('Action', label3X, labelY) + .css(labelCSS) + .add(); + + return [label1, label2, label3]; +} + +function addButtons(chart) { + const buttonX = chart.plotWidth; + const buttons = []; + + chart.series[0].points.forEach((point, i) => { + const pointX = chart.plotTop + point.shapeArgs.x; + const pointWidth = point.shapeArgs.width; + + const button = chart.renderer + .button( + 'How to fix', + buttonX, + pointX, + function () {}, + buttonAttrs + ) + .css(buttonCSS) + .add(); + + buttons.push(button); + + // You can only get correct button height after it was rendered. + button.attr({ + y: pointX + pointWidth / 2 - button.height / 2 + }); + }); + + return buttons; +} From 15e955635ee72e9484be3dc11a94b10c662ca3cd Mon Sep 17 00:00:00 2001 From: dqmrf Date: Wed, 24 Jun 2026 11:22:09 +0200 Subject: [PATCH 2/3] H06: Remove unnecessary data --- highcharts-api/highcharts/06-stacked-bar/main.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/highcharts-api/highcharts/06-stacked-bar/main.js b/highcharts-api/highcharts/06-stacked-bar/main.js index 78d0c33..ef13034 100644 --- a/highcharts-api/highcharts/06-stacked-bar/main.js +++ b/highcharts-api/highcharts/06-stacked-bar/main.js @@ -1,5 +1,3 @@ -const categories = ['Data', 'Emails', 'Duplicates', 'Support']; - const buttonAttrs = { height: 6, zIndex: 10, @@ -48,7 +46,6 @@ Highcharts.chart('container', { }, xAxis: { type: 'category', - categories, lineWidth: 0, gridLineWidth: 1 }, From 63c4fecb88f798b52b8e7f62cfd403926daefab1 Mon Sep 17 00:00:00 2001 From: dqmrf Date: Fri, 26 Jun 2026 12:19:18 +0200 Subject: [PATCH 3/3] H06: Refactor chart --- .../highcharts/06-stacked-bar/main.js | 168 ++++++------------ 1 file changed, 59 insertions(+), 109 deletions(-) diff --git a/highcharts-api/highcharts/06-stacked-bar/main.js b/highcharts-api/highcharts/06-stacked-bar/main.js index ef13034..05e9b3c 100644 --- a/highcharts-api/highcharts/06-stacked-bar/main.js +++ b/highcharts-api/highcharts/06-stacked-bar/main.js @@ -1,42 +1,59 @@ -const buttonAttrs = { - height: 6, - zIndex: 10, - stroke: 'blue', -}; -const buttonCSS = { 'font-size': '13px' }; -const labelCSS = { 'font-size': '10px' }; - -const chartEvents = { - render: function() { - const chart = this; - - if (chart.svgElements) { - chart.svgElements.forEach(e => e.destroy()); - } - chart.svgElements = []; - - const buttons = addButtons(chart); - chart.svgElements.push(...buttons); - - const labels = addLabels(chart); - chart.svgElements.push(...labels); - } -} +const categories = ['Data', 'Emails', 'Duplicates', 'Support']; Highcharts.chart('container', { - dataTable: { - columns: { - Categories: ['Data', 'Emails', 'Duplicates', 'Support'], - Orange: [100, 130, 35, 30], - Green: [15, 0, 15, 10], - Blue: [110, 110, 30], - Red: [15, 0, 10, 5] - } - }, chart: { type: 'bar', - events: chartEvents, - marginTop: 20 + marginTop: 20, + events: { + load: function() { + const chart = this; + + chart.customLabels = []; + chart.customButtons = []; + }, + render: function() { + const chart = this; + const { renderer, series, xAxis, customLabels, customButtons } = chart; + + // Add buttons + series[0].points.forEach((point, i) => { + if (!customButtons[i]) { + customButtons[i] = renderer + .button('How to fix', 0, 0, function () {}, { + height: 6, + zIndex: 10, + stroke: 'blue', + }) + .css({ 'font-size': '13px' }) + .add(); + } + + const pointWidth = point.shapeArgs.width; + const pointX = chart.plotTop + point.shapeArgs.x; + + customButtons[i].attr({ + x: chart.plotWidth, + y: pointX + pointWidth / 2 - customButtons[i].height / 2 + }); + }); + + // Add labels + if (customLabels.length == 0) { + customLabels.push( + ...['Issue', 'Record Count', 'Action'] + .map(title => + renderer.label(title, 0, chart.plotTop - 25) + .css({ 'font-size': '10px' }) + .add() + ) + ); + } + + customLabels[0].attr({ x: 3 }); + customLabels[1].attr({ x: chart.plotLeft - 3 }); + customLabels[2].attr({ x: chart.plotWidth }); + } + } }, title: { text: null @@ -45,9 +62,9 @@ Highcharts.chart('container', { enabled: false }, xAxis: { - type: 'category', lineWidth: 0, - gridLineWidth: 1 + gridLineWidth: 1, + categories }, yAxis: { title: { text: 'Amount' }, @@ -55,88 +72,21 @@ Highcharts.chart('container', { gridLineWidth: 0, stackLabels: { enabled: true, - formatter: function() { - return this.total + ' K'; - } + format: '{total} K' } }, plotOptions: { series: { - dataMapping: { - name: 'Categories' - }, stacking: 'normal' } }, series: [{ - dataMapping: { - y: 'Orange' - } + data: [100, 130, 35, 30], }, { - dataMapping: { - y: 'Green' - } + data: [15, 0, 15, 10], }, { - dataMapping: { - y: 'Blue' - } + data: [110, 110, 30, 40] }, { - dataMapping: { - y: 'Red' - } + data: [15, 0, 10, 5] }] }); - -function addLabels(chart) { - const labelY = -5; - const label1X = 3, - label2X = chart.plotLeft - 3, - label3X = chart.plotWidth; - - const label1 = chart.renderer - .label('Issue', label1X, labelY) - .css(labelCSS) - .add(); - - const label2 = chart.renderer - .label('Record Count', label2X, labelY) - .css(labelCSS) - .add(); - - const label3 = chart.renderer - .label('Action', label3X, labelY) - .css(labelCSS) - .add(); - - return [label1, label2, label3]; -} - -function addButtons(chart) { - const buttonX = chart.plotWidth; - const buttons = []; - - chart.series[0].points.forEach((point, i) => { - const pointX = chart.plotTop + point.shapeArgs.x; - const pointWidth = point.shapeArgs.width; - - const button = chart.renderer - .button( - 'How to fix', - buttonX, - pointX, - function () {}, - buttonAttrs - ) - .css(buttonCSS) - .add(); - - buttons.push(button); - - // You can only get correct button height after it was rendered. - button.attr({ - y: pointX + pointWidth / 2 - button.height / 2 - }); - }); - - return buttons; -}