From 1e2cd935ce14843985c948f26ed33e067448c848 Mon Sep 17 00:00:00 2001 From: dqmrf Date: Thu, 25 Jun 2026 15:45:20 +0200 Subject: [PATCH 1/4] H08: Create chart with separate axes and custom svg elements --- .../highcharts/08-separate-axes/index.html | 4 +- .../highcharts/08-separate-axes/main.js | 244 ++++++++++++++++++ 2 files changed, 247 insertions(+), 1 deletion(-) diff --git a/highcharts-api/highcharts/08-separate-axes/index.html b/highcharts-api/highcharts/08-separate-axes/index.html index 6c800fd..cf4fec2 100644 --- a/highcharts-api/highcharts/08-separate-axes/index.html +++ b/highcharts-api/highcharts/08-separate-axes/index.html @@ -1,13 +1,15 @@ + Separate Axes - + + diff --git a/highcharts-api/highcharts/08-separate-axes/main.js b/highcharts-api/highcharts/08-separate-axes/main.js index e69de29..ac61032 100644 --- a/highcharts-api/highcharts/08-separate-axes/main.js +++ b/highcharts-api/highcharts/08-separate-axes/main.js @@ -0,0 +1,244 @@ +const categories = [ 'Dep1', 'Dep2', 'Dep3', 'Dep4', 'Dep5' ]; +const yMaxValue = 100; +const halfWidth = 45; // Width(%) of one chart half. + + +class SvgElement { + constructor(chart) { + if (new.target === SvgElement) { + throw new Error('"SvgElement" is abstract.'); + } + this.svg = null; + this.chart = chart; + } + create() { throw new Error('"SvgElement.create" must be implemented.'); } + update() { throw new Error('"SvgElement.update" must be implemented.'); } + destroy() { this.svg?.destroy(); } +} + +class SvgAxisTitle extends SvgElement { + static css = { + 'font-size': '12px', + 'font-weight': 'bold', + } + + constructor(chart, title) { + super(chart); + this.title = title; + this.y = 3; + } + + getXY() { + const { chart } = this; + const x = chart.plotLeft + (chart.plotWidth * halfWidth / 2 / 100); + + return { x, y: this.y } + } + + create() { + const { chart } = this; + const { x, y } = this.getXY(); + + this.svg = chart.renderer + .label(this.title, x, y) + .css(SvgAxisTitle.css) + .add(); + + this.update(); + return this; + } + + update() { + const { x, y } = this.getXY(); + + this.svg.attr({ + x: x - this.svg.width / 2, + y + }); + + return this; + } +} + +class SvgAxisTitleRight extends SvgAxisTitle { + getXY() { + const { chart } = this; + const rightHalfOffset = 100 - halfWidth; + const x = chart.plotLeft + + (chart.plotWidth * (rightHalfOffset + halfWidth / 2) / 100); + + return { x, y: this.y } + } +} + +class SvgBarLabel extends SvgElement { + static css = { + color: 'gray', + 'font-size': '12px' + } + + constructor(chart, point) { + super(chart); + this.point = point; + } + + getXY() { + const { chart, point } = this; + const { shapeArgs } = point; + + const x = chart.plotLeft + chart.plotWidth / 2, + y = shapeArgs.x + chart.plotTop + shapeArgs.width / 2; + + return { x, y }; + } + + create() { + const { x, y } = this.getXY(); + + this.svg = this.chart.renderer + .label(this.point.category, x, y) + .css(SvgBarLabel.css) + .add(); + + this.update(); + return this; + } + + update() { + const { svg } = this; + const { x, y } = this.getXY(); + + svg.attr({ + x: x - svg.width / 2, + y: y - svg.height / 2 + }); + + return this; + } +} + + +const chartEvents = { + render: function() { + const chart = this; + + if (chart.svgElements) { + chart.svgElements.forEach(e => e.update()); + return; + } + + chart.svgElements = []; + + chart.series[0].points.forEach(point => { + const label = new SvgBarLabel(chart, point); + label.create(); + chart.svgElements.push(label); + }); + + const leftAxisTitle = new SvgAxisTitle( + chart, + 'Manegrial Position' + ).create(); + + chart.svgElements.push(leftAxisTitle); + + const rightAxisTitle = new SvgAxisTitleRight( + chart, + 'Non Manegrial Position' + ).create(); + + chart.svgElements.push(rightAxisTitle); + }, + + destroy: function() { + this.svgElements?.forEach(e => e.destroy()); + } +}; + +Highcharts.chart('container', { + chart: { + type: 'bar', + events: chartEvents, + marginTop: 36 + }, + title: false, + yAxis: [{ + width: `${halfWidth}%`, + max: yMaxValue, + reversed: true, + title: { text: null } + }, { + width: `${halfWidth}%`, + left: `${100 - halfWidth}%`, + offset: 0, + max: yMaxValue, + title: { text: null } + }], + xAxis: { + visible: false, + reversed: false, + categories + }, + legend: { + enabled: false + }, + plotOptions: { + series: { + borderWidth: 0, + borderRadius: 0, + grouping: false, + } + }, + exporting: { + buttons: { + contextButton: { + enabled: false + } + } + }, + series: [{ + yAxis: 0, + name: 'Manegrial Background', + data: Array(5).fill(yMaxValue), + color: 'lightgray', + enableMouseTracking: false, + states: { hover: { enabled: false } } + }, { + yAxis: 0, + name: 'Manegrial Position', + data: [80, 16, 96, 60, 72], + color: 'red', + dataLabels: { + enabled: true, + inside: true, + align: 'right', + format: '{y} %', + x: -4, + style: { + color: 'black' + } + } + }, { + yAxis: 1, + name: 'Non Manegrial Background', + data: Array(5).fill(yMaxValue), + color: 'lightgray', + enableMouseTracking: false, + states: { hover: { enabled: false } } + }, { + yAxis: 1, + name: 'Non Manegrial Position', + data: [21, 6, 9, 50, 81], + color: 'red', + dataLabels: { + enabled: true, + inside: true, + align: 'left', + format: '{y} %', + x: 4, + style: { + color: 'black' + } + } + }] +}); From ae26c687afc7833cabdcfedb8e29a075f8f9aead Mon Sep 17 00:00:00 2001 From: dqmrf Date: Fri, 26 Jun 2026 14:22:13 +0200 Subject: [PATCH 2/4] H08: Refactor all --- .../highcharts/08-separate-axes/main.js | 204 +++++---------- .../highcharts/08-separate-axes/main.old.js | 244 ++++++++++++++++++ 2 files changed, 303 insertions(+), 145 deletions(-) create mode 100644 highcharts-api/highcharts/08-separate-axes/main.old.js diff --git a/highcharts-api/highcharts/08-separate-axes/main.js b/highcharts-api/highcharts/08-separate-axes/main.js index ac61032..5312c6a 100644 --- a/highcharts-api/highcharts/08-separate-axes/main.js +++ b/highcharts-api/highcharts/08-separate-axes/main.js @@ -1,157 +1,71 @@ const categories = [ 'Dep1', 'Dep2', 'Dep3', 'Dep4', 'Dep5' ]; -const yMaxValue = 100; -const halfWidth = 45; // Width(%) of one chart half. +const yAxisMax = 100; +const axisWidth = 45; +const chartEvents = { + load: function() { + const chart = this; -class SvgElement { - constructor(chart) { - if (new.target === SvgElement) { - throw new Error('"SvgElement" is abstract.'); - } - this.svg = null; - this.chart = chart; - } - create() { throw new Error('"SvgElement.create" must be implemented.'); } - update() { throw new Error('"SvgElement.update" must be implemented.'); } - destroy() { this.svg?.destroy(); } -} - -class SvgAxisTitle extends SvgElement { - static css = { - 'font-size': '12px', - 'font-weight': 'bold', - } - - constructor(chart, title) { - super(chart); - this.title = title; - this.y = 3; - } - - getXY() { - const { chart } = this; - const x = chart.plotLeft + (chart.plotWidth * halfWidth / 2 / 100); - - return { x, y: this.y } - } - - create() { - const { chart } = this; - const { x, y } = this.getXY(); - - this.svg = chart.renderer - .label(this.title, x, y) - .css(SvgAxisTitle.css) - .add(); - - this.update(); - return this; - } - - update() { - const { x, y } = this.getXY(); - - this.svg.attr({ - x: x - this.svg.width / 2, - y - }); - - return this; - } -} - -class SvgAxisTitleRight extends SvgAxisTitle { - getXY() { - const { chart } = this; - const rightHalfOffset = 100 - halfWidth; - const x = chart.plotLeft + - (chart.plotWidth * (rightHalfOffset + halfWidth / 2) / 100); - - return { x, y: this.y } - } -} - -class SvgBarLabel extends SvgElement { - static css = { - color: 'gray', - 'font-size': '12px' - } - - constructor(chart, point) { - super(chart); - this.point = point; - } - - getXY() { - const { chart, point } = this; - const { shapeArgs } = point; - - const x = chart.plotLeft + chart.plotWidth / 2, - y = shapeArgs.x + chart.plotTop + shapeArgs.width / 2; - - return { x, y }; - } - - create() { - const { x, y } = this.getXY(); - - this.svg = this.chart.renderer - .label(this.point.category, x, y) - .css(SvgBarLabel.css) - .add(); - - this.update(); - return this; - } - - update() { - const { svg } = this; - const { x, y } = this.getXY(); - - svg.attr({ - x: x - svg.width / 2, - y: y - svg.height / 2 - }); - - return this; - } -} + chart.customAxisLabels = [ + { text: 'Manegrial Position', axis: 0 }, + { text: 'Non Manegrial Position', axis: 1 }, + ]; + chart.customBarLabels = categories.map(c => ({ text: c })); + }, -const chartEvents = { render: function() { const chart = this; + const { + renderer, customAxisLabels, customBarLabels, plotLeft, plotTop, plotWidth + } = chart; + + // Create bar labels + chart.series[0].points.forEach((point, i) => { + const { shapeArgs } = point; + const l = customBarLabels[i]; + const x = plotLeft + plotWidth / 2; + const y = shapeArgs.x + plotTop + shapeArgs.width / 2; + + if (!l.element) { + l.element = renderer.label(l.text, 0, 0) + .css({ + color: 'gray', + 'font-size': '12px' + }) + .add(); + } - if (chart.svgElements) { - chart.svgElements.forEach(e => e.update()); - return; - } - - chart.svgElements = []; - - chart.series[0].points.forEach(point => { - const label = new SvgBarLabel(chart, point); - label.create(); - chart.svgElements.push(label); + l.element.attr({ + x: x - l.element.width / 2, + y: y - l.element.height / 2 + }); }); - const leftAxisTitle = new SvgAxisTitle( - chart, - 'Manegrial Position' - ).create(); + // Create axis labels + customAxisLabels.forEach(l => { + if (!l.element) { + l.element = renderer.label(l.text, 0, 0) + .css({ + 'font-size': '12px', + 'font-weight': 'bold', + }) + .add(); + } - chart.svgElements.push(leftAxisTitle); + const rightOffsetX = l.axis ? (100 - axisWidth) : 0; + const midX = plotLeft + (plotWidth * (rightOffsetX + axisWidth / 2) / 100); - const rightAxisTitle = new SvgAxisTitleRight( - chart, - 'Non Manegrial Position' - ).create(); + const x = midX - l.element.width / 2, + y = 3; - chart.svgElements.push(rightAxisTitle); + l.element.attr({ x, y }); + }); }, destroy: function() { - this.svgElements?.forEach(e => e.destroy()); + this.customAxisLabels?.forEach(l => l.element?.destroy()); + this.customBarLabels?.forEach(l => l.element?.destroy()); } }; @@ -163,15 +77,15 @@ Highcharts.chart('container', { }, title: false, yAxis: [{ - width: `${halfWidth}%`, - max: yMaxValue, + width: `${axisWidth}%`, + max: yAxisMax, reversed: true, title: { text: null } }, { - width: `${halfWidth}%`, - left: `${100 - halfWidth}%`, + width: `${axisWidth}%`, + left: `${100 - axisWidth}%`, offset: 0, - max: yMaxValue, + max: yAxisMax, title: { text: null } }], xAxis: { @@ -199,7 +113,7 @@ Highcharts.chart('container', { series: [{ yAxis: 0, name: 'Manegrial Background', - data: Array(5).fill(yMaxValue), + data: Array(categories.length).fill(yAxisMax), color: 'lightgray', enableMouseTracking: false, states: { hover: { enabled: false } } @@ -221,7 +135,7 @@ Highcharts.chart('container', { }, { yAxis: 1, name: 'Non Manegrial Background', - data: Array(5).fill(yMaxValue), + data: Array(categories.length).fill(yAxisMax), color: 'lightgray', enableMouseTracking: false, states: { hover: { enabled: false } } diff --git a/highcharts-api/highcharts/08-separate-axes/main.old.js b/highcharts-api/highcharts/08-separate-axes/main.old.js new file mode 100644 index 0000000..ac61032 --- /dev/null +++ b/highcharts-api/highcharts/08-separate-axes/main.old.js @@ -0,0 +1,244 @@ +const categories = [ 'Dep1', 'Dep2', 'Dep3', 'Dep4', 'Dep5' ]; +const yMaxValue = 100; +const halfWidth = 45; // Width(%) of one chart half. + + +class SvgElement { + constructor(chart) { + if (new.target === SvgElement) { + throw new Error('"SvgElement" is abstract.'); + } + this.svg = null; + this.chart = chart; + } + create() { throw new Error('"SvgElement.create" must be implemented.'); } + update() { throw new Error('"SvgElement.update" must be implemented.'); } + destroy() { this.svg?.destroy(); } +} + +class SvgAxisTitle extends SvgElement { + static css = { + 'font-size': '12px', + 'font-weight': 'bold', + } + + constructor(chart, title) { + super(chart); + this.title = title; + this.y = 3; + } + + getXY() { + const { chart } = this; + const x = chart.plotLeft + (chart.plotWidth * halfWidth / 2 / 100); + + return { x, y: this.y } + } + + create() { + const { chart } = this; + const { x, y } = this.getXY(); + + this.svg = chart.renderer + .label(this.title, x, y) + .css(SvgAxisTitle.css) + .add(); + + this.update(); + return this; + } + + update() { + const { x, y } = this.getXY(); + + this.svg.attr({ + x: x - this.svg.width / 2, + y + }); + + return this; + } +} + +class SvgAxisTitleRight extends SvgAxisTitle { + getXY() { + const { chart } = this; + const rightHalfOffset = 100 - halfWidth; + const x = chart.plotLeft + + (chart.plotWidth * (rightHalfOffset + halfWidth / 2) / 100); + + return { x, y: this.y } + } +} + +class SvgBarLabel extends SvgElement { + static css = { + color: 'gray', + 'font-size': '12px' + } + + constructor(chart, point) { + super(chart); + this.point = point; + } + + getXY() { + const { chart, point } = this; + const { shapeArgs } = point; + + const x = chart.plotLeft + chart.plotWidth / 2, + y = shapeArgs.x + chart.plotTop + shapeArgs.width / 2; + + return { x, y }; + } + + create() { + const { x, y } = this.getXY(); + + this.svg = this.chart.renderer + .label(this.point.category, x, y) + .css(SvgBarLabel.css) + .add(); + + this.update(); + return this; + } + + update() { + const { svg } = this; + const { x, y } = this.getXY(); + + svg.attr({ + x: x - svg.width / 2, + y: y - svg.height / 2 + }); + + return this; + } +} + + +const chartEvents = { + render: function() { + const chart = this; + + if (chart.svgElements) { + chart.svgElements.forEach(e => e.update()); + return; + } + + chart.svgElements = []; + + chart.series[0].points.forEach(point => { + const label = new SvgBarLabel(chart, point); + label.create(); + chart.svgElements.push(label); + }); + + const leftAxisTitle = new SvgAxisTitle( + chart, + 'Manegrial Position' + ).create(); + + chart.svgElements.push(leftAxisTitle); + + const rightAxisTitle = new SvgAxisTitleRight( + chart, + 'Non Manegrial Position' + ).create(); + + chart.svgElements.push(rightAxisTitle); + }, + + destroy: function() { + this.svgElements?.forEach(e => e.destroy()); + } +}; + +Highcharts.chart('container', { + chart: { + type: 'bar', + events: chartEvents, + marginTop: 36 + }, + title: false, + yAxis: [{ + width: `${halfWidth}%`, + max: yMaxValue, + reversed: true, + title: { text: null } + }, { + width: `${halfWidth}%`, + left: `${100 - halfWidth}%`, + offset: 0, + max: yMaxValue, + title: { text: null } + }], + xAxis: { + visible: false, + reversed: false, + categories + }, + legend: { + enabled: false + }, + plotOptions: { + series: { + borderWidth: 0, + borderRadius: 0, + grouping: false, + } + }, + exporting: { + buttons: { + contextButton: { + enabled: false + } + } + }, + series: [{ + yAxis: 0, + name: 'Manegrial Background', + data: Array(5).fill(yMaxValue), + color: 'lightgray', + enableMouseTracking: false, + states: { hover: { enabled: false } } + }, { + yAxis: 0, + name: 'Manegrial Position', + data: [80, 16, 96, 60, 72], + color: 'red', + dataLabels: { + enabled: true, + inside: true, + align: 'right', + format: '{y} %', + x: -4, + style: { + color: 'black' + } + } + }, { + yAxis: 1, + name: 'Non Manegrial Background', + data: Array(5).fill(yMaxValue), + color: 'lightgray', + enableMouseTracking: false, + states: { hover: { enabled: false } } + }, { + yAxis: 1, + name: 'Non Manegrial Position', + data: [21, 6, 9, 50, 81], + color: 'red', + dataLabels: { + enabled: true, + inside: true, + align: 'left', + format: '{y} %', + x: 4, + style: { + color: 'black' + } + } + }] +}); From e94e24458caf7c4782b196c7c088279d865efe5b Mon Sep 17 00:00:00 2001 From: dqmrf Date: Fri, 26 Jun 2026 14:22:13 +0200 Subject: [PATCH 3/4] H08: Refactor all --- .../highcharts/08-separate-axes/main.js | 204 +++++------------- 1 file changed, 59 insertions(+), 145 deletions(-) diff --git a/highcharts-api/highcharts/08-separate-axes/main.js b/highcharts-api/highcharts/08-separate-axes/main.js index ac61032..5312c6a 100644 --- a/highcharts-api/highcharts/08-separate-axes/main.js +++ b/highcharts-api/highcharts/08-separate-axes/main.js @@ -1,157 +1,71 @@ const categories = [ 'Dep1', 'Dep2', 'Dep3', 'Dep4', 'Dep5' ]; -const yMaxValue = 100; -const halfWidth = 45; // Width(%) of one chart half. +const yAxisMax = 100; +const axisWidth = 45; +const chartEvents = { + load: function() { + const chart = this; -class SvgElement { - constructor(chart) { - if (new.target === SvgElement) { - throw new Error('"SvgElement" is abstract.'); - } - this.svg = null; - this.chart = chart; - } - create() { throw new Error('"SvgElement.create" must be implemented.'); } - update() { throw new Error('"SvgElement.update" must be implemented.'); } - destroy() { this.svg?.destroy(); } -} - -class SvgAxisTitle extends SvgElement { - static css = { - 'font-size': '12px', - 'font-weight': 'bold', - } - - constructor(chart, title) { - super(chart); - this.title = title; - this.y = 3; - } - - getXY() { - const { chart } = this; - const x = chart.plotLeft + (chart.plotWidth * halfWidth / 2 / 100); - - return { x, y: this.y } - } - - create() { - const { chart } = this; - const { x, y } = this.getXY(); - - this.svg = chart.renderer - .label(this.title, x, y) - .css(SvgAxisTitle.css) - .add(); - - this.update(); - return this; - } - - update() { - const { x, y } = this.getXY(); - - this.svg.attr({ - x: x - this.svg.width / 2, - y - }); - - return this; - } -} - -class SvgAxisTitleRight extends SvgAxisTitle { - getXY() { - const { chart } = this; - const rightHalfOffset = 100 - halfWidth; - const x = chart.plotLeft + - (chart.plotWidth * (rightHalfOffset + halfWidth / 2) / 100); - - return { x, y: this.y } - } -} - -class SvgBarLabel extends SvgElement { - static css = { - color: 'gray', - 'font-size': '12px' - } - - constructor(chart, point) { - super(chart); - this.point = point; - } - - getXY() { - const { chart, point } = this; - const { shapeArgs } = point; - - const x = chart.plotLeft + chart.plotWidth / 2, - y = shapeArgs.x + chart.plotTop + shapeArgs.width / 2; - - return { x, y }; - } - - create() { - const { x, y } = this.getXY(); - - this.svg = this.chart.renderer - .label(this.point.category, x, y) - .css(SvgBarLabel.css) - .add(); - - this.update(); - return this; - } - - update() { - const { svg } = this; - const { x, y } = this.getXY(); - - svg.attr({ - x: x - svg.width / 2, - y: y - svg.height / 2 - }); - - return this; - } -} + chart.customAxisLabels = [ + { text: 'Manegrial Position', axis: 0 }, + { text: 'Non Manegrial Position', axis: 1 }, + ]; + chart.customBarLabels = categories.map(c => ({ text: c })); + }, -const chartEvents = { render: function() { const chart = this; + const { + renderer, customAxisLabels, customBarLabels, plotLeft, plotTop, plotWidth + } = chart; + + // Create bar labels + chart.series[0].points.forEach((point, i) => { + const { shapeArgs } = point; + const l = customBarLabels[i]; + const x = plotLeft + plotWidth / 2; + const y = shapeArgs.x + plotTop + shapeArgs.width / 2; + + if (!l.element) { + l.element = renderer.label(l.text, 0, 0) + .css({ + color: 'gray', + 'font-size': '12px' + }) + .add(); + } - if (chart.svgElements) { - chart.svgElements.forEach(e => e.update()); - return; - } - - chart.svgElements = []; - - chart.series[0].points.forEach(point => { - const label = new SvgBarLabel(chart, point); - label.create(); - chart.svgElements.push(label); + l.element.attr({ + x: x - l.element.width / 2, + y: y - l.element.height / 2 + }); }); - const leftAxisTitle = new SvgAxisTitle( - chart, - 'Manegrial Position' - ).create(); + // Create axis labels + customAxisLabels.forEach(l => { + if (!l.element) { + l.element = renderer.label(l.text, 0, 0) + .css({ + 'font-size': '12px', + 'font-weight': 'bold', + }) + .add(); + } - chart.svgElements.push(leftAxisTitle); + const rightOffsetX = l.axis ? (100 - axisWidth) : 0; + const midX = plotLeft + (plotWidth * (rightOffsetX + axisWidth / 2) / 100); - const rightAxisTitle = new SvgAxisTitleRight( - chart, - 'Non Manegrial Position' - ).create(); + const x = midX - l.element.width / 2, + y = 3; - chart.svgElements.push(rightAxisTitle); + l.element.attr({ x, y }); + }); }, destroy: function() { - this.svgElements?.forEach(e => e.destroy()); + this.customAxisLabels?.forEach(l => l.element?.destroy()); + this.customBarLabels?.forEach(l => l.element?.destroy()); } }; @@ -163,15 +77,15 @@ Highcharts.chart('container', { }, title: false, yAxis: [{ - width: `${halfWidth}%`, - max: yMaxValue, + width: `${axisWidth}%`, + max: yAxisMax, reversed: true, title: { text: null } }, { - width: `${halfWidth}%`, - left: `${100 - halfWidth}%`, + width: `${axisWidth}%`, + left: `${100 - axisWidth}%`, offset: 0, - max: yMaxValue, + max: yAxisMax, title: { text: null } }], xAxis: { @@ -199,7 +113,7 @@ Highcharts.chart('container', { series: [{ yAxis: 0, name: 'Manegrial Background', - data: Array(5).fill(yMaxValue), + data: Array(categories.length).fill(yAxisMax), color: 'lightgray', enableMouseTracking: false, states: { hover: { enabled: false } } @@ -221,7 +135,7 @@ Highcharts.chart('container', { }, { yAxis: 1, name: 'Non Manegrial Background', - data: Array(5).fill(yMaxValue), + data: Array(categories.length).fill(yAxisMax), color: 'lightgray', enableMouseTracking: false, states: { hover: { enabled: false } } From 49537d3ff629efa6b3e3acf65c067497f302ba18 Mon Sep 17 00:00:00 2001 From: dqmrf Date: Fri, 26 Jun 2026 14:30:31 +0200 Subject: [PATCH 4/4] H08: Remove old main.js file --- .../highcharts/08-separate-axes/main.old.js | 244 ------------------ 1 file changed, 244 deletions(-) delete mode 100644 highcharts-api/highcharts/08-separate-axes/main.old.js diff --git a/highcharts-api/highcharts/08-separate-axes/main.old.js b/highcharts-api/highcharts/08-separate-axes/main.old.js deleted file mode 100644 index ac61032..0000000 --- a/highcharts-api/highcharts/08-separate-axes/main.old.js +++ /dev/null @@ -1,244 +0,0 @@ -const categories = [ 'Dep1', 'Dep2', 'Dep3', 'Dep4', 'Dep5' ]; -const yMaxValue = 100; -const halfWidth = 45; // Width(%) of one chart half. - - -class SvgElement { - constructor(chart) { - if (new.target === SvgElement) { - throw new Error('"SvgElement" is abstract.'); - } - this.svg = null; - this.chart = chart; - } - create() { throw new Error('"SvgElement.create" must be implemented.'); } - update() { throw new Error('"SvgElement.update" must be implemented.'); } - destroy() { this.svg?.destroy(); } -} - -class SvgAxisTitle extends SvgElement { - static css = { - 'font-size': '12px', - 'font-weight': 'bold', - } - - constructor(chart, title) { - super(chart); - this.title = title; - this.y = 3; - } - - getXY() { - const { chart } = this; - const x = chart.plotLeft + (chart.plotWidth * halfWidth / 2 / 100); - - return { x, y: this.y } - } - - create() { - const { chart } = this; - const { x, y } = this.getXY(); - - this.svg = chart.renderer - .label(this.title, x, y) - .css(SvgAxisTitle.css) - .add(); - - this.update(); - return this; - } - - update() { - const { x, y } = this.getXY(); - - this.svg.attr({ - x: x - this.svg.width / 2, - y - }); - - return this; - } -} - -class SvgAxisTitleRight extends SvgAxisTitle { - getXY() { - const { chart } = this; - const rightHalfOffset = 100 - halfWidth; - const x = chart.plotLeft + - (chart.plotWidth * (rightHalfOffset + halfWidth / 2) / 100); - - return { x, y: this.y } - } -} - -class SvgBarLabel extends SvgElement { - static css = { - color: 'gray', - 'font-size': '12px' - } - - constructor(chart, point) { - super(chart); - this.point = point; - } - - getXY() { - const { chart, point } = this; - const { shapeArgs } = point; - - const x = chart.plotLeft + chart.plotWidth / 2, - y = shapeArgs.x + chart.plotTop + shapeArgs.width / 2; - - return { x, y }; - } - - create() { - const { x, y } = this.getXY(); - - this.svg = this.chart.renderer - .label(this.point.category, x, y) - .css(SvgBarLabel.css) - .add(); - - this.update(); - return this; - } - - update() { - const { svg } = this; - const { x, y } = this.getXY(); - - svg.attr({ - x: x - svg.width / 2, - y: y - svg.height / 2 - }); - - return this; - } -} - - -const chartEvents = { - render: function() { - const chart = this; - - if (chart.svgElements) { - chart.svgElements.forEach(e => e.update()); - return; - } - - chart.svgElements = []; - - chart.series[0].points.forEach(point => { - const label = new SvgBarLabel(chart, point); - label.create(); - chart.svgElements.push(label); - }); - - const leftAxisTitle = new SvgAxisTitle( - chart, - 'Manegrial Position' - ).create(); - - chart.svgElements.push(leftAxisTitle); - - const rightAxisTitle = new SvgAxisTitleRight( - chart, - 'Non Manegrial Position' - ).create(); - - chart.svgElements.push(rightAxisTitle); - }, - - destroy: function() { - this.svgElements?.forEach(e => e.destroy()); - } -}; - -Highcharts.chart('container', { - chart: { - type: 'bar', - events: chartEvents, - marginTop: 36 - }, - title: false, - yAxis: [{ - width: `${halfWidth}%`, - max: yMaxValue, - reversed: true, - title: { text: null } - }, { - width: `${halfWidth}%`, - left: `${100 - halfWidth}%`, - offset: 0, - max: yMaxValue, - title: { text: null } - }], - xAxis: { - visible: false, - reversed: false, - categories - }, - legend: { - enabled: false - }, - plotOptions: { - series: { - borderWidth: 0, - borderRadius: 0, - grouping: false, - } - }, - exporting: { - buttons: { - contextButton: { - enabled: false - } - } - }, - series: [{ - yAxis: 0, - name: 'Manegrial Background', - data: Array(5).fill(yMaxValue), - color: 'lightgray', - enableMouseTracking: false, - states: { hover: { enabled: false } } - }, { - yAxis: 0, - name: 'Manegrial Position', - data: [80, 16, 96, 60, 72], - color: 'red', - dataLabels: { - enabled: true, - inside: true, - align: 'right', - format: '{y} %', - x: -4, - style: { - color: 'black' - } - } - }, { - yAxis: 1, - name: 'Non Manegrial Background', - data: Array(5).fill(yMaxValue), - color: 'lightgray', - enableMouseTracking: false, - states: { hover: { enabled: false } } - }, { - yAxis: 1, - name: 'Non Manegrial Position', - data: [21, 6, 9, 50, 81], - color: 'red', - dataLabels: { - enabled: true, - inside: true, - align: 'left', - format: '{y} %', - x: 4, - style: { - color: 'black' - } - } - }] -});