From 0f5ba74ad9ca15792f75289c393c36c7741736f0 Mon Sep 17 00:00:00 2001 From: wchmiel Date: Mon, 15 Sep 2025 11:27:42 +0200 Subject: [PATCH 01/16] Rewritten package into ts, added some gulp compile tasks, etc --- .eslint-default-config.yml | 106 --- .eslintrc | 28 - .eslintrc.json | 115 +++ .gitignore | 2 + compileExterns.js | 2 + demo.html | 203 +++++ dist/grouped-categories.js | 506 ++++++++++++ ...categories.js => grouped-categories-old.js | 0 gulpfile.js | 102 ++- index.html | 7 +- jest.config.js | 20 + package.json | 49 +- test-setup.ts | 24 + ts/HighchartsConfig.d.ts | 148 ++++ ts/groupedCategories.ts | 736 ++++++++++++++++++ tsconfig.json | 28 + 16 files changed, 1925 insertions(+), 151 deletions(-) delete mode 100644 .eslint-default-config.yml delete mode 100644 .eslintrc create mode 100644 .eslintrc.json create mode 100644 compileExterns.js create mode 100644 demo.html create mode 100644 dist/grouped-categories.js rename grouped-categories.js => grouped-categories-old.js (100%) create mode 100644 jest.config.js create mode 100644 test-setup.ts create mode 100644 ts/HighchartsConfig.d.ts create mode 100644 ts/groupedCategories.ts create mode 100644 tsconfig.json diff --git a/.eslint-default-config.yml b/.eslint-default-config.yml deleted file mode 100644 index 83e30a7..0000000 --- a/.eslint-default-config.yml +++ /dev/null @@ -1,106 +0,0 @@ -# Copied from https://github.com/eslint/eslint/blob/master/packages/eslint-config-eslint/default.yml -# Updated 2015-04-06 - -extends: - "eslint:recommended" - -rules: - array-callback-return: "error" - indent: ["error", 4, {SwitchCase: 1}] - block-spacing: "error" - brace-style: ["error", "1tbs"] - camelcase: ["error", { properties: "never" }] - callback-return: ["error", ["cb", "callback", "next"]] - comma-spacing: "error" - comma-style: ["error", "last"] - consistent-return: "error" - curly: ["error", "all"] - default-case: "error" - dot-notation: ["error", { allowKeywords: true }] - eol-last: "error" - eqeqeq: "error" - func-style: ["error", "declaration"] - guard-for-in: "error" - key-spacing: ["error", { beforeColon: false, afterColon: true }] - keyword-spacing: "error" - lines-around-comment: ["error", { - beforeBlockComment: true, - afterBlockComment: false, - beforeLineComment: true, - afterLineComment: false - }] - new-cap: "error" - newline-after-var: "error" - new-parens: "error" - no-alert: "error" - no-array-constructor: "error" - no-caller: "error" - no-console: 0 - no-delete-var: "error" - no-eval: "error" - no-extend-native: "error" - no-extra-bind: "error" - no-fallthrough: "error" - no-floating-decimal: "error" - no-implied-eval: "error" - no-invalid-this: "error" - no-iterator: "error" - no-label-var: "error" - no-labels: "error" - no-lone-blocks: "error" - no-loop-func: "error" - no-mixed-spaces-and-tabs: ["error", false] - no-multi-spaces: "error" - no-multi-str: "error" - no-native-reassign: "error" - no-nested-ternary: "error" - no-new: "error" - no-new-func: "error" - no-new-object: "error" - no-new-wrappers: "error" - no-octal: "error" - no-octal-escape: "error" - no-process-exit: "error" - no-proto: "error" - no-redeclare: "error" - no-return-assign: "error" - no-script-url: "error" - no-self-assign: "error" - no-sequences: "error" - no-shadow: "error" - no-shadow-restricted-names: "error" - no-spaced-func: "error" - no-trailing-spaces: "error" - no-undef: "error" - no-undef-init: "error" - no-undefined: "error" - no-underscore-dangle: ["error", {allowAfterThis: true}] - no-unmodified-loop-condition: "error" - no-unused-expressions: "error" - no-unused-vars: ["error", {vars: "all", args: "after-used"}] - no-use-before-define: "error" - no-useless-concat: "error" - no-with: "error" - one-var-declaration-per-line: "error" - quotes: ["error", "double"] - radix: "error" - require-jsdoc: "error" - semi: "error" - semi-spacing: ["error", {before: false, after: true}] - space-before-blocks: "error" - space-before-function-paren: ["error", "never"] - space-in-parens: "error" - space-infix-ops: "error" - space-unary-ops: ["error", {words: true, nonwords: false}] - spaced-comment: ["error", "always", { exceptions: ["-"]}] - strict: ["error", "global"] - valid-jsdoc: ["error", { prefer: { "return": "returns"}}] - wrap-iife: "error" - yoda: ["error", "never"] - - # Previously on by default in node environment - no-catch-shadow: "off" - no-mixed-requires: "error" - no-new-require: "error" - no-path-concat: "error" - handle-callback-err: ["error", "err"] \ No newline at end of file diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index fe1d162..0000000 --- a/.eslintrc +++ /dev/null @@ -1,28 +0,0 @@ -extends: - ".eslint-default-config.yml" - -rules: - camelcase: [2, {"properties": "always"}] - comma-dangle: [2, "never"] - dot-location: [2, "property"] - lines-around-comment: 0 - newline-after-var: 0 - no-alert: 2 - no-console: 2 - no-debugger: 2 - no-else-return: 2 - no-unmodified-loop-condition: 0 - object-curly-spacing: [2, "always"] - operator-linebreak: [2, "after"] - space-before-function-paren: [2, {"anonymous": "always", "named": "never"}] # JSLint style - strict: 0 - quotes: [2, "single"] - - no-trailing-spaces: ["error", { "skipBlankLines": true }] - indent: ["error", "tab", {SwitchCase: 1}] - no-nested-ternary: 0 - no-invalid-this: 0 - eol-last: 0 - require-jsdoc: 0 - brace-style: [2, "1tbs", { "allowSingleLine": true }] - wrap-iife: [2, "any"] \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..df371ef --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,115 @@ +{ + "env": { + "browser": true + }, + "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"], + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint"], + "parserOptions": { + "project": "./tsconfig.json", + "ecmaVersion": 2020, + "sourceType": "module" + }, + "ignorePatterns": [ + "node_modules", + "dist", + "**/*.d.ts", + "**/*.test.ts", + "gulpfile.js", + "jest.config.js", + "test-setup.ts", + "compileExterns.js", + "grouped-categories-old.js" + ], + "rules": { + "camelcase": [ + 2, + { + "properties": "always" + } + ], + "capitalized-comments": ["warn", "always", { "ignoreConsecutiveComments": true }], + "class-methods-use-this": 0, + "comma-dangle": [ + 2, + "never" + ], + "consistent-return": 1, + "default-case": 1, + "default-param-last": 1, + "dot-notation": 0, + "function-paren-newline": 0, + "func-style": 0, + "indent": 2, + "max-len": [ + "error", + { + "code": 120, + "comments": 120, + "ignoreUrls": true, + "ignoreComments": true + } + ], + "no-dupe-class-members": 2, + "no-inner-declarations": 2, + "no-invalid-this": 0, + "no-shadow": 2, + "no-undef": 0, + "no-underscore-dangle": 2, + "no-unused-expressions": 0, + "no-use-before-define": 2, + "no-useless-constructor": 2, + "no-useless-escape": 2, + "no-useless-return": 2, + "object-curly-spacing": [2, "always"], + "object-shorthand": 2, + "prefer-const": 1, + "prefer-spread": 0, + "prefer-rest-params": 0, + "require-unicode-regexp": 2, + "quote-props": [2, "as-needed", { "keywords": true, "unnecessary": false }], + "semi": 2, + "@typescript-eslint/array-type": [2, { "default": "array-simple" }], + "@typescript-eslint/consistent-type-assertions": 2, + "@typescript-eslint/explicit-function-return-type": [ + "error", + { + "allowExpressions": false, + "allowTypedFunctionExpressions": false + } + ], + "@typescript-eslint/indent": [ + "error", + 4, + { + "FunctionExpression": { "parameters": 1 }, + "SwitchCase": 1 + } + ], + "@typescript-eslint/no-empty-function": 2, + "@typescript-eslint/no-empty-interface": 2, + "@typescript-eslint/no-explicit-any": 2, + "@typescript-eslint/no-floating-promises": 1, + "@typescript-eslint/no-inferrable-types": 1, + "@typescript-eslint/no-namespace": 2, + "@typescript-eslint/no-this-alias": 0, + "@typescript-eslint/no-unnecessary-type-assertion": 2, + "@typescript-eslint/no-unsafe-argument": 2, + "@typescript-eslint/no-unsafe-assignment": 2, + "@typescript-eslint/no-unsafe-call": 2, + "@typescript-eslint/no-unsafe-member-access": 2, + "@typescript-eslint/no-unsafe-return": 2, + "@typescript-eslint/no-unsafe-declaration-merging": 2, + "@typescript-eslint/no-unused-vars": 2, + "@typescript-eslint/no-use-before-define": 2, + "@typescript-eslint/no-useless-constructor": 2, + "@typescript-eslint/prefer-as-const": 1, + "@typescript-eslint/prefer-includes": 2, + "@typescript-eslint/prefer-regexp-exec": 2, + "@typescript-eslint/prefer-string-starts-ends-with": 0, + "@typescript-eslint/restrict-plus-operands": 1, + "@typescript-eslint/restrict-template-expressions": 1, + "@typescript-eslint/semi": 2, + "@typescript-eslint/unbound-method": 0 + } +} diff --git a/.gitignore b/.gitignore index 5e52727..de7063e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ /node_modules package-lock.json +.DS_Store +**/.DS_Store diff --git a/compileExterns.js b/compileExterns.js new file mode 100644 index 0000000..a005255 --- /dev/null +++ b/compileExterns.js @@ -0,0 +1,2 @@ +function Highcharts () {} +function module () {} \ No newline at end of file diff --git a/demo.html b/demo.html new file mode 100644 index 0000000..a33e9d4 --- /dev/null +++ b/demo.html @@ -0,0 +1,203 @@ + + + + + + Highcharts Grouped Categories Demo + + + + + + +
+

Highcharts Grouped Categories Demo

+ +
+

Basic Grouped Categories

+

This chart demonstrates simple two-level grouping with fruits and vegetables.

+
+ +
+
+
+ +
+

Nested Grouped Categories

+

This chart shows three-level nesting with continents, countries, and cities.

+
+ +
+
+
+ +
+

Custom Styling

+

This chart demonstrates custom styling for different group levels.

+
+ +
+
+
+
+ + + + diff --git a/dist/grouped-categories.js b/dist/grouped-categories.js new file mode 100644 index 0000000..c0eba3a --- /dev/null +++ b/dist/grouped-categories.js @@ -0,0 +1,506 @@ +/** +---- +* +* (c) 2012-2025 Black Label +* +* License: MIT +*/ +(function (factory) { + if (typeof module === 'object' && module.exports) { + module.exports = factory; + } else { + factory(Highcharts); + } +}(function (Highcharts) { +// Import Highcharts from 'highcharts-github/ts/Core/Globals'; + + + + + +const { merge, pick, objectEach, isNumber, isObject, isString, pInt, format, Tick, Axis, SVGElement } = Highcharts; + +// Utility functions +function deepClone(obj) { + return JSON.parse(JSON.stringify(obj)); +} +const sum = (arr) => arr.reduce((acc, val) => acc + val, 0); +function walk(arr, key, fn) { + for (let i = arr.length - 1; i >= 0; i--) { + const children = arr[i][key]; + if (children) { + walk(children, key, fn); + } + fn(arr[i]); + } +} +// Category class +class Category { + constructor(obj, parent) { + this.userOptions = deepClone(obj); + this.name = typeof obj === 'string' ? obj : (obj.name || ''); + this.parent = parent; + } + toString() { + const parts = []; + let cat = this; + while (cat) { + parts.push(cat.name); + cat = cat.parent; + } + return parts.join(', '); + } +} +// Add category leaf to array +function addLeaf(out, cat, parent) { + out.unshift(new Category(cat, parent)); + let currentParent = parent; + while (currentParent) { + currentParent.leaves = (currentParent.leaves || 0) + 1; + currentParent = currentParent.parent; + } +} +// Builds reverse category tree +function buildTree(cats, out, options, parent, depth = 0) { + options.depth = options.depth || 0; + for (let i = cats.length - 1; i >= 0; i--) { + const cat = cats[i]; + if (typeof cat === 'object' && cat.categories) { + if (parent) { + cat.parent = parent; + } + buildTree(cat.categories, out, options, cat, depth + 1); + } + else { + addLeaf(out, cat, parent); + } + } + options.depth = Math.max(options.depth, depth); +} +// Pushes part of grid to path +function addGridPart(path, d, width) { + // Based on crispLine from HC (#65) + if (d[0] === d[2]) { + d[0] = d[2] = Math.round(d[0]) - (width % 2 / 2); + } + if (d[1] === d[3]) { + d[1] = d[3] = Math.round(d[1]) + (width % 2 / 2); + } + path.push('M', d[0], d[1], 'L', d[2], d[3]); +} +// Returns tick position +function tickPosition(tick, pos) { + return tick.getPosition(tick.axis.horiz, pos, tick.axis.tickmarkOffset); +} +// Create local function `fontMetrics` to provide compatibility with HC 11 (#200) +function fontMetrics(fontSize, chart, elem) { + let fontSizeNum; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore: workaround for old IE, window.getComputedStyle always exists in modern browsers + if ((chart?.renderer.styledMode || (isString(fontSize) && fontSize.includes('px'))) && window.getComputedStyle) { + fontSizeNum = elem && SVGElement.prototype.getStyle.call(elem, 'font-size'); + } + else { + fontSizeNum = fontSize || elem?.styles.fontSize || chart?.renderer.style.fontSize; + } + if (isString(fontSizeNum) && fontSizeNum.includes('px')) { + fontSizeNum = pInt(fontSizeNum); + } + else if (!isNumber(fontSizeNum) || isNaN(fontSizeNum)) { + fontSizeNum = 12; + } + const lineHeight = (fontSizeNum < 24 ? fontSizeNum + 3 : Math.round(fontSizeNum * 1.2)); + const baseline = Math.round(lineHeight * 0.8); + return { + h: lineHeight, + b: baseline, + f: fontSizeNum + }; +} +// Main plugin implementation +// Cache prototypes +const axisProto = Axis.prototype; +const tickProto = Tick.prototype; +// Cache original methods +const protoAxisInit = axisProto.init; +const protoAxisRender = axisProto.render; +const protoAxisSetCategories = axisProto.setCategories; +const protoTickGetLabelSize = tickProto.getLabelSize; +const protoTickAddLabel = tickProto.addLabel; +const protoTickDestroy = tickProto.destroy; +const protoTickRender = tickProto.render; +const protoTickReplaceMovedLabel = tickProto.replaceMovedLabel; +// Axis prototype extensions +axisProto.init = function (chart, options, coll) { + protoAxisInit.call(this, chart, options, coll); + if (isObject(options) && options.categories) { + this.setupGroups(options); + } +}; +// Setup required axis options +axisProto.setupGroups = function (options) { + const axis = this; + const chart = axis.chart; + const categories = deepClone(options.categories || []); + const reverseTree = []; + const stats = { depth: 0 }; + const labelOptions = axis.options.labels; + const userAttr = labelOptions && labelOptions.groupedOptions; + const css = labelOptions && labelOptions.style; + buildTree(categories, reverseTree, stats); + axis.categoriesTree = categories; + axis.categories = reverseTree; + axis.isGrouped = stats.depth !== 0; + axis.labelsDepth = stats.depth; + axis.labelsSizes = []; + axis.labelsGridPath = []; + axis.tickLength = options.tickLength || axis.tickLength || null; + axis.tickWidth = pick(options.tickWidth, axis.isXAxis ? 1 : 0); + axis.directionFactor = [-1, 1, 1, -1][axis.side]; + axis.options.lineWidth = pick(options.lineWidth, 1); + axis.groupFontHeights = []; + for (let i = 0; i <= stats.depth; i++) { + const hasOptions = userAttr && userAttr[i - 1]; + const mergedCSS = hasOptions && userAttr[i - 1].style ? merge(css, userAttr[i - 1].style) : css; + axis.groupFontHeights[i] = Math.round(fontMetrics(mergedCSS.fontSize ? mergedCSS.fontSize : 0, chart).b * 0.3); + } +}; +axisProto.render = function () { + const axis = this; + if (axis.isGrouped) { + axis.labelsGridPath = []; + } + if (axis.originalTickLength === undefined) { + axis.originalTickLength = axis.options.tickLength; + } + axis.options.tickLength = axis.isGrouped ? 0.001 : axis.originalTickLength; + protoAxisRender.call(axis); + if (!axis.isGrouped) { + if (axis.labelsGrid) { + axis.labelsGrid.attr({ visibility: 'hidden' }); + } + return false; + } + const options = axis.options; + const top = axis.top; + const left = axis.left; + const right = left + axis.width; + const bottom = top + axis.height; + const visible = axis.hasVisibleSeries || axis.hasData; + let depth = axis.labelsDepth || 0; + let grid = axis.labelsGrid; + const horiz = axis.horiz; + const d = axis.labelsGridPath; + let i = options.drawHorizontalBorders === false ? (depth + 1) : 0; // TODO: check if this is needed + let offset = axis.opposite ? (horiz ? top : right) : (horiz ? bottom : left); + const tickWidth = axis.tickWidth || 0; + let part; + if (axis.userTickLength) { + depth -= 1; + } + if (!grid) { + grid = axis.labelsGrid = axis.chart.renderer.path() + .attr({ + strokeWidth: tickWidth, + 'stroke-width': tickWidth, + stroke: options.tickColor || '' + }) + .add(axis.axisGroup); + if (!options.tickColor) { + grid.addClass('highcharts-tick'); + } + } + while (i <= depth) { + offset += axis.groupSize(i); + part = horiz ? [left, offset, right, offset] : [offset, top, offset, bottom]; + addGridPart(d, part, tickWidth); + i++; + } + // TODO: fix it + grid.attr({ d: d, visibility: visible ? 'visible' : 'hidden' }); + axis.labelGroup?.attr({ visibility: visible ? 'visible' : 'hidden' }); + // TODO check if this assertion is correct, fix it + walk((axis.categoriesTree || []), 'categories', (group) => { + const tick = group.tick; + if (!tick) { + return false; + } + if ((axis.min && tick.startAt + tick.leaves - 1 < axis.min) || (axis.max && tick.startAt > axis.max)) { + tick.label?.hide(); + tick.destroyed = 0; + } + else { + tick.label?.attr({ visibility: visible ? 'visible' : 'hidden' }); + } + return true; + }); + return true; +}; +axisProto.setCategories = function (newCategories, doRedraw) { + const axis = this; + if (axis.categories) { + axis.cleanGroups(); + } + axis.setupGroups({ categories: newCategories }); + axis.categories = axis.userOptions.categories = newCategories; + if (axis.categories.every((cat) => isString(cat))) { + protoAxisSetCategories.call(axis, axis.categories, doRedraw); + } +}; +axisProto.cleanGroups = function () { + const axis = this; + const ticks = axis.ticks; + for (const n in ticks) { + if (ticks[n].parent) { + delete (ticks[n]).parent; + } + } + // TODO check it here, fix it + walk((axis.categoriesTree || []), 'categories', (group) => { + const tick = group.tick; + if (!tick) { + return false; + } + tick.label?.destroy(); + objectEach(tick, (_v, i) => delete tick[i]); + delete group.tick; + return true; + }); + axis.labelsGrid = null; +}; +axisProto.groupSize = function (level, position) { + const axis = this; + const positions = (axis.labelsSizes || []); + const direction = (axis.directionFactor || 1); + const groupedOptions = axis.options.labels && axis.options.labels.groupedOptions && isNumber(level) ? + axis.options.labels.groupedOptions[level - 1] : false; + let userXY = 0; + if (groupedOptions) { + if (direction === -1) { + userXY = groupedOptions.x || 0; + } + else { + userXY = groupedOptions.y || 0; + } + } + if (isNumber(level) && position !== undefined) { + positions[level] = Math.max(positions[level] || 0, position + 10 + Math.abs(userXY)); + } + if (level === true) { + return sum(positions) * direction; + } + else if (isNumber(level) && positions[level]) { + return positions[level] * direction; + } + return 0; +}; +// Tick prototype extensions +tickProto.addLabel = function () { + const tick = this; + const axis = tick.axis; + const labelOptions = pick(tick.options && tick.options.labels, axis.options.labels); + let category; + protoTickAddLabel.call(tick); + if (!axis.categories || !(category = axis.categories[tick.pos])) { + return false; + } + if (tick.label) { + const formatter = function (ctx) { + if (labelOptions.formatter) { + return labelOptions.formatter.call(ctx, ctx); + } + if (labelOptions.format) { + ctx.text = axis.defaultLabelFormatter.call(ctx); + return format(labelOptions.format, ctx, axis.chart); + } + return axis.defaultLabelFormatter.call(ctx); + }; + tick.label.attr('text', formatter({ + axis: axis, // TODO: fix it + tick: tick, // TODO: fix it + chart: axis.chart, + isFirst: !!tick.isFirst, + isLast: !!tick.isLast, + value: isObject(category) ? category.name : category, + pos: tick.pos + })); + tick.label.textPxLength = tick.label.getBBox().width; + } + if (axis.isGrouped && axis.options.labels.enabled && !isString(category)) { + tick.addGroupedLabels(category); + } + return true; +}; +tickProto.addGroupedLabels = function (category) { + const tick = this; + const axis = tick.axis; + const chart = axis.chart; + const options = axis.options.labels; + const useHTML = options.useHTML; + const css = options.style; + const userAttr = options.groupedOptions; + const attr = { align: 'center', rotation: options.rotation, x: 0, y: 0, style: undefined }; + const sizeKey = axis.horiz ? 'height' : 'width'; + let depth = 0; + let label; + let currentTick = tick; + let currentCategory = category; + while (currentTick) { + if (currentCategory && depth > 0 && !currentCategory.tick) { + tick.value = currentCategory.name; + const ctx = { + chart, + axis: axis, // TODO: fix it + tick: tick, // TODO: fix it + isFirst: !!tick.isFirst, + isLast: !!tick.isLast, + value: currentCategory.name, + pos: tick.pos + }; + const name = options.formatter ? options.formatter.call(ctx, ctx) : currentCategory.name; + const hasOptions = userAttr && userAttr[depth - 1]; + const mergedAttrs = hasOptions ? merge(attr, userAttr[depth - 1]) : attr; + const mergedCSS = hasOptions && userAttr[depth - 1].style ? merge(css, userAttr[depth - 1].style) : css; + delete mergedAttrs.style; + label = chart.renderer.text(name, 0, 0, useHTML).attr(mergedAttrs).add(axis.labelGroup); + if (label && !chart.styledMode) { + label.css(mergedCSS); + } + currentTick.startAt = tick.pos; + currentTick.childCount = (currentCategory.categories || []).length; + currentTick.leaves = currentCategory.leaves; + currentTick.visible = !!currentTick.childCount; + currentTick.label = label; + currentTick.labelOffsets = { x: mergedAttrs.x, y: mergedAttrs.y }; + currentCategory.tick = currentTick; + } + if (currentTick && currentTick.label) { + axis.groupSize(depth, (currentTick.label.getBBox())[sizeKey]); + } + currentCategory = currentCategory?.parent; + if (currentCategory) { + currentTick = currentTick.parent = currentCategory.tick || {}; + } + else { + currentTick = undefined; + } + depth++; + } +}; +tickProto.render = function (index, old, opacity) { + protoTickRender.call(this, index, old, opacity); + const tick = this; + const axis = tick.axis; + const treeCat = axis.categories && axis.categories[tick.pos]; + if (!axis.isGrouped || !treeCat || (axis.max && tick.pos > axis.max)) { + return; + } + const tickPos = tick.pos; + const isFirst = tick.isFirst; + const max = axis.max; + const min = axis.min; + const horiz = axis.horiz; + const grid = axis.labelsGridPath; + const tickWidth = axis.tickWidth || 0; + const xy = tickPosition(tick, tickPos); + const start = horiz ? xy.y : xy.x; + const baseLine = fontMetrics(axis.options.labels.style.fontSize ? axis.options.labels.style.fontSize : 0, axis.chart).b; + let group = tick; + let size = axis.groupSize(0); + let depth = 1; + let reverseCrisp = ((horiz && xy.x === axis.pos + axis.len) || (!horiz && xy.y === axis.pos)) ? -1 : 0; + let gridAttrs; + let lvlSize; + let minPos; + let maxPos; + let attrs; + let bBox; + if (isFirst) { + gridAttrs = horiz ? + [axis.left, xy.y, axis.left, xy.y + axis.groupSize(true)] : + axis.isXAxis ? + [xy.x, axis.top, xy.x + axis.groupSize(true), axis.top] : + [xy.x, axis.top + axis.len, xy.x + axis.groupSize(true), axis.top + axis.len]; + addGridPart(grid, gridAttrs, tickWidth); + } + if (horiz && axis.left < xy.x) { + addGridPart(grid, [xy.x - reverseCrisp, xy.y, xy.x - reverseCrisp, xy.y + size], tickWidth); + } + else if (!horiz && axis.top <= xy.y) { + addGridPart(grid, [xy.x, xy.y + reverseCrisp, xy.x + size, xy.y + reverseCrisp], tickWidth); + } + size = start + size; + function fixOffset(tCat) { + let ret = 0; + if (isFirst && !isString(tCat)) { + ret = (tCat.parent?.categories || []).indexOf(tCat.name); + ret = ret < 0 ? 0 : ret; + return ret; + } + return ret; + } + while (group.parent) { + group = group.parent; + const fix = fixOffset(treeCat); + const userX = group.labelOffsets?.x || 0; + const userY = group.labelOffsets?.y || 0; + minPos = tickPosition(tick, min ? Math.max(group.startAt - 1, min - 1) : group.startAt - 1); + maxPos = tickPosition(tick, max ? Math.min(group.startAt + group.leaves - 1 - fix, max) : group.startAt + group.leaves - 1 - fix); + bBox = group.label?.getBBox(true); + lvlSize = axis.groupSize(depth); + reverseCrisp = ((horiz && maxPos.x === axis.pos + axis.len) || (!horiz && maxPos.y === axis.pos)) ? -1 : 0; + attrs = horiz ? { + x: (minPos.x + maxPos.x) / 2 + userX, + y: (size) + ((axis).groupFontHeights?.[depth] || 0) + lvlSize / 2 + userY / 2 + } : { + x: (size) + lvlSize / 2 + userX, + y: (minPos.y + maxPos.y - (bBox?.height || 0)) / 2 + baseLine + userY + }; + if (!isNaN(attrs.x) && !isNaN(attrs.y)) { + group.label?.attr(attrs); + if (grid) { + if (horiz && axis.left < maxPos.x) { + addGridPart(grid, [maxPos.x - reverseCrisp, size, maxPos.x - reverseCrisp, size + lvlSize], tickWidth); + } + else if (!horiz && axis.top <= maxPos.y) { + addGridPart(grid, [size, maxPos.y + reverseCrisp, size + lvlSize, maxPos.y + reverseCrisp], tickWidth); + } + } + } + size = size + lvlSize; + depth++; + } +}; +tickProto.destroy = function () { + const tick = this; + let group = tick.parent; + while (group) { + group.destroyed = group.destroyed ? (group.destroyed + 1) : 1; + group = group.parent; + } + protoTickDestroy.call(tick); +}; +tickProto.getLabelSize = function () { + const tick = this; + const axis = tick.axis; + if (axis.isGrouped === true) { + const size = protoTickGetLabelSize.call(tick) + 10; + const topLabelSize = (axis.labelsSizes?.[0] || 0); + if (topLabelSize < size) { + if (!axis.labelsSizes) { + axis.labelsSizes = []; + } + axis.labelsSizes[0] = size; + } + return sum(axis.labelsSizes || []); + } + return protoTickGetLabelSize.call(tick); +}; +tickProto.replaceMovedLabel = function () { + const tick = this; + if (!tick.axis.isGrouped) { + protoTickReplaceMovedLabel.call(tick); + } +}; + +})); \ No newline at end of file diff --git a/grouped-categories.js b/grouped-categories-old.js similarity index 100% rename from grouped-categories.js rename to grouped-categories-old.js diff --git a/gulpfile.js b/gulpfile.js index c3ba9cd..4898333 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,10 +1,96 @@ -'use strict'; -var gulp = require('gulp'), - eslint = require('gulp-eslint'); +const gulp = require('gulp'), + eslint = require('gulp-eslint'), + sourcemaps = require('gulp-sourcemaps'), + closureCompiler = require('google-closure-compiler').gulp(), + babel = require('gulp-babel'), + gulpTypescript = require("gulp-typescript"), + through2 = require('through2'), + rename = require('gulp-rename'), + tsProject = gulpTypescript.createProject("tsconfig.json"); + +let decorator = [ + '/**', + '----', + '*', + '* (c) 2012-2025 Black Label', + '*', + '* License: MIT', + '*/', + '' + ]; + +gulp.task("compile", () => { + return tsProject + .src() + .pipe(tsProject()) + .js + .pipe(rename('grouped-categories.js')) + .pipe(through2.obj(async function (file, _encoding, callback) { + if (file.isBuffer()) { + let fileContent = file.contents.toString('utf8'); + const removedSpecifiers = [], + removedPaths = [], + importPathReg = /import (.+?) from ["'](.+?)["'];/g, + formattedPathReg = /^highcharts-github\/ts\//, + exportReg = /\bexport\s*{[^}]*};?/g, + utilsPathReg = /^.*Utilities.*$/m, + templatingPathReg = /^.*Templating.*$/m; + + fileContent = fileContent.replace(importPathReg, (_match, specifier, path) => { + removedSpecifiers.push(specifier); + removedPaths.push(`${path.replace(formattedPathReg, "")}.js`); + return ''; + }); + + fileContent = fileContent.replace( + utilsPathReg, + 'const { merge, pick, objectEach, isNumber, isObject, isString, pInt, format, Tick, Axis, SVGElement } = Highcharts;' + ); + fileContent = fileContent.replace(templatingPathReg, ''); + fileContent = fileContent.replace(exportReg, ''); + + const wrappedFileContent = decorator.join('\n') + +`(function (factory) { + if (typeof module === 'object' && module.exports) { + module.exports = factory; + } else { + factory(Highcharts); + } +}(function (Highcharts) { +${fileContent} +}));`; + file.contents = Buffer.from(wrappedFileContent, 'utf8'); + } + + this.push(file); + callback(); + })) + .pipe(gulp.dest('dist')) + .pipe(sourcemaps.init()) + .pipe(babel({ + presets: ['@babel/preset-env'], + overrides: [{ + presets: [["@babel/preset-env", { targets: "defaults" }]] + }] + })) + .pipe(closureCompiler({ + compilation_level: 'SIMPLE', + warning_level: 'DEFAULT', // VERBOSE + language_in: 'ECMASCRIPT_2020', + language_out: 'ECMASCRIPT_2020', + output_wrapper: '(function(){\n%output%\n}).call(this)', + js_output_file: 'grouped-categories.min.js', + externs: 'compileExterns.js' + })) + .pipe(sourcemaps.write('/')) + .pipe(gulp.dest('dist')) +}); gulp.task('lint', function () { - return gulp.src(['grouped-categories.js']) - .pipe(eslint()) - .pipe(eslint.failOnError()) - .pipe(eslint.formatEach()); -}); \ No newline at end of file + return gulp.src(['ts/**/*.ts', '!ts/**/*.d.ts', '!ts/**/*.test.ts'], { allowEmpty: true }) + .pipe(eslint()) + .pipe(eslint.format()) + .pipe(eslint.failAfterError()); +}); + +gulp.task('build', gulp.series('lint', 'compile')); diff --git a/index.html b/index.html index 6d75756..e74530a 100644 --- a/index.html +++ b/index.html @@ -9,8 +9,8 @@ Grouped Categories - Highcharts module - - + + @@ -47,7 +47,8 @@

Grouped Categories - Highcharts module

window.chart = new Highcharts.Chart({ chart: { renderTo: "chart", - type: "column" + type: "column", + styledMode: true }, series: [{ data: [4, 14, 18, 5, 6, 5, 14, 15, 18] diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..5c1f369 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,20 @@ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'jsdom', + roots: ['/ts'], + testMatch: ['**/*.test.ts'], + transform: { + '^.+\\.ts$': 'ts-jest', + }, + collectCoverageFrom: [ + 'ts/**/*.ts', + '!ts/**/*.test.ts', + '!ts/**/*.d.ts' + ], + coverageDirectory: 'coverage', + coverageReporters: ['text', 'lcov', 'html'], + setupFilesAfterEnv: ['/ts/test-setup.ts'], + moduleNameMapping: { + '^highcharts$': '/node_modules/highcharts/highcharts.js' + } +}; diff --git a/package.json b/package.json index e613c62..c7ffba5 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,18 @@ "name": "highcharts-grouped-categories", "version": "1.3.2", "description": "Highcharts plugin to add grouped categories to charts.", - "main": "grouped-categories.js", + "types": "ts/groupedCategories.d.ts", + "main": "./dist/grouped-categories.js", + "scripts": { + "build": "tsc", + "build:ts": "tsc", + "build:gulp": "npx gulp build", + "test": "jest", + "test:watch": "jest --watch", + "lint": "npx gulp lint", + "compile": "npx gulp compile", + "clean": "rm -rf ts/*.js ts/*.d.ts ts/*.map" + }, "repository": { "type": "git", "url": "git+https://github.com/blacklabel/grouped_categories.git" @@ -11,19 +22,45 @@ "highcharts", "grouped", "categories", - "highcharts-addon" + "highcharts-addon", + "typescript" ], - "author": "Black Label (https://blacklabel.pl/highcharts/)", + "author": "Black Label (https://blacklabel.net)", "license": "SEE LICENSE IN license.txt", "bugs": { "url": "https://github.com/blacklabel/grouped_categories/issues" }, "homepage": "https://github.com/blacklabel/grouped_categories#readme", "files": [ - "grouped-categories.js" + "grouped-categories.js", + "ts/groupedCategories.js", + "ts/groupedCategories.d.ts" ], "devDependencies": { - "gulp": "^4.0.2", - "gulp-eslint": "^3.0.0" + "@babel/core": "^7.25.2", + "@babel/preset-env": "^7.25.4", + "@types/jest": "^29.5.0", + "@types/node": "^20.0.0", + "@types/trusted-types": "^2.0.2", + "@typescript-eslint/eslint-plugin": "^6.11.0", + "@typescript-eslint/parser": "^6.11.0", + "eslint": "^8.0.1", + "google-closure-compiler": "^20240317.0.0", + "gulp": "^5.0.0", + "gulp-babel": "^8.0.0", + "gulp-bump": "^3.2.0", + "gulp-eslint": "^6.0.0", + "gulp-git": "^2.11.0", + "gulp-rename": "^2.0.0", + "gulp-replace": "^1.1.4", + "gulp-sourcemaps": "3.0.0", + "gulp-typescript": "^6.0.0-alpha.1", + "highcharts-github": "github:highcharts/highcharts", + "jest": "^29.5.0", + "jest-environment-jsdom": "^29.5.0", + "through2": "^4.0.2", + "ts-jest": "^29.1.1", + "ts-node": "^10.9.1", + "typescript": "^5.2.2" } } diff --git a/test-setup.ts b/test-setup.ts new file mode 100644 index 0000000..65cfc3d --- /dev/null +++ b/test-setup.ts @@ -0,0 +1,24 @@ +// Test setup file for Jest + +// Mock Highcharts if not available in test environment +if (typeof window === 'undefined') { + (global as any).window = {} as any; +} + +// Mock document if not available +if (typeof document === 'undefined') { + (global as any).document = { + createElement: jest.fn(), + body: { + appendChild: jest.fn(), + removeChild: jest.fn() + } + } as any; +} + +// Mock console methods to reduce noise in tests +(global as any).console = { + ...console, + warn: jest.fn(), + error: jest.fn(), +}; diff --git a/ts/HighchartsConfig.d.ts b/ts/HighchartsConfig.d.ts new file mode 100644 index 0000000..007b4fb --- /dev/null +++ b/ts/HighchartsConfig.d.ts @@ -0,0 +1,148 @@ +import 'highcharts-github/ts/masters/highcharts.src'; +import 'highcharts-github/ts/masters/highstock.src'; +import 'highcharts-github/ts/masters/highmaps.src'; +import 'highcharts-github/ts/masters/highcharts-3d.src'; +import 'highcharts-github/ts/masters/highcharts-gantt.src'; +import 'highcharts-github/ts/masters/highcharts-more.src'; + +import 'highcharts-github/ts/masters/modules/accessibility.src'; +import 'highcharts-github/ts/masters/modules/annotations-advanced.src'; +import 'highcharts-github/ts/masters/modules/annotations.src'; +import 'highcharts-github/ts/masters/modules/arc-diagram.src'; +import 'highcharts-github/ts/masters/modules/arrow-symbols.src'; +import 'highcharts-github/ts/masters/modules/boost-canvas.src'; +import 'highcharts-github/ts/masters/modules/boost.src'; +import 'highcharts-github/ts/masters/modules/broken-axis.src'; +import 'highcharts-github/ts/masters/modules/bullet.src'; +import 'highcharts-github/ts/masters/modules/coloraxis.src'; +import 'highcharts-github/ts/masters/modules/current-date-indicator.src'; +import 'highcharts-github/ts/masters/modules/cylinder.src'; +import 'highcharts-github/ts/masters/modules/data-tools.src'; +import 'highcharts-github/ts/masters/modules/data.src'; +import 'highcharts-github/ts/masters/modules/datagrouping.src'; +import 'highcharts-github/ts/masters/modules/debugger.src'; +import 'highcharts-github/ts/masters/modules/dependency-wheel.src'; +import 'highcharts-github/ts/masters/modules/dotplot.src'; +import 'highcharts-github/ts/masters/modules/drag-panes.src'; +import 'highcharts-github/ts/masters/modules/draggable-points.src'; +import 'highcharts-github/ts/masters/modules/drilldown.src'; +import 'highcharts-github/ts/masters/modules/dumbbell.src'; +import 'highcharts-github/ts/masters/modules/export-data.src'; +import 'highcharts-github/ts/masters/modules/exporting.src'; +import 'highcharts-github/ts/masters/modules/flowmap.src'; +import 'highcharts-github/ts/masters/modules/full-screen.src'; +import 'highcharts-github/ts/masters/modules/funnel.src'; +import 'highcharts-github/ts/masters/modules/funnel3d.src'; +import 'highcharts-github/ts/masters/modules/gantt.src'; +import 'highcharts-github/ts/masters/modules/geoheatmap.src'; +import 'highcharts-github/ts/masters/modules/grid-axis.src'; +import 'highcharts-github/ts/masters/modules/heatmap.src'; +import 'highcharts-github/ts/masters/modules/heikinashi.src'; +import 'highcharts-github/ts/masters/modules/histogram-bellcurve.src'; +import 'highcharts-github/ts/masters/modules/hollowcandlestick.src'; +import 'highcharts-github/ts/masters/modules/item-series.src'; +import 'highcharts-github/ts/masters/modules/lollipop.src'; +import 'highcharts-github/ts/masters/modules/map.src'; +import 'highcharts-github/ts/masters/modules/marker-clusters.src'; +import 'highcharts-github/ts/masters/modules/mouse-wheel-zoom.src'; +import 'highcharts-github/ts/masters/modules/networkgraph.src'; +import 'highcharts-github/ts/masters/modules/no-data-to-display.src'; + +// eslint-disable-next-line max-len +import 'highcharts-github/ts/Extensions/OfflineExporting/OfflineExportingVendor'; + +import 'highcharts-github/ts/masters/modules/offline-exporting.src'; +import 'highcharts-github/ts/masters/modules/organization.src'; +import 'highcharts-github/ts/masters/modules/overlapping-datalabels.src'; +import 'highcharts-github/ts/masters/modules/parallel-coordinates.src'; +import 'highcharts-github/ts/masters/modules/pareto.src'; +import 'highcharts-github/ts/masters/modules/pathfinder.src'; +import 'highcharts-github/ts/masters/modules/pattern-fill.src'; +import 'highcharts-github/ts/masters/modules/pictorial.src'; +import 'highcharts-github/ts/masters/modules/price-indicator.src'; +import 'highcharts-github/ts/masters/modules/pyramid3d.src'; +import 'highcharts-github/ts/masters/modules/sankey.src'; +import 'highcharts-github/ts/masters/modules/series-label.src'; +import 'highcharts-github/ts/masters/modules/series-on-point.src'; +import 'highcharts-github/ts/masters/modules/solid-gauge.src'; +import 'highcharts-github/ts/masters/modules/sonification.src'; +import 'highcharts-github/ts/masters/modules/static-scale.src'; +import 'highcharts-github/ts/masters/modules/stock-tools.src'; +import 'highcharts-github/ts/masters/modules/stock.src'; +import 'highcharts-github/ts/masters/modules/streamgraph.src'; +import 'highcharts-github/ts/masters/modules/sunburst.src'; +import 'highcharts-github/ts/masters/modules/tiledwebmap.src'; +import 'highcharts-github/ts/masters/modules/tilemap.src'; +import 'highcharts-github/ts/masters/modules/timeline.src'; +import 'highcharts-github/ts/masters/modules/treegraph.src'; +import 'highcharts-github/ts/masters/modules/treegrid.src'; +import 'highcharts-github/ts/masters/modules/treemap.src'; +import 'highcharts-github/ts/masters/modules/variable-pie.src'; +import 'highcharts-github/ts/masters/modules/variwide.src'; +import 'highcharts-github/ts/masters/modules/vector.src'; +import 'highcharts-github/ts/masters/modules/venn.src'; +import 'highcharts-github/ts/masters/modules/windbarb.src'; +import 'highcharts-github/ts/masters/modules/wordcloud.src'; +import 'highcharts-github/ts/masters/modules/xrange.src'; + +import 'highcharts-github/ts/masters/indicators/acceleration-bands.src'; +import 'highcharts-github/ts/masters/indicators/accumulation-distribution.src'; +import 'highcharts-github/ts/masters/indicators/ao.src'; +import 'highcharts-github/ts/masters/indicators/apo.src'; +import 'highcharts-github/ts/masters/indicators/aroon-oscillator.src'; +import 'highcharts-github/ts/masters/indicators/aroon.src'; +import 'highcharts-github/ts/masters/indicators/atr.src'; +import 'highcharts-github/ts/masters/indicators/bollinger-bands.src'; +import 'highcharts-github/ts/masters/indicators/cci.src'; +import 'highcharts-github/ts/masters/indicators/chaikin.src'; +import 'highcharts-github/ts/masters/indicators/cmf.src'; +import 'highcharts-github/ts/masters/indicators/cmo.src'; +import 'highcharts-github/ts/masters/indicators/dema.src'; +import 'highcharts-github/ts/masters/indicators/disparity-index.src'; +import 'highcharts-github/ts/masters/indicators/dmi.src'; +import 'highcharts-github/ts/masters/indicators/dpo.src'; +import 'highcharts-github/ts/masters/indicators/ema.src'; +import 'highcharts-github/ts/masters/indicators/ichimoku-kinko-hyo.src'; +import 'highcharts-github/ts/masters/indicators/indicators-all.src'; +import 'highcharts-github/ts/masters/indicators/indicators.src'; +import 'highcharts-github/ts/masters/indicators/keltner-channels.src'; +import 'highcharts-github/ts/masters/indicators/klinger.src'; +import 'highcharts-github/ts/masters/indicators/macd.src'; +import 'highcharts-github/ts/masters/indicators/mfi.src'; +import 'highcharts-github/ts/masters/indicators/momentum.src'; +import 'highcharts-github/ts/masters/indicators/natr.src'; +import 'highcharts-github/ts/masters/indicators/obv.src'; +import 'highcharts-github/ts/masters/indicators/pivot-points.src'; +import 'highcharts-github/ts/masters/indicators/ppo.src'; +import 'highcharts-github/ts/masters/indicators/price-channel.src'; +import 'highcharts-github/ts/masters/indicators/price-envelopes.src'; +import 'highcharts-github/ts/masters/indicators/psar.src'; +import 'highcharts-github/ts/masters/indicators/regressions.src'; +import 'highcharts-github/ts/masters/indicators/roc.src'; +import 'highcharts-github/ts/masters/indicators/rsi.src'; +import 'highcharts-github/ts/masters/indicators/slow-stochastic.src'; +import 'highcharts-github/ts/masters/indicators/stochastic.src'; +import 'highcharts-github/ts/masters/indicators/supertrend.src'; +import 'highcharts-github/ts/masters/indicators/tema.src'; +import 'highcharts-github/ts/masters/indicators/trendline.src'; +import 'highcharts-github/ts/masters/indicators/trix.src'; +import 'highcharts-github/ts/masters/indicators/volume-by-price.src'; +import 'highcharts-github/ts/masters/indicators/vwap.src'; +import 'highcharts-github/ts/masters/indicators/williams-r.src'; +import 'highcharts-github/ts/masters/indicators/wma.src'; +import 'highcharts-github/ts/masters/indicators/zigzag.src'; + +import 'highcharts-github/ts/masters/themes/avocado.src'; +import 'highcharts-github/ts/masters/themes/brand-dark.src'; +import 'highcharts-github/ts/masters/themes/brand-light.src'; +import 'highcharts-github/ts/masters/themes/dark-blue.src'; +import 'highcharts-github/ts/masters/themes/dark-green.src'; +import 'highcharts-github/ts/masters/themes/dark-unica.src'; +import 'highcharts-github/ts/masters/themes/gray.src'; +import 'highcharts-github/ts/masters/themes/grid-light.src'; +import 'highcharts-github/ts/masters/themes/grid.src'; +import 'highcharts-github/ts/masters/themes/high-contrast-dark.src'; +import 'highcharts-github/ts/masters/themes/high-contrast-light.src'; +import 'highcharts-github/ts/masters/themes/sand-signika.src'; +import 'highcharts-github/ts/masters/themes/skies.src'; +import 'highcharts-github/ts/masters/themes/sunset.src'; diff --git a/ts/groupedCategories.ts b/ts/groupedCategories.ts new file mode 100644 index 0000000..79541ff --- /dev/null +++ b/ts/groupedCategories.ts @@ -0,0 +1,736 @@ +import Templating from 'highcharts-github/ts/Core/Templating'; +import Tick from 'highcharts-github/ts/Core/Axis/Tick'; +import Axis from 'highcharts-github/ts/Core/Axis/Axis'; +import Utilities from 'highcharts-github/ts/Core/Utilities'; +import SVGElement from 'highcharts-github/ts/Core/Renderer/SVG/SVGElement'; +import CSSObject from 'highcharts-github/ts/Core/Renderer/CSSObject'; + +import type PositionObject from 'highcharts-github/ts/Core/Renderer/PositionObject'; +import type BBoxObject from 'highcharts-github/ts/Core/Renderer/BBoxObject'; +import type Chart from 'highcharts-github/ts/Core/Chart/Chart'; +import type { + AxisCollectionKey, + AxisOptions, + AxisLabelOptions +} from 'highcharts-github/ts/Core/Axis/AxisOptions'; +import type Time from 'highcharts-github/ts/Core/Time'; +import type SVGPath from 'highcharts-github/ts/Core/Renderer/SVG/SVGPath'; + +interface GroupedLabelOptions extends AxisLabelOptions { + groupedOptions?: Array<{ + x?: number; + y?: number; + style?: CSSObject; + }>; + rotation?: number; +} + +// Type definitions for grouped categories +interface GroupedCategory { + name: string; + categories?: Array; + parent?: GroupedCategory; + leaves?: number; + // eslint-disable-next-line no-use-before-define + tick?: GroupedTick; +} + +// Custom interface that doesn't extend XAxisOptions to avoid conflicts +interface GroupedAxisOptions extends Omit { + categories: Array; + tickLength: number; + tickWidth: number; + lineWidth: number; + tickColor: string; + labels: GroupedLabelOptions; + drawHorizontalBorders?: boolean; // TODO: check if this is needed +} + +interface GroupedTick extends Omit { + value?: string; + childCount?: number; + labelOffsets?: { + x: number; + y: number; + }; + leaves?: number; + startAt?: number; + visible?: boolean; + destroyed?: number; + parent?: GroupedTick; + options?: GroupedAxisOptions; + // eslint-disable-next-line no-use-before-define + axis: GroupedAxis; + addGroupedLabels: (this: GroupedTick, category: GroupedCategory) => void; +} + +export interface AxisLabelFormatterContextObject { + axis: Axis; + chart: Chart; + dateTimeLabelFormat?: Time.DateTimeFormat; + isFirst: boolean; + isLast: boolean; + pos: number; + text?: string; + tick: Tick; + value: number|string; +} + +interface GroupedAxis extends Omit { + options: GroupedAxisOptions; + userOptions: GroupedAxisOptions; + categoriesTree?: Array; + categories?: Array; + isGrouped?: boolean; + labelsDepth?: number; + labelsSizes?: number[]; + labelsGridPath: Array; + labelsGrid?: SVGElement | null; + groupFontHeights?: number[]; + directionFactor?: number; + userTickLength?: boolean; + originalTickLength?: number; + tickLength?: number | null; + tickWidth?: number; + ticks: Record; + init: (chart: Chart, options: Partial, coll?: AxisCollectionKey) => void; + setupGroups: (options: Partial) => void; + groupSize: (level: number | boolean, position?: number) => number; + cleanGroups: () => void; +} + +const { merge, pick, objectEach, isNumber, isObject, isString, pInt } = Utilities; +const { format } = Templating; + +// Utility functions +function deepClone(obj: T): T { + return JSON.parse(JSON.stringify(obj)) as T; +} + +const sum = (arr: number[]): number => arr.reduce((acc, val): number => acc + val, 0); + +function walk( + arr: T[], + key: keyof T, + fn: (item: T) => boolean | void +): void { + for (let i = arr.length - 1; i >= 0; i--) { + const children = arr[i][key] as T[]; + if (children) { + walk(children, key, fn); + } + fn(arr[i]); + } +} + +// Category class +class Category { + public userOptions: GroupedCategory; + public name: string; + public parent?: GroupedCategory; + + constructor(obj: GroupedCategory | string, parent?: GroupedCategory) { + this.userOptions = deepClone(obj as GroupedCategory); + this.name = typeof obj === 'string' ? obj : (obj.name || ''); + this.parent = parent; + } + + toString(): string { + const parts: string[] = []; + let cat: GroupedCategory | undefined = this; + + while (cat) { + parts.push(cat.name); + cat = cat.parent; + } + + return parts.join(', '); + } +} + +// Add category leaf to array +function addLeaf( + out: GroupedCategory[], + cat: GroupedCategory | string, + parent?: GroupedCategory +): void { + out.unshift(new Category(cat, parent)); + + let currentParent = parent; + while (currentParent) { + currentParent.leaves = (currentParent.leaves || 0) + 1; + currentParent = currentParent.parent; + } +} + +// Builds reverse category tree +function buildTree( + cats: Array, + out: GroupedCategory[], + options: { depth: number }, + parent?: GroupedCategory, + depth = 0 +): void { + options.depth = options.depth || 0; + + for (let i = cats.length - 1; i >= 0; i--) { + const cat = cats[i]; + + if (typeof cat === 'object' && cat.categories) { + if (parent) { + cat.parent = parent; + } + buildTree(cat.categories, out, options, cat, depth + 1); + } else { + addLeaf(out, cat, parent); + } + } + options.depth = Math.max(options.depth, depth); +} + +// Pushes part of grid to path +function addGridPart(path: Array, d: number[], width: number): void { + // Based on crispLine from HC (#65) + if (d[0] === d[2]) { + d[0] = d[2] = Math.round(d[0]) - (width % 2 / 2); + } + if (d[1] === d[3]) { + d[1] = d[3] = Math.round(d[1]) + (width % 2 / 2); + } + + path.push( + 'M', + d[0], d[1], + 'L', + d[2], d[3] + ); +} + +// Returns tick position +function tickPosition(tick: GroupedTick, pos: number): PositionObject { + return tick.getPosition(tick.axis.horiz, pos, tick.axis.tickmarkOffset); +} + +// Create local function `fontMetrics` to provide compatibility with HC 11 (#200) +function fontMetrics(fontSize: string | number, chart?: Chart, elem?: SVGElement): { + h: number; + b: number; + f: number; +} { + let fontSizeNum: number | string | undefined; + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore: workaround for old IE, window.getComputedStyle always exists in modern browsers + if ((chart?.renderer.styledMode || (isString(fontSize) && fontSize.includes('px'))) && window.getComputedStyle) { + fontSizeNum = elem && SVGElement.prototype.getStyle.call(elem, 'font-size'); + } else { + fontSizeNum = fontSize || elem?.styles.fontSize || chart?.renderer.style.fontSize; + } + + if (isString(fontSizeNum) && fontSizeNum.includes('px')) { + fontSizeNum = pInt(fontSizeNum); + } else if (!isNumber(fontSizeNum) || isNaN(fontSizeNum)) { + fontSizeNum = 12; + } + + const lineHeight = (fontSizeNum < 24 ? fontSizeNum + 3 : Math.round(fontSizeNum * 1.2)); + const baseline = Math.round(lineHeight * 0.8); + + return { + h: lineHeight, + b: baseline, + f: fontSizeNum + }; +} + +// Main plugin implementation + +// Cache prototypes +const axisProto = Axis.prototype as GroupedAxis; +const tickProto = Tick.prototype as GroupedTick; + +// Cache original methods +const protoAxisInit = axisProto.init; +const protoAxisRender = axisProto.render; +const protoAxisSetCategories = axisProto.setCategories; +const protoTickGetLabelSize = tickProto.getLabelSize; +const protoTickAddLabel = tickProto.addLabel; +const protoTickDestroy = tickProto.destroy; +const protoTickRender = tickProto.render; +const protoTickReplaceMovedLabel = tickProto.replaceMovedLabel; + +// Axis prototype extensions +axisProto.init = function ( + this: GroupedAxis, + chart: Chart, + options: Partial, + coll?: AxisCollectionKey +): void { + protoAxisInit.call(this, chart, options, coll); + + if (isObject(options) && options.categories) { + this.setupGroups(options); + } +}; + +// Setup required axis options +axisProto.setupGroups = function (this: GroupedAxis, options: Partial): void { + const axis = this; + const chart = axis.chart; + const categories = deepClone(options.categories || []); + const reverseTree: GroupedCategory[] = []; + const stats: { depth: number } = { depth: 0 }; + const labelOptions = axis.options.labels; + const userAttr = labelOptions && labelOptions.groupedOptions; + const css = labelOptions && labelOptions.style; + + buildTree(categories, reverseTree, stats); + + axis.categoriesTree = categories; + axis.categories = reverseTree; + axis.isGrouped = stats.depth !== 0; + axis.labelsDepth = stats.depth; + axis.labelsSizes = []; + axis.labelsGridPath = []; + axis.tickLength = options.tickLength || axis.tickLength || null; + axis.tickWidth = pick(options.tickWidth, axis.isXAxis ? 1 : 0); + axis.directionFactor = [-1, 1, 1, -1][axis.side]; + axis.options.lineWidth = pick(options.lineWidth, 1); + axis.groupFontHeights = []; + + for (let i = 0; i <= stats.depth; i++) { + const hasOptions = userAttr && userAttr[i - 1]; + const mergedCSS = hasOptions && userAttr[i - 1].style ? merge(css, userAttr[i - 1].style) : css; + axis.groupFontHeights[i] = Math.round(fontMetrics(mergedCSS.fontSize ? mergedCSS.fontSize : 0, chart).b * 0.3); + } +}; + +axisProto.render = function (this: GroupedAxis): boolean | void { + const axis = this; + + if (axis.isGrouped) { + axis.labelsGridPath = []; + } + + if (axis.originalTickLength === undefined) { + axis.originalTickLength = axis.options.tickLength; + } + + axis.options.tickLength = axis.isGrouped ? 0.001 : axis.originalTickLength; + + protoAxisRender.call(axis); + + if (!axis.isGrouped) { + if (axis.labelsGrid) { + axis.labelsGrid.attr({ visibility: 'hidden' }); + } + return false; + } + + const options = axis.options; + const top = axis.top; + const left = axis.left; + const right = left + axis.width; + const bottom = top + axis.height; + const visible = axis.hasVisibleSeries || axis.hasData; + let depth = axis.labelsDepth || 0; + let grid = axis.labelsGrid; + const horiz = axis.horiz; + const d = axis.labelsGridPath; + let i = options.drawHorizontalBorders === false ? (depth + 1) : 0; // TODO: check if this is needed + let offset = axis.opposite ? (horiz ? top : right) : (horiz ? bottom : left); + const tickWidth = axis.tickWidth || 0; + let part: number[]; + + if (axis.userTickLength) { + depth -= 1; + } + + if (!grid) { + grid = axis.labelsGrid = axis.chart.renderer.path() + .attr({ + strokeWidth: tickWidth, + 'stroke-width': tickWidth, + stroke: options.tickColor || '' + }) + .add(axis.axisGroup); + + if (!options.tickColor) { + grid.addClass('highcharts-tick'); + } + } + + while (i <= depth) { + offset += axis.groupSize(i); + part = horiz ? [left, offset, right, offset] : [offset, top, offset, bottom]; + addGridPart(d, part, tickWidth); + i++; + } + + // TODO: fix it + grid.attr({ d: d as unknown as SVGPath, visibility: visible ? 'visible' : 'hidden' }); + axis.labelGroup?.attr({ visibility: visible ? 'visible' : 'hidden' }); + + // TODO check if this assertion is correct, fix it + walk((axis.categoriesTree || []) as GroupedCategory[], 'categories', (group: GroupedCategory): boolean => { + const tick = group.tick; + + if (!tick) { return false; } + + if ((axis.min && tick.startAt! + tick.leaves! - 1 < axis.min) || (axis.max && tick.startAt! > axis.max)) { + tick.label?.hide(); + tick.destroyed = 0; + } else { + tick.label?.attr({ visibility: visible ? 'visible' : 'hidden' }); + } + + return true; + }); + + return true; +}; + +axisProto.setCategories = function ( + this: GroupedAxis, + newCategories: Array, + doRedraw?: boolean +): void { + const axis = this; + + if (axis.categories) { + axis.cleanGroups(); + } + + axis.setupGroups({ categories: newCategories }); + axis.categories = axis.userOptions.categories = newCategories; + + if (axis.categories.every((cat): boolean => isString(cat))) { + protoAxisSetCategories.call(axis, axis.categories as string[], doRedraw); + } +}; + +axisProto.cleanGroups = function (): void { + const axis = this; + const ticks = axis.ticks; + + for (const n in ticks) { + if (ticks[n].parent) { + delete (ticks[n]).parent; + } + } + + // TODO check it here, fix it + walk((axis.categoriesTree || []) as GroupedCategory[], 'categories', (group: GroupedCategory): boolean => { + const tick = group.tick; + + if (!tick) { return false; } + + tick.label?.destroy(); + objectEach(tick, (_v: GroupedTick[keyof GroupedTick], i: keyof GroupedTick): boolean => delete tick[i]); + + delete group.tick; + return true; + }); + + axis.labelsGrid = null; +}; + +axisProto.groupSize = function (this: GroupedAxis, level: number | boolean, position?: number): number { + const axis = this; + const positions = (axis.labelsSizes || []); + const direction = (axis.directionFactor || 1); + const groupedOptions = axis.options.labels && axis.options.labels.groupedOptions && isNumber(level) ? + axis.options.labels.groupedOptions[level - 1] : false; + let userXY = 0; + + if (groupedOptions) { + if (direction === -1) { + userXY = groupedOptions.x || 0; + } else { + userXY = groupedOptions.y || 0; + } + } + + if (isNumber(level) && position !== undefined) { + positions[level] = Math.max(positions[level] || 0, position + 10 + Math.abs(userXY)); + } + + if (level === true) { + return sum(positions) * direction; + } else if (isNumber(level) && positions[level]) { + return positions[level] * direction; + } + + return 0; +}; + +// Tick prototype extensions +tickProto.addLabel = function (this: GroupedTick): boolean { + const tick = this; + const axis = tick.axis; + const labelOptions = pick(tick.options && tick.options.labels, axis.options.labels); + + let category: string | GroupedCategory; + + protoTickAddLabel.call(tick); + + if (!axis.categories || !(category = axis.categories[tick.pos])) { + return false; + } + + if (tick.label) { + const formatter = function (ctx: AxisLabelFormatterContextObject): string { + if (labelOptions.formatter) { + return labelOptions.formatter.call(ctx, ctx); + } + + if (labelOptions.format) { + ctx.text = axis.defaultLabelFormatter.call(ctx); + return format(labelOptions.format, ctx, axis.chart); + } + + return axis.defaultLabelFormatter.call(ctx); + }; + + tick.label.attr('text', formatter({ + axis: axis as Axis, // TODO: fix it + tick: tick as Tick, // TODO: fix it + chart: axis.chart, + isFirst: !!tick.isFirst, + isLast: !!tick.isLast, + value: isObject(category) ? category.name : category, + pos: tick.pos + })); + + tick.label.textPxLength = tick.label.getBBox().width; + } + + if (axis.isGrouped && axis.options.labels.enabled && !isString(category)) { + tick.addGroupedLabels(category); + } + + return true; +}; + +tickProto.addGroupedLabels = function (this: GroupedTick, category: GroupedCategory): void { + const tick = this; + const axis = tick.axis; + const chart = axis.chart; + const options = axis.options.labels; + const useHTML = options.useHTML; + const css = options.style; + const userAttr = options.groupedOptions; + const attr = { align: 'center' as const, rotation: options.rotation, x: 0, y: 0, style: undefined }; + const sizeKey = axis.horiz ? 'height' : 'width'; + + let depth = 0; + let label: SVGElement; + let currentTick: GroupedTick | undefined = tick; + let currentCategory: GroupedCategory | undefined = category; + + while (currentTick) { + if (currentCategory && depth > 0 && !currentCategory.tick) { + tick.value = currentCategory.name; + const ctx = { + chart, + axis: axis as Axis, // TODO: fix it + tick: tick as Tick, // TODO: fix it + isFirst: !!tick.isFirst, + isLast: !!tick.isLast, + value: currentCategory.name, + pos: tick.pos + }; + const name = options.formatter ? options.formatter.call(ctx, ctx) : currentCategory.name; + const hasOptions = userAttr && userAttr[depth - 1]; + const mergedAttrs = hasOptions ? merge(attr, userAttr[depth - 1]) : attr; + const mergedCSS = hasOptions && userAttr[depth - 1].style ? merge(css, userAttr[depth - 1].style) : css; + + delete mergedAttrs.style; + + label = chart.renderer.text(name, 0, 0, useHTML).attr(mergedAttrs).add(axis.labelGroup); + + if (label && !chart.styledMode) { + label.css(mergedCSS); + } + + currentTick.startAt = tick.pos; + currentTick.childCount = (currentCategory.categories || []).length; + currentTick.leaves = currentCategory.leaves; + currentTick.visible = !!currentTick.childCount; + currentTick.label = label; + currentTick.labelOffsets = { x: mergedAttrs.x, y: mergedAttrs.y }; + + currentCategory.tick = currentTick; + } + + if (currentTick && currentTick.label) { + axis.groupSize(depth, (currentTick.label.getBBox())[sizeKey]); + } + + currentCategory = currentCategory?.parent; + + if (currentCategory) { + currentTick = currentTick.parent = currentCategory.tick || {} as GroupedTick; + } else { + currentTick = undefined; + } + + depth++; + } +}; + +tickProto.render = function (index: number, old?: boolean, opacity?: number): void { + protoTickRender.call(this, index, old, opacity); + + const tick = this; + const axis = tick.axis; + const treeCat = axis.categories && axis.categories[tick.pos]; + + if (!axis.isGrouped || !treeCat || (axis.max && tick.pos > axis.max)) { + return; + } + + const tickPos = tick.pos; + const isFirst = tick.isFirst; + const max = axis.max; + const min = axis.min; + const horiz = axis.horiz; + const grid = axis.labelsGridPath; + const tickWidth = axis.tickWidth || 0; + const xy = tickPosition(tick, tickPos); + const start = horiz ? xy.y : xy.x; + const baseLine = fontMetrics( + axis.options.labels.style.fontSize ? axis.options.labels.style.fontSize : 0, axis.chart + ).b; + + let group = tick; + let size = axis.groupSize(0); + let depth = 1; + let reverseCrisp = ((horiz && xy.x === axis.pos + axis.len) || (!horiz && xy.y === axis.pos)) ? -1 : 0; + let gridAttrs: number[]; + let lvlSize: number; + let minPos: PositionObject; + let maxPos: PositionObject; + let attrs: { x: number; y: number }; + let bBox: BBoxObject | undefined; + + if (isFirst) { + gridAttrs = horiz ? + [axis.left, xy.y, axis.left, xy.y + axis.groupSize(true)] : + axis.isXAxis ? + [xy.x, axis.top, xy.x + axis.groupSize(true), axis.top] : + [xy.x, axis.top + axis.len, xy.x + axis.groupSize(true), axis.top + axis.len]; + + addGridPart(grid, gridAttrs, tickWidth); + } + + if (horiz && axis.left < xy.x) { + addGridPart(grid, [xy.x - reverseCrisp, xy.y, xy.x - reverseCrisp, xy.y + size], tickWidth); + } else if (!horiz && axis.top <= xy.y) { + addGridPart(grid, [xy.x, xy.y + reverseCrisp, xy.x + size, xy.y + reverseCrisp], tickWidth); + } + + size = start + size; + + function fixOffset(tCat: string | GroupedCategory): number { + let ret = 0; + + if (isFirst && !isString(tCat)) { + ret = (tCat.parent?.categories || []).indexOf(tCat.name); + ret = ret < 0 ? 0 : ret; + return ret; + } + + return ret; + } + + while (group.parent) { + group = group.parent; + + const fix = fixOffset(treeCat); + const userX = group.labelOffsets?.x || 0; + const userY = group.labelOffsets?.y || 0; + + minPos = tickPosition(tick, min ? Math.max(group.startAt! - 1, min - 1) : group.startAt! - 1); + maxPos = tickPosition( + tick, + max ? Math.min(group.startAt! + group.leaves! - 1 - fix, max) : group.startAt! + group.leaves! - 1 - fix + ); + bBox = group.label?.getBBox(true); + lvlSize = axis.groupSize(depth); + reverseCrisp = ((horiz && maxPos.x === axis.pos + axis.len) || (!horiz && maxPos.y === axis.pos)) ? -1 : 0; + + attrs = horiz ? { + x: (minPos.x + maxPos.x) / 2 + userX, + y: (size) + ((axis).groupFontHeights?.[depth] || 0) + lvlSize / 2 + userY / 2 + } : { + x: (size) + lvlSize / 2 + userX, + y: (minPos.y + maxPos.y - (bBox?.height || 0)) / 2 + baseLine + userY + }; + + if (!isNaN(attrs.x) && !isNaN(attrs.y)) { + group.label?.attr(attrs); + + if (grid) { + if (horiz && axis.left < maxPos.x) { + addGridPart( + grid, + [maxPos.x - reverseCrisp, size, maxPos.x - reverseCrisp, size + lvlSize], + tickWidth + ); + } else if (!horiz && axis.top <= maxPos.y) { + addGridPart( + grid, + [size, maxPos.y + reverseCrisp, size + lvlSize, maxPos.y + reverseCrisp], + tickWidth + ); + } + } + } + + size = size + lvlSize; + depth++; + } +}; + +tickProto.destroy = function (): void { + const tick = this; + let group = tick.parent; + + while (group) { + group.destroyed = group.destroyed ? (group.destroyed + 1) : 1; + group = group.parent; + } + + protoTickDestroy.call(tick); +}; + +tickProto.getLabelSize = function (): number { + const tick = this; + const axis = tick.axis; + + if (axis.isGrouped === true) { + const size = protoTickGetLabelSize.call(tick) + 10; + const topLabelSize = (axis.labelsSizes?.[0] || 0); + + if (topLabelSize < size) { + if (!axis.labelsSizes) { + axis.labelsSizes = []; + } + + axis.labelsSizes[0] = size; + } + + return sum(axis.labelsSizes || []); + } + + return protoTickGetLabelSize.call(tick); +}; + +tickProto.replaceMovedLabel = function (): void { + const tick = this; + + if (!tick.axis.isGrouped) { + protoTickReplaceMovedLabel.call(tick); + } +}; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..78b5be3 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ES2020", + "moduleResolution": "node", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "outDir": "./ts", + "rootDir": "./ts", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "resolveJsonModule": true, + "allowSyntheticDefaultImports": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true + }, + "include": [ + "ts/**/*" + ], + "exclude": [ + "node_modules", + "**/*.test.ts" + ] +} From 0b1bfaa1618beaff1add4c03109590f897ae0efe Mon Sep 17 00:00:00 2001 From: wchmiel Date: Fri, 19 Sep 2025 07:21:41 +0200 Subject: [PATCH 02/16] Moved types to separate file --- jest.config.js | 5 +-- package.json | 1 + ts/groupedCategories.ts | 98 ++++------------------------------------ types.d.ts | 99 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 109 insertions(+), 94 deletions(-) create mode 100644 types.d.ts diff --git a/jest.config.js b/jest.config.js index 5c1f369..a4d5383 100644 --- a/jest.config.js +++ b/jest.config.js @@ -13,8 +13,5 @@ module.exports = { ], coverageDirectory: 'coverage', coverageReporters: ['text', 'lcov', 'html'], - setupFilesAfterEnv: ['/ts/test-setup.ts'], - moduleNameMapping: { - '^highcharts$': '/node_modules/highcharts/highcharts.js' - } + setupFilesAfterEnv: ['/test-setup.ts'], }; diff --git a/package.json b/package.json index c7ffba5..5caadc0 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,7 @@ "gulp-replace": "^1.1.4", "gulp-sourcemaps": "3.0.0", "gulp-typescript": "^6.0.0-alpha.1", + "highcharts": "^11.0.0", "highcharts-github": "github:highcharts/highcharts", "jest": "^29.5.0", "jest-environment-jsdom": "^29.5.0", diff --git a/ts/groupedCategories.ts b/ts/groupedCategories.ts index 79541ff..5008e20 100644 --- a/ts/groupedCategories.ts +++ b/ts/groupedCategories.ts @@ -3,101 +3,19 @@ import Tick from 'highcharts-github/ts/Core/Axis/Tick'; import Axis from 'highcharts-github/ts/Core/Axis/Axis'; import Utilities from 'highcharts-github/ts/Core/Utilities'; import SVGElement from 'highcharts-github/ts/Core/Renderer/SVG/SVGElement'; -import CSSObject from 'highcharts-github/ts/Core/Renderer/CSSObject'; import type PositionObject from 'highcharts-github/ts/Core/Renderer/PositionObject'; import type BBoxObject from 'highcharts-github/ts/Core/Renderer/BBoxObject'; import type Chart from 'highcharts-github/ts/Core/Chart/Chart'; -import type { - AxisCollectionKey, - AxisOptions, - AxisLabelOptions -} from 'highcharts-github/ts/Core/Axis/AxisOptions'; -import type Time from 'highcharts-github/ts/Core/Time'; +import type { AxisCollectionKey } from 'highcharts-github/ts/Core/Axis/AxisOptions'; import type SVGPath from 'highcharts-github/ts/Core/Renderer/SVG/SVGPath'; - -interface GroupedLabelOptions extends AxisLabelOptions { - groupedOptions?: Array<{ - x?: number; - y?: number; - style?: CSSObject; - }>; - rotation?: number; -} - -// Type definitions for grouped categories -interface GroupedCategory { - name: string; - categories?: Array; - parent?: GroupedCategory; - leaves?: number; - // eslint-disable-next-line no-use-before-define - tick?: GroupedTick; -} - -// Custom interface that doesn't extend XAxisOptions to avoid conflicts -interface GroupedAxisOptions extends Omit { - categories: Array; - tickLength: number; - tickWidth: number; - lineWidth: number; - tickColor: string; - labels: GroupedLabelOptions; - drawHorizontalBorders?: boolean; // TODO: check if this is needed -} - -interface GroupedTick extends Omit { - value?: string; - childCount?: number; - labelOffsets?: { - x: number; - y: number; - }; - leaves?: number; - startAt?: number; - visible?: boolean; - destroyed?: number; - parent?: GroupedTick; - options?: GroupedAxisOptions; - // eslint-disable-next-line no-use-before-define - axis: GroupedAxis; - addGroupedLabels: (this: GroupedTick, category: GroupedCategory) => void; -} - -export interface AxisLabelFormatterContextObject { - axis: Axis; - chart: Chart; - dateTimeLabelFormat?: Time.DateTimeFormat; - isFirst: boolean; - isLast: boolean; - pos: number; - text?: string; - tick: Tick; - value: number|string; -} - -interface GroupedAxis extends Omit { - options: GroupedAxisOptions; - userOptions: GroupedAxisOptions; - categoriesTree?: Array; - categories?: Array; - isGrouped?: boolean; - labelsDepth?: number; - labelsSizes?: number[]; - labelsGridPath: Array; - labelsGrid?: SVGElement | null; - groupFontHeights?: number[]; - directionFactor?: number; - userTickLength?: boolean; - originalTickLength?: number; - tickLength?: number | null; - tickWidth?: number; - ticks: Record; - init: (chart: Chart, options: Partial, coll?: AxisCollectionKey) => void; - setupGroups: (options: Partial) => void; - groupSize: (level: number | boolean, position?: number) => number; - cleanGroups: () => void; -} +import type { + GroupedCategory, + GroupedTick, + GroupedAxis, + GroupedAxisOptions, + AxisLabelFormatterContextObject +} from './../types'; const { merge, pick, objectEach, isNumber, isObject, isString, pInt } = Utilities; const { format } = Templating; diff --git a/types.d.ts b/types.d.ts new file mode 100644 index 0000000..9e76707 --- /dev/null +++ b/types.d.ts @@ -0,0 +1,99 @@ +import type Chart from 'highcharts-github/ts/Core/Chart/Chart'; +import type CSSObject from 'highcharts-github/ts/Core/Renderer/CSSObject'; +import type { + AxisCollectionKey, + AxisOptions, + AxisLabelOptions +} from 'highcharts-github/ts/Core/Axis/AxisOptions'; +import type Time from 'highcharts-github/ts/Core/Time'; +import type Tick from 'highcharts-github/ts/Core/Axis/Tick'; +import type Axis from 'highcharts-github/ts/Core/Axis/Axis'; +import type SVGElement from 'highcharts-github/ts/Core/Renderer/SVG/SVGElement'; + +interface GroupedLabelOptions extends AxisLabelOptions { + groupedOptions?: Array<{ + x?: number; + y?: number; + style?: CSSObject; + }>; + rotation?: number; +} + +interface GroupedCategory { + name: string; + categories?: Array; + parent?: GroupedCategory; + leaves?: number; + tick?: GroupedTick; +} + +interface GroupedAxisOptions extends Omit { + categories: Array; + tickLength: number; + tickWidth: number; + lineWidth: number; + tickColor: string; + labels: GroupedLabelOptions; + drawHorizontalBorders?: boolean; // TODO: check if this is needed +} + +interface GroupedTick extends Omit { + value?: string; + childCount?: number; + labelOffsets?: { + x: number; + y: number; + }; + leaves?: number; + startAt?: number; + visible?: boolean; + destroyed?: number; + parent?: GroupedTick; + options?: GroupedAxisOptions; + axis: GroupedAxis; + addGroupedLabels: (this: GroupedTick, category: GroupedCategory) => void; +} + +interface AxisLabelFormatterContextObject { + axis: Axis; + chart: Chart; + dateTimeLabelFormat?: Time.DateTimeFormat; + isFirst: boolean; + isLast: boolean; + pos: number; + text?: string; + tick: Tick; + value: number|string; +} + +interface GroupedAxis extends Omit { + options: GroupedAxisOptions; + userOptions: GroupedAxisOptions; + categoriesTree?: Array; + categories?: Array; + isGrouped?: boolean; + labelsDepth?: number; + labelsSizes?: number[]; + labelsGridPath: Array; + labelsGrid?: SVGElement | null; + groupFontHeights?: number[]; + directionFactor?: number; + userTickLength?: boolean; + originalTickLength?: number; + tickLength?: number | null; + tickWidth?: number; + ticks: Record; + init: (chart: Chart, options: Partial, coll?: AxisCollectionKey) => void; + setupGroups: (options: Partial) => void; + groupSize: (level: number | boolean, position?: number) => number; + cleanGroups: () => void; +} + +export { + GroupedLabelOptions, + GroupedCategory, + GroupedAxisOptions, + GroupedTick, + AxisLabelFormatterContextObject, + GroupedAxis +}; From e74eef91b8cbbed54b2dbaea5d4f3b8b0e08f7ae Mon Sep 17 00:00:00 2001 From: wchmiel Date: Fri, 19 Sep 2025 09:12:02 +0200 Subject: [PATCH 03/16] Changed utils functions into arrow fn --- dist/grouped-categories.js | 29 +++++++++++++---------------- index.html | 2 +- ts/groupedCategories.ts | 38 +++++++++++++++++--------------------- 3 files changed, 31 insertions(+), 38 deletions(-) diff --git a/dist/grouped-categories.js b/dist/grouped-categories.js index c0eba3a..a1c2b7e 100644 --- a/dist/grouped-categories.js +++ b/dist/grouped-categories.js @@ -12,7 +12,6 @@ factory(Highcharts); } }(function (Highcharts) { -// Import Highcharts from 'highcharts-github/ts/Core/Globals'; @@ -21,11 +20,9 @@ const { merge, pick, objectEach, isNumber, isObject, isString, pInt, format, Tick, Axis, SVGElement } = Highcharts; // Utility functions -function deepClone(obj) { - return JSON.parse(JSON.stringify(obj)); -} +const deepClone = (obj) => JSON.parse(JSON.stringify(obj)); const sum = (arr) => arr.reduce((acc, val) => acc + val, 0); -function walk(arr, key, fn) { +const walk = (arr, key, fn) => { for (let i = arr.length - 1; i >= 0; i--) { const children = arr[i][key]; if (children) { @@ -33,7 +30,7 @@ function walk(arr, key, fn) { } fn(arr[i]); } -} +}; // Category class class Category { constructor(obj, parent) { @@ -52,16 +49,16 @@ class Category { } } // Add category leaf to array -function addLeaf(out, cat, parent) { +const addLeaf = (out, cat, parent) => { out.unshift(new Category(cat, parent)); let currentParent = parent; while (currentParent) { currentParent.leaves = (currentParent.leaves || 0) + 1; currentParent = currentParent.parent; } -} +}; // Builds reverse category tree -function buildTree(cats, out, options, parent, depth = 0) { +const buildTree = (cats, out, options, parent, depth = 0) => { options.depth = options.depth || 0; for (let i = cats.length - 1; i >= 0; i--) { const cat = cats[i]; @@ -76,9 +73,9 @@ function buildTree(cats, out, options, parent, depth = 0) { } } options.depth = Math.max(options.depth, depth); -} +}; // Pushes part of grid to path -function addGridPart(path, d, width) { +const addGridPart = (path, d, width) => { // Based on crispLine from HC (#65) if (d[0] === d[2]) { d[0] = d[2] = Math.round(d[0]) - (width % 2 / 2); @@ -87,13 +84,13 @@ function addGridPart(path, d, width) { d[1] = d[3] = Math.round(d[1]) + (width % 2 / 2); } path.push('M', d[0], d[1], 'L', d[2], d[3]); -} +}; // Returns tick position -function tickPosition(tick, pos) { +const tickPosition = (tick, pos) => { return tick.getPosition(tick.axis.horiz, pos, tick.axis.tickmarkOffset); -} +}; // Create local function `fontMetrics` to provide compatibility with HC 11 (#200) -function fontMetrics(fontSize, chart, elem) { +const fontMetrics = (fontSize, chart, elem) => { let fontSizeNum; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore: workaround for old IE, window.getComputedStyle always exists in modern browsers @@ -116,7 +113,7 @@ function fontMetrics(fontSize, chart, elem) { b: baseline, f: fontSizeNum }; -} +}; // Main plugin implementation // Cache prototypes const axisProto = Axis.prototype; diff --git a/index.html b/index.html index e74530a..b0bdb9d 100644 --- a/index.html +++ b/index.html @@ -9,7 +9,7 @@ Grouped Categories - Highcharts module - + diff --git a/ts/groupedCategories.ts b/ts/groupedCategories.ts index 5008e20..7cbce7b 100644 --- a/ts/groupedCategories.ts +++ b/ts/groupedCategories.ts @@ -21,17 +21,13 @@ const { merge, pick, objectEach, isNumber, isObject, isString, pInt } = Utilitie const { format } = Templating; // Utility functions -function deepClone(obj: T): T { - return JSON.parse(JSON.stringify(obj)) as T; -} - +const deepClone = (obj: T): T => JSON.parse(JSON.stringify(obj)) as T; const sum = (arr: number[]): number => arr.reduce((acc, val): number => acc + val, 0); - -function walk( +const walk = ( arr: T[], key: keyof T, fn: (item: T) => boolean | void -): void { +): void => { for (let i = arr.length - 1; i >= 0; i--) { const children = arr[i][key] as T[]; if (children) { @@ -39,7 +35,7 @@ function walk( } fn(arr[i]); } -} +}; // Category class class Category { @@ -67,11 +63,11 @@ class Category { } // Add category leaf to array -function addLeaf( +const addLeaf = ( out: GroupedCategory[], cat: GroupedCategory | string, parent?: GroupedCategory -): void { +): void => { out.unshift(new Category(cat, parent)); let currentParent = parent; @@ -79,16 +75,16 @@ function addLeaf( currentParent.leaves = (currentParent.leaves || 0) + 1; currentParent = currentParent.parent; } -} +}; // Builds reverse category tree -function buildTree( +const buildTree = ( cats: Array, out: GroupedCategory[], options: { depth: number }, parent?: GroupedCategory, depth = 0 -): void { +): void => { options.depth = options.depth || 0; for (let i = cats.length - 1; i >= 0; i--) { @@ -104,10 +100,10 @@ function buildTree( } } options.depth = Math.max(options.depth, depth); -} +}; // Pushes part of grid to path -function addGridPart(path: Array, d: number[], width: number): void { +const addGridPart = (path: Array, d: number[], width: number): void => { // Based on crispLine from HC (#65) if (d[0] === d[2]) { d[0] = d[2] = Math.round(d[0]) - (width % 2 / 2); @@ -122,19 +118,19 @@ function addGridPart(path: Array, d: number[], width: number): 'L', d[2], d[3] ); -} +}; // Returns tick position -function tickPosition(tick: GroupedTick, pos: number): PositionObject { +const tickPosition = (tick: GroupedTick, pos: number): PositionObject => { return tick.getPosition(tick.axis.horiz, pos, tick.axis.tickmarkOffset); -} +}; // Create local function `fontMetrics` to provide compatibility with HC 11 (#200) -function fontMetrics(fontSize: string | number, chart?: Chart, elem?: SVGElement): { +const fontMetrics = (fontSize: string | number, chart?: Chart, elem?: SVGElement): { h: number; b: number; f: number; -} { +} => { let fontSizeNum: number | string | undefined; // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -159,7 +155,7 @@ function fontMetrics(fontSize: string | number, chart?: Chart, elem?: SVGElement b: baseline, f: fontSizeNum }; -} +}; // Main plugin implementation From 8303251cf903156d06a96bfe08414ef3f5540f15 Mon Sep 17 00:00:00 2001 From: wchmiel Date: Fri, 19 Sep 2025 09:55:19 +0200 Subject: [PATCH 04/16] Added last gh fixes --- dist/grouped-categories.js | 11 +++++++++++ index.html | 2 +- ts/groupedCategories.ts | 12 ++++++++++++ types.d.ts | 1 + 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/dist/grouped-categories.js b/dist/grouped-categories.js index a1c2b7e..acb20d0 100644 --- a/dist/grouped-categories.js +++ b/dist/grouped-categories.js @@ -297,6 +297,8 @@ tickProto.addLabel = function () { const axis = tick.axis; const labelOptions = pick(tick.options && tick.options.labels, axis.options.labels); let category; + // Initialize topLabelSize on the axis + axis.topLabelSize = 0; protoTickAddLabel.call(tick); if (!axis.categories || !(category = axis.categories[tick.pos])) { return false; @@ -487,6 +489,7 @@ tickProto.getLabelSize = function () { if (!axis.labelsSizes) { axis.labelsSizes = []; } + axis.topLabelSize = axis.labelsSizes[0]; axis.labelsSizes[0] = size; } return sum(axis.labelsSizes || []); @@ -498,6 +501,14 @@ tickProto.replaceMovedLabel = function () { if (!tick.axis.isGrouped) { protoTickReplaceMovedLabel.call(tick); } + else { + // Get rid of unnecessary duplicated label, #222 + const movedLabel = this.movedLabel; + if (movedLabel) { + movedLabel.destroy(); + delete this.movedLabel; + } + } }; })); \ No newline at end of file diff --git a/index.html b/index.html index b0bdb9d..e74530a 100644 --- a/index.html +++ b/index.html @@ -9,7 +9,7 @@ Grouped Categories - Highcharts module - + diff --git a/ts/groupedCategories.ts b/ts/groupedCategories.ts index 7cbce7b..c3ed55f 100644 --- a/ts/groupedCategories.ts +++ b/ts/groupedCategories.ts @@ -386,6 +386,9 @@ tickProto.addLabel = function (this: GroupedTick): boolean { let category: string | GroupedCategory; + // Initialize topLabelSize on the axis + axis.topLabelSize = 0; + protoTickAddLabel.call(tick); if (!axis.categories || !(category = axis.categories[tick.pos])) { @@ -632,6 +635,7 @@ tickProto.getLabelSize = function (): number { axis.labelsSizes = []; } + axis.topLabelSize = axis.labelsSizes[0]; axis.labelsSizes[0] = size; } @@ -646,5 +650,13 @@ tickProto.replaceMovedLabel = function (): void { if (!tick.axis.isGrouped) { protoTickReplaceMovedLabel.call(tick); + } else { + // Get rid of unnecessary duplicated label, #222 + const movedLabel = this.movedLabel; + + if (movedLabel) { + movedLabel.destroy(); + delete this.movedLabel; + } } }; diff --git a/types.d.ts b/types.d.ts index 9e76707..603426b 100644 --- a/types.d.ts +++ b/types.d.ts @@ -82,6 +82,7 @@ interface GroupedAxis extends Omit; init: (chart: Chart, options: Partial, coll?: AxisCollectionKey) => void; setupGroups: (options: Partial) => void; From ad5a75e065774ffe5ab64f2189cd9d01ca80dd79 Mon Sep 17 00:00:00 2001 From: wchmiel Date: Wed, 24 Sep 2025 06:53:21 +0200 Subject: [PATCH 05/16] Fixed #228, Not grouped axis should not be affected by the lib --- CHANGELOG.md | 40 ++++++++++++++++++++++ bug1.html | 68 ++++++++++++++++++++++++++++++++++++++ dist/grouped-categories.js | 39 +++++++++++++--------- index.html | 32 ++++++++++++++++-- package.json | 2 +- ts/groupedCategories.ts | 44 ++++++++++++++---------- 6 files changed, 190 insertions(+), 35 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 bug1.html diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..409cb3f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,40 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +## [Unreleased] + +### Added +- TypeScript rewrite of the plugin in `ts/groupedCategories.ts`, aligned to Highcharts APIs. +- Demo page `demo.html` showcasing basic, nested, and styled grouped categories. +- Build pipeline with Gulp + TypeScript + Closure Compiler outputting to `dist/`. +- Source maps for minified build. +- ESLint configuration with TypeScript rules and stable parser settings. +- tsconfig with ES2020 targets and declaration output. +- NPM scripts: `build`, `build:gulp`, `lint`, `compile`, `clean`, `test`, `test:watch`. +- README overhaul with usage, development, and testing instructions. + +### Changed +- Path rendering logic to match legacy plugin behavior (using 'M'/'L' commands). +- Grid path buffer type to accept string/number for correct SVG path serialization. +- `groupSize` calculations to mirror legacy spacing/offset behavior. +- Font metrics handling for compatibility across HC versions. +- Relaxed type boundaries at Highcharts internal interop points to avoid false negatives. +- Gulp lint task to correctly complete and ignore declaration/test files. +- Closure Compiler flow to avoid duplicate sourcemap key collisions by splitting stages. +- Babel configuration to preserve ES2020 when desired. + +### Fixed +- #227 +- #228 + +### Notes / Migration +- Use `npm run build` for TypeScript build only, `npm run build:gulp` for full pipeline. +- Tests require Highcharts installed (dev dep recommended): `npm i -D highcharts`. +- To install Highcharts from GitHub, prefer `--ignore-scripts` or use the published package. +- Recommended editor settings: ensure ESLint extension uses the workspace config. + +--- + +## [1.3.2] - 2025-09-23 +- Legacy JS plugin version; current release supersedes it with TS rewrite and modern tooling. diff --git a/bug1.html b/bug1.html new file mode 100644 index 0000000..f655af0 --- /dev/null +++ b/bug1.html @@ -0,0 +1,68 @@ + + + + + + + +Grouped Categories - Highcharts module + + + + + + +
+ +
+ +
+ + + + + + diff --git a/dist/grouped-categories.js b/dist/grouped-categories.js index acb20d0..c1e2682 100644 --- a/dist/grouped-categories.js +++ b/dist/grouped-categories.js @@ -145,21 +145,24 @@ axisProto.setupGroups = function (options) { const userAttr = labelOptions && labelOptions.groupedOptions; const css = labelOptions && labelOptions.style; buildTree(categories, reverseTree, stats); - axis.categoriesTree = categories; - axis.categories = reverseTree; axis.isGrouped = stats.depth !== 0; - axis.labelsDepth = stats.depth; - axis.labelsSizes = []; - axis.labelsGridPath = []; - axis.tickLength = options.tickLength || axis.tickLength || null; - axis.tickWidth = pick(options.tickWidth, axis.isXAxis ? 1 : 0); - axis.directionFactor = [-1, 1, 1, -1][axis.side]; - axis.options.lineWidth = pick(options.lineWidth, 1); - axis.groupFontHeights = []; - for (let i = 0; i <= stats.depth; i++) { - const hasOptions = userAttr && userAttr[i - 1]; - const mergedCSS = hasOptions && userAttr[i - 1].style ? merge(css, userAttr[i - 1].style) : css; - axis.groupFontHeights[i] = Math.round(fontMetrics(mergedCSS.fontSize ? mergedCSS.fontSize : 0, chart).b * 0.3); + if (axis.isGrouped) { + axis.categoriesTree = categories; + axis.categories = reverseTree; + axis.labelsDepth = stats.depth; + axis.labelsSizes = []; + axis.labelsGridPath = []; + axis.tickLength = options.tickLength || axis.tickLength || null; + axis.tickWidth = pick(options.tickWidth, axis.isXAxis ? 1 : 0); + axis.directionFactor = [-1, 1, 1, -1][axis.side]; + axis.options.lineWidth = pick(options.lineWidth, 1); + axis.groupFontHeights = []; + for (let i = 0; i <= stats.depth; i++) { + const hasOptions = userAttr && userAttr[i - 1]; + const mergedCSS = hasOptions && userAttr[i - 1].style ? merge(css, userAttr[i - 1].style) : css; + axis.groupFontHeights[i] = + Math.round(fontMetrics(mergedCSS.fontSize ? mergedCSS.fontSize : 0, chart).b * 0.3); // TODO why * 0.3? + } } }; axisProto.render = function () { @@ -281,6 +284,7 @@ axisProto.groupSize = function (level, position) { } } if (isNumber(level) && position !== undefined) { + // TODO - Why + 10? Should be like this for sure? Try use label.distance here positions[level] = Math.max(positions[level] || 0, position + 10 + Math.abs(userXY)); } if (level === true) { @@ -300,6 +304,10 @@ tickProto.addLabel = function () { // Initialize topLabelSize on the axis axis.topLabelSize = 0; protoTickAddLabel.call(tick); + // Not grouped axis should not be affected, #228 + if (!axis.isGrouped) { + return false; + } if (!axis.categories || !(category = axis.categories[tick.pos])) { return false; } @@ -448,6 +456,7 @@ tickProto.render = function (index, old, opacity) { bBox = group.label?.getBBox(true); lvlSize = axis.groupSize(depth); reverseCrisp = ((horiz && maxPos.x === axis.pos + axis.len) || (!horiz && maxPos.y === axis.pos)) ? -1 : 0; + // TODO - check y position calculation attrs = horiz ? { x: (minPos.x + maxPos.x) / 2 + userX, y: (size) + ((axis).groupFontHeights?.[depth] || 0) + lvlSize / 2 + userY / 2 @@ -483,7 +492,7 @@ tickProto.getLabelSize = function () { const tick = this; const axis = tick.axis; if (axis.isGrouped === true) { - const size = protoTickGetLabelSize.call(tick) + 10; + const size = protoTickGetLabelSize.call(tick) + 10; // TODO - Why + 10, label.distance here? const topLabelSize = (axis.labelsSizes?.[0] || 0); if (topLabelSize < size) { if (!axis.labelsSizes) { diff --git a/index.html b/index.html index e74530a..52b8d77 100644 --- a/index.html +++ b/index.html @@ -9,7 +9,7 @@ Grouped Categories - Highcharts module - + @@ -41,6 +41,7 @@

Grouped Categories - Highcharts module

Go to project page to see this module in action: https://blacklabel.github.io/grouped_categories/

+
+ +

Requirements

@@ -177,7 +194,18 @@

Usage and demos

series: [{ data: [19, 6, 2, 1, 9, 4, 15, 2, 9, 11, 16, 18] }], - xAxis: { + xAxis: { + labels: { + groupedOptions: [{ + style: { + color: 'red' // set red font for labels in 1st-Level + } + }, { + rotation: -45, // rotate labels for a 2nd-level + align: 'right' + }], + rotation: 0 // 0-level options aren't changed, use them as always + }, categories: [{ name: "America", categories: [{ diff --git a/package.json b/package.json index 5caadc0..f4194f6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "highcharts-grouped-categories", - "version": "1.3.2", + "version": "2.0.0", "description": "Highcharts plugin to add grouped categories to charts.", "types": "ts/groupedCategories.d.ts", "main": "./dist/grouped-categories.js", diff --git a/ts/groupedCategories.ts b/ts/groupedCategories.ts index c3ed55f..6425b04 100644 --- a/ts/groupedCategories.ts +++ b/ts/groupedCategories.ts @@ -199,23 +199,26 @@ axisProto.setupGroups = function (this: GroupedAxis, options: Partial Date: Wed, 24 Sep 2025 13:00:47 +0200 Subject: [PATCH 06/16] Fixed #206, axis labels distance option should be taken into account --- bug2.html | 54 ++++++++++++++++++++++++++++++ dist/grouped-categories.js | 25 +++++++------- dist/grouped-categories.min.js | 19 +++++++++++ dist/grouped-categories.min.js.map | 1 + gulpfile.js | 9 +++++ package.json | 1 + ts/groupedCategories.ts | 27 ++++++++------- 7 files changed, 111 insertions(+), 25 deletions(-) create mode 100644 bug2.html create mode 100644 dist/grouped-categories.min.js create mode 100644 dist/grouped-categories.min.js.map diff --git a/bug2.html b/bug2.html new file mode 100644 index 0000000..987d292 --- /dev/null +++ b/bug2.html @@ -0,0 +1,54 @@ + + + + + + + +Grouped Categories - Highcharts module + + + + + + +
+ +
+ +
+ + + + + + diff --git a/dist/grouped-categories.js b/dist/grouped-categories.js index c1e2682..5269f00 100644 --- a/dist/grouped-categories.js +++ b/dist/grouped-categories.js @@ -491,19 +491,20 @@ tickProto.destroy = function () { tickProto.getLabelSize = function () { const tick = this; const axis = tick.axis; - if (axis.isGrouped === true) { - const size = protoTickGetLabelSize.call(tick) + 10; // TODO - Why + 10, label.distance here? - const topLabelSize = (axis.labelsSizes?.[0] || 0); - if (topLabelSize < size) { - if (!axis.labelsSizes) { - axis.labelsSizes = []; - } - axis.topLabelSize = axis.labelsSizes[0]; - axis.labelsSizes[0] = size; - } - return sum(axis.labelsSizes || []); + if (!axis.isGrouped) { + return protoTickGetLabelSize.call(tick); + } + // Axis labels distance option should be taken into account, #206 + // The default for a.reduce((b,c)=>b+c,0),B=(a,b,c)=>{for(let e=a.length-1;0<=e;e--){const g=a[e][b];g&&B(g,b,c);c(a[e])}};class P{constructor(a,b){this.userOptions=JSON.parse(JSON.stringify(a));this.name="string"===typeof a?a:a.name||"";this.parent=b}toString(){const a=[];let b=this;for(;b;)a.push(b.name), +b=b.parent;return a.join(", ")}}const E=(a,b,c,e,g=0)=>{c.depth=c.depth||0;for(let k=a.length-1;0<=k;k--){const p=a[k];if("object"===typeof p&&p.categories)e&&(p.parent=e),E(p.categories,b,c,p,g+1);else{var d=e;for(b.unshift(new P(p,d));d;)d.leaves=(d.leaves||0)+1,d=d.parent}}c.depth=Math.max(c.depth,g)},w=(a,b,c)=>{b[0]===b[2]&&(b[0]=b[2]=Math.round(b[0])-c%2/2);b[1]===b[3]&&(b[1]=b[3]=Math.round(b[1])+c%2/2);a.push("M",b[0],b[1],"L",b[2],b[3])},F=(a,b,c)=>{a=(b?.renderer.styledMode||x(a)&&a.includes("px"))&& +window.getComputedStyle?c&&O.prototype.getStyle.call(c,"font-size"):a||c?.styles.fontSize||b?.renderer.style.fontSize;if(x(a)&&a.includes("px"))a=K(a);else if(!y(a)||isNaN(a))a=12;b=24>a?a+3:Math.round(1.2*a);return{h:b,b:Math.round(.8*b),f:a}};r=N.prototype;const v=M.prototype,Q=r.init,R=r.render,S=r.setCategories,G=v.getLabelSize,T=v.addLabel,U=v.destroy,V=v.render,W=v.replaceMovedLabel;r.init=function(a,b,c){Q.call(this,a,b,c);C(b)&&b.categories&&this.setupGroups(b)};r.setupGroups=function(a){const b= +this.chart;var c=JSON.parse(JSON.stringify(a.categories||[]));const e=[],g={depth:0};var d=this.options.labels;const k=d&&d.groupedOptions;d=d&&d.style;E(c,e,g);if(this.isGrouped=0!==g.depth)for(this.categoriesTree=c,this.categories=e,this.labelsDepth=g.depth,this.labelsSizes=[],this.labelsGridPath=[],this.tickLength=a.tickLength||this.tickLength||null,this.tickWidth=A(a.tickWidth,this.isXAxis?1:0),this.directionFactor=[-1,1,1,-1][this.side],this.options.lineWidth=A(a.lineWidth,1),this.groupFontHeights= +[],a=0;a<=g.depth;a++)c=k&&k[a-1]&&k[a-1].style?z(d,k[a-1].style):d,this.groupFontHeights[a]=Math.round(.3*F(c.fontSize?c.fontSize:0,b).b)};r.render=function(){const a=this;a.isGrouped&&(a.labelsGridPath=[]);void 0===a.originalTickLength&&(a.originalTickLength=a.options.tickLength);a.options.tickLength=a.isGrouped?.001:a.originalTickLength;R.call(a);if(!a.isGrouped)return a.labelsGrid&&a.labelsGrid.attr({visibility:"hidden"}),!1;var b=a.options;const c=a.top,e=a.left,g=e+a.width,d=c+a.height,k=a.hasVisibleSeries|| +a.hasData;let p=a.labelsDepth||0,f=a.labelsGrid;const n=a.horiz,q=a.labelsGridPath;let l=!1===b.drawHorizontalBorders?p+1:0,m=a.opposite?n?c:g:n?d:e;const t=a.tickWidth||0;a.userTickLength&&--p;f||(f=a.labelsGrid=a.chart.renderer.path().attr({strokeWidth:t,"stroke-width":t,stroke:b.tickColor||""}).add(a.axisGroup),b.tickColor||f.addClass("highcharts-tick"));for(;l<=p;)m+=a.groupSize(l),b=n?[e,m,g,m]:[m,c,m,d],w(q,b,t),l++;f.attr({d:q,visibility:k?"visible":"hidden"});a.labelGroup?.attr({visibility:k? +"visible":"hidden"});B(a.categoriesTree||[],"categories",h=>{h=h.tick;if(!h)return!1;a.min&&h.startAt+h.leaves-1a.max?(h.label?.hide(),h.destroyed=0):h.label?.attr({visibility:k?"visible":"hidden"});return!0});return!0};r.setCategories=function(a,b){this.categories&&this.cleanGroups();this.setupGroups({categories:a});this.categories=this.userOptions.categories=a;this.categories.every(c=>x(c))&&S.call(this,this.categories,b)};r.cleanGroups=function(){const a=this.ticks;for(const b in a)a[b].parent&& +delete a[b].parent;B(this.categoriesTree||[],"categories",b=>{const c=b.tick;if(!c)return!1;c.label?.destroy();J(c,(e,g)=>delete c[g]);delete b.tick;return!0});this.labelsGrid=null};r.groupSize=function(a,b){const c=this.labelsSizes||[],e=this.directionFactor||1,g=this.options.labels&&this.options.labels.groupedOptions&&y(a)?this.options.labels.groupedOptions[a-1]:!1;let d=0;g&&(d=-1===e?g.x||0:g.y||0);y(a)&&void 0!==b&&(c[a]=Math.max(c[a]||0,b+10+Math.abs(d)));return!0===a?D(c)*e:y(a)&&c[a]?c[a]* +e:0};v.addLabel=function(){const a=this.axis;var b=A(this.options&&this.options.labels,a.options.labels);let c;a.topLabelSize=0;T.call(this);if(!a.isGrouped||!a.categories||!(c=a.categories[this.pos]))return!1;if(this.label){var e=this.label,g=e.attr;var d={axis:a,tick:this,chart:a.chart,isFirst:!!this.isFirst,isLast:!!this.isLast,value:C(c)?c.name:c,pos:this.pos};b.formatter?b=b.formatter.call(d,d):b.format?(d.text=a.defaultLabelFormatter.call(d),b=L(b.format,d,a.chart)):b=a.defaultLabelFormatter.call(d); +g.call(e,"text",b);this.label.textPxLength=this.label.getBBox().width}a.isGrouped&&a.options.labels.enabled&&!x(c)&&this.addGroupedLabels(c);return!0};v.addGroupedLabels=function(a){const b=this.axis,c=b.chart,e=b.options.labels,g=e.useHTML,d=e.style,k=e.groupedOptions,p={align:"center",rotation:e.rotation,x:0,y:0,style:void 0},f=b.horiz?"height":"width";let n=0;let q=this;for(;q;){if(a&&0a.max)){c=this.isFirst;var e=a.max,g=a.min,d=a.horiz,k=a.labelsGridPath,p=a.tickWidth||0,f=this.getPosition(this.axis.horiz,this.pos,this.axis.tickmarkOffset),n=d?f.y:f.x,q=F(a.options.labels.style.fontSize?a.options.labels.style.fontSize:0,a.chart).b,l=this,m=a.groupSize(0),t=1,h=d&&f.x===a.pos+a.len||!d&&f.y===a.pos?-1:0;if(c){var u=d?[a.left,f.y,a.left,f.y+a.groupSize(!0)]:a.isXAxis?[f.x,a.top,f.x+a.groupSize(!0), +a.top]:[f.x,a.top+a.len,f.x+a.groupSize(!0),a.top+a.len];w(k,u,p)}d&&a.lefth?0:h);const H=l.labelOffsets?.x||0,I=l.labelOffsets?.y||0;n=this.getPosition(this.axis.horiz,g?Math.max(l.startAt-1,g-1):l.startAt-1,this.axis.tickmarkOffset);u=this.getPosition(this.axis.horiz,e?Math.min(l.startAt+l.leaves-1-h,e):l.startAt+l.leaves- +1-h,this.axis.tickmarkOffset);var X=l.label?.getBBox(!0);f=a.groupSize(t);h=d&&u.x===a.pos+a.len||!d&&u.y===a.pos?-1:0;n=d?{x:(n.x+u.x)/2+H,y:m+(a.groupFontHeights?.[t]||0)+f/2+I/2}:{x:m+f/2+H,y:(n.y+u.y-(X?.height||0))/2+q+I};isNaN(n.x)||isNaN(n.y)||(l.label?.attr(n),k&&(d&&a.left Date: Thu, 25 Sep 2025 09:13:21 +0200 Subject: [PATCH 07/16] Small fix --- CHANGELOG.md | 2 ++ bug2.html | 43 ++++++++++++++++++++++++++++-- dist/grouped-categories.js | 3 +-- dist/grouped-categories.min.js | 2 +- dist/grouped-categories.min.js.map | 2 +- index.html | 7 ++--- ts/groupedCategories.ts | 3 +-- 7 files changed, 51 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 409cb3f..072889b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,8 +25,10 @@ All notable changes to this project will be documented in this file. - Babel configuration to preserve ES2020 when desired. ### Fixed +- #181 - #227 - #228 +- #206 ### Notes / Migration - Use `npm run build` for TypeScript build only, `npm run build:gulp` for full pipeline. diff --git a/bug2.html b/bug2.html index 987d292..8cde68b 100644 --- a/bug2.html +++ b/bug2.html @@ -32,8 +32,22 @@ data: [4, 14, 18, 5, 6, 5, 14, 15, 18] }], xAxis: { + xlabels: { + groupedOptions: [{ + style: { + color: 'red' // set red font for labels in 1st-Level + }, + rotation: -45, + }, { + style: { + color: 'green' // set red font for labels in 1st-Level + }, + rotation: 45, + }], + rotation: -45 // 0-level options aren't changed, use them as always + }, xcategories: ["Apple", "Banana", "Orange", "Carrot", "Potato", "Tomato", "Cod", "Salmon", "Tuna"], - categories: [{ + xcategories: [{ name: "Fruit", categories: ["Apple", "Banana", "Orange"] }, { @@ -43,7 +57,32 @@ name: "Fish", categories: ["Cod", "Salmon", "Tuna"] }], - xlabels: { + categories: [{ + name: "America", + categories: [{ + name: "USA", + categories: ["New York", "San Francisco"] + }, { + name: "Canada", + categories: ["Toronto", "Vancouver"] + }, { + name: "Mexico", + categories: ["Acapulco", "Leon"] + }] + }, { + name: "Europe", + categories: [{ + name: "United Kingdom", + categories: ["London", "Liverpool"] + }, { + name: "France", + categories: ["Paris", "Marseille"] + }, { + name: "Germany", + categories: ["Berlin", "Munich"] + }] + }], + labels: { distance: 5 } } diff --git a/dist/grouped-categories.js b/dist/grouped-categories.js index 5269f00..4c08ecd 100644 --- a/dist/grouped-categories.js +++ b/dist/grouped-categories.js @@ -502,8 +502,7 @@ tickProto.getLabelSize = function () { axis.labelsSizes = []; } axis.topLabelSize = axis.labelsSizes[0]; - axis.labelsSizes[0] = size; - axis.labelsSizes[1] = size; + axis.labelsSizes = axis.labelsSizes.map(() => size); return sum(axis.labelsSizes || []) - distance; }; tickProto.replaceMovedLabel = function () { diff --git a/dist/grouped-categories.min.js b/dist/grouped-categories.min.js index fd18d75..745bc7c 100644 --- a/dist/grouped-categories.min.js +++ b/dist/grouped-categories.min.js @@ -13,7 +13,7 @@ pos:this.pos};var m=e.formatter?e.formatter.call(l,l):a.name;var t=k&&k[n-1];l=t a,b,c);a=this.axis;b=a.categories&&a.categories[this.pos];if(a.isGrouped&&b&&!(a.max&&this.pos>a.max)){c=this.isFirst;var e=a.max,g=a.min,d=a.horiz,k=a.labelsGridPath,p=a.tickWidth||0,f=this.getPosition(this.axis.horiz,this.pos,this.axis.tickmarkOffset),n=d?f.y:f.x,q=F(a.options.labels.style.fontSize?a.options.labels.style.fontSize:0,a.chart).b,l=this,m=a.groupSize(0),t=1,h=d&&f.x===a.pos+a.len||!d&&f.y===a.pos?-1:0;if(c){var u=d?[a.left,f.y,a.left,f.y+a.groupSize(!0)]:a.isXAxis?[f.x,a.top,f.x+a.groupSize(!0), a.top]:[f.x,a.top+a.len,f.x+a.groupSize(!0),a.top+a.len];w(k,u,p)}d&&a.lefth?0:h);const H=l.labelOffsets?.x||0,I=l.labelOffsets?.y||0;n=this.getPosition(this.axis.horiz,g?Math.max(l.startAt-1,g-1):l.startAt-1,this.axis.tickmarkOffset);u=this.getPosition(this.axis.horiz,e?Math.min(l.startAt+l.leaves-1-h,e):l.startAt+l.leaves- 1-h,this.axis.tickmarkOffset);var X=l.label?.getBBox(!0);f=a.groupSize(t);h=d&&u.x===a.pos+a.len||!d&&u.y===a.pos?-1:0;n=d?{x:(n.x+u.x)/2+H,y:m+(a.groupFontHeights?.[t]||0)+f/2+I/2}:{x:m+f/2+H,y:(n.y+u.y-(X?.height||0))/2+q+I};isNaN(n.x)||isNaN(n.y)||(l.label?.attr(n),k&&(d&&a.leftc);return D(a.labelsSizes||[])-b};v.replaceMovedLabel=function(){if(this.axis.isGrouped){const a=this.movedLabel;a&&(a.destroy(),delete this.movedLabel)}else W.call(this)}}); }).call(this) //# sourceMappingURL=grouped-categories.min.js.map diff --git a/dist/grouped-categories.min.js.map b/dist/grouped-categories.min.js.map index d6c800b..4aeb105 100644 --- a/dist/grouped-categories.min.js.map +++ b/dist/grouped-categories.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["grouped-categories.js"],"names":["factory","module","exports","Highcharts","merge","pick","objectEach","isNumber","isObject","isString","pInt","format","Tick","Axis","SVGElement","sum","arr","reduce","acc","val","walk","key","fn","i","length","children","Category","constructor","obj","parent","userOptions","JSON","parse","stringify","name","toString","parts","cat","push","join","buildTree","cats","out","options","depth","categories","unshift","currentParent","leaves","Math","max","addGridPart","path","d","width","round","fontMetrics","fontSize","chart","elem","fontSizeNum","renderer","styledMode","includes","window","getComputedStyle","prototype","getStyle","call","styles","style","isNaN","lineHeight","h","b","f","axisProto","tickProto","protoAxisInit","init","protoAxisRender","render","protoAxisSetCategories","setCategories","protoTickGetLabelSize","getLabelSize","protoTickAddLabel","addLabel","protoTickDestroy","destroy","protoTickRender","protoTickReplaceMovedLabel","replaceMovedLabel","axisProto.init","coll","setupGroups","axisProto.setupGroups","axis","reverseTree","stats","labelOptions","labels","userAttr","groupedOptions","css","isGrouped","categoriesTree","labelsDepth","labelsSizes","labelsGridPath","tickLength","tickWidth","isXAxis","directionFactor","side","lineWidth","groupFontHeights","mergedCSS","hasOptions","axisProto.render","undefined","originalTickLength","labelsGrid","attr","visibility","top","left","right","bottom","height","visible","hasVisibleSeries","hasData","grid","horiz","drawHorizontalBorders","offset","opposite","userTickLength","strokeWidth","stroke","tickColor","add","axisGroup","addClass","groupSize","part","labelGroup","group","tick","min","startAt","label","hide","destroyed","axisProto.setCategories","newCategories","doRedraw","cleanGroups","every","axisProto.cleanGroups","ticks","n","_v","axisProto.groupSize","level","position","positions","direction","userXY","x","y","abs","tickProto.addLabel","category","topLabelSize","pos","isFirst","isLast","value","formatter","ctx","text","defaultLabelFormatter","textPxLength","getBBox","enabled","addGroupedLabels","tickProto.addGroupedLabels","useHTML","align","rotation","sizeKey","currentTick","currentCategory","mergedAttrs","childCount","labelOffsets","tickProto.render","index","old","opacity","treeCat","xy","getPosition","tickmarkOffset","start","baseLine","size","reverseCrisp","len","gridAttrs","ret","indexOf","userX","userY","minPos","maxPos","fix","bBox","lvlSize","attrs","tickProto.destroy","tickProto.getLabelSize","distance","tickProto.replaceMovedLabel","movedLabel"],"mappings":"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","file":"grouped-categories.min.js","sourcesContent":[]} \ No newline at end of file +{"version":3,"sources":["grouped-categories.js"],"names":["factory","module","exports","Highcharts","merge","pick","objectEach","isNumber","isObject","isString","pInt","format","Tick","Axis","SVGElement","sum","arr","reduce","acc","val","walk","key","fn","i","length","children","Category","constructor","obj","parent","userOptions","JSON","parse","stringify","name","toString","parts","cat","push","join","buildTree","cats","out","options","depth","categories","unshift","currentParent","leaves","Math","max","addGridPart","path","d","width","round","fontMetrics","fontSize","chart","elem","fontSizeNum","renderer","styledMode","includes","window","getComputedStyle","prototype","getStyle","call","styles","style","isNaN","lineHeight","h","b","f","axisProto","tickProto","protoAxisInit","init","protoAxisRender","render","protoAxisSetCategories","setCategories","protoTickGetLabelSize","getLabelSize","protoTickAddLabel","addLabel","protoTickDestroy","destroy","protoTickRender","protoTickReplaceMovedLabel","replaceMovedLabel","axisProto.init","coll","setupGroups","axisProto.setupGroups","axis","reverseTree","stats","labelOptions","labels","userAttr","groupedOptions","css","isGrouped","categoriesTree","labelsDepth","labelsSizes","labelsGridPath","tickLength","tickWidth","isXAxis","directionFactor","side","lineWidth","groupFontHeights","mergedCSS","hasOptions","axisProto.render","undefined","originalTickLength","labelsGrid","attr","visibility","top","left","right","bottom","height","visible","hasVisibleSeries","hasData","grid","horiz","drawHorizontalBorders","offset","opposite","userTickLength","strokeWidth","stroke","tickColor","add","axisGroup","addClass","groupSize","part","labelGroup","group","tick","min","startAt","label","hide","destroyed","axisProto.setCategories","newCategories","doRedraw","cleanGroups","every","axisProto.cleanGroups","ticks","n","_v","axisProto.groupSize","level","position","positions","direction","userXY","x","y","abs","tickProto.addLabel","category","topLabelSize","pos","isFirst","isLast","value","formatter","ctx","text","defaultLabelFormatter","textPxLength","getBBox","enabled","addGroupedLabels","tickProto.addGroupedLabels","useHTML","align","rotation","sizeKey","currentTick","currentCategory","mergedAttrs","childCount","labelOffsets","tickProto.render","index","old","opacity","treeCat","xy","getPosition","tickmarkOffset","start","baseLine","size","reverseCrisp","len","gridAttrs","ret","indexOf","userX","userY","minPos","maxPos","fix","bBox","lvlSize","attrs","tickProto.destroy","tickProto.getLabelSize","distance","map","tickProto.replaceMovedLabel","movedLabel"],"mappings":"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","file":"grouped-categories.min.js","sourcesContent":[]} \ No newline at end of file diff --git a/index.html b/index.html index 52b8d77..d518ff6 100644 --- a/index.html +++ b/index.html @@ -199,12 +199,13 @@

Usage and demos

groupedOptions: [{ style: { color: 'red' // set red font for labels in 1st-Level - } + }, + rotation: -90, }, { - rotation: -45, // rotate labels for a 2nd-level + rotation: -90, // rotate labels for a 2nd-level align: 'right' }], - rotation: 0 // 0-level options aren't changed, use them as always + rotation: -90 // 0-level options aren't changed, use them as always }, categories: [{ name: "America", diff --git a/ts/groupedCategories.ts b/ts/groupedCategories.ts index e4c9a71..d4cbfff 100644 --- a/ts/groupedCategories.ts +++ b/ts/groupedCategories.ts @@ -650,8 +650,7 @@ tickProto.getLabelSize = function (): number { } axis.topLabelSize = axis.labelsSizes[0]; - axis.labelsSizes[0] = size; - axis.labelsSizes[1] = size; + axis.labelsSizes = axis.labelsSizes.map((): number => size); return sum(axis.labelsSizes || []) - distance; }; From 7cff0d94c97503c2e73f7b9575f76efdf99c6af4 Mon Sep 17 00:00:00 2001 From: wchmiel Date: Fri, 26 Sep 2025 07:06:31 +0200 Subject: [PATCH 08/16] Small getLabelsSize method fix --- CHANGELOG.md | 1 + bug2.html | 15 ++++++++------- dist/grouped-categories.js | 10 ++++++---- dist/grouped-categories.min.js | 2 +- dist/grouped-categories.min.js.map | 2 +- ts/groupedCategories.ts | 12 +++++++----- 6 files changed, 24 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 072889b..4853c83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ All notable changes to this project will be documented in this file. - #227 - #228 - #206 +- #212 ### Notes / Migration - Use `npm run build` for TypeScript build only, `npm run build:gulp` for full pipeline. diff --git a/bug2.html b/bug2.html index 8cde68b..820124c 100644 --- a/bug2.html +++ b/bug2.html @@ -14,7 +14,7 @@
-
+
@@ -25,26 +25,27 @@ chart: { renderTo: "chart", type: "column", - inverted: false, + inverted: true, // styledMode: true }, series: [{ data: [4, 14, 18, 5, 6, 5, 14, 15, 18] }], xAxis: { - xlabels: { + labels: { groupedOptions: [{ style: { color: 'red' // set red font for labels in 1st-Level }, - rotation: -45, + rotation: -30, }, { style: { - color: 'green' // set red font for labels in 1st-Level + color: 'green' // set red font for labels in 1st-Level }, rotation: 45, }], - rotation: -45 // 0-level options aren't changed, use them as always + rotation: -55, // 0-level options aren't changed, use them as always + distance: 10 }, xcategories: ["Apple", "Banana", "Orange", "Carrot", "Potato", "Tomato", "Cod", "Salmon", "Tuna"], xcategories: [{ @@ -82,7 +83,7 @@ categories: ["Berlin", "Munich"] }] }], - labels: { + xlabels: { distance: 5 } } diff --git a/dist/grouped-categories.js b/dist/grouped-categories.js index 4c08ecd..b37dcad 100644 --- a/dist/grouped-categories.js +++ b/dist/grouped-categories.js @@ -494,15 +494,17 @@ tickProto.getLabelSize = function () { if (!axis.isGrouped) { return protoTickGetLabelSize.call(tick); } + if (!axis.labelsSizes) { + axis.labelsSizes = []; + } // Axis labels distance option should be taken into account, #206 // The default for size); return sum(axis.labelsSizes || []) - distance; }; tickProto.replaceMovedLabel = function () { diff --git a/dist/grouped-categories.min.js b/dist/grouped-categories.min.js index 745bc7c..aeeff30 100644 --- a/dist/grouped-categories.min.js +++ b/dist/grouped-categories.min.js @@ -13,7 +13,7 @@ pos:this.pos};var m=e.formatter?e.formatter.call(l,l):a.name;var t=k&&k[n-1];l=t a,b,c);a=this.axis;b=a.categories&&a.categories[this.pos];if(a.isGrouped&&b&&!(a.max&&this.pos>a.max)){c=this.isFirst;var e=a.max,g=a.min,d=a.horiz,k=a.labelsGridPath,p=a.tickWidth||0,f=this.getPosition(this.axis.horiz,this.pos,this.axis.tickmarkOffset),n=d?f.y:f.x,q=F(a.options.labels.style.fontSize?a.options.labels.style.fontSize:0,a.chart).b,l=this,m=a.groupSize(0),t=1,h=d&&f.x===a.pos+a.len||!d&&f.y===a.pos?-1:0;if(c){var u=d?[a.left,f.y,a.left,f.y+a.groupSize(!0)]:a.isXAxis?[f.x,a.top,f.x+a.groupSize(!0), a.top]:[f.x,a.top+a.len,f.x+a.groupSize(!0),a.top+a.len];w(k,u,p)}d&&a.lefth?0:h);const H=l.labelOffsets?.x||0,I=l.labelOffsets?.y||0;n=this.getPosition(this.axis.horiz,g?Math.max(l.startAt-1,g-1):l.startAt-1,this.axis.tickmarkOffset);u=this.getPosition(this.axis.horiz,e?Math.min(l.startAt+l.leaves-1-h,e):l.startAt+l.leaves- 1-h,this.axis.tickmarkOffset);var X=l.label?.getBBox(!0);f=a.groupSize(t);h=d&&u.x===a.pos+a.len||!d&&u.y===a.pos?-1:0;n=d?{x:(n.x+u.x)/2+H,y:m+(a.groupFontHeights?.[t]||0)+f/2+I/2}:{x:m+f/2+H,y:(n.y+u.y-(X?.height||0))/2+q+I};isNaN(n.x)||isNaN(n.y)||(l.label?.attr(n),k&&(d&&a.leftc);return D(a.labelsSizes||[])-b};v.replaceMovedLabel=function(){if(this.axis.isGrouped){const a=this.movedLabel;a&&(a.destroy(),delete this.movedLabel)}else W.call(this)}}); +this.axis;if(!a.isGrouped)return G.call(this);a.labelsSizes||(a.labelsSizes=[]);const b=a.options.labels.distance||8,c=a.labelsSizes[0]||0,e=G.call(this)+2*b;c size); - return sum(axis.labelsSizes || []) - distance; }; From 9774dc1e763ffd654d7b45af3a7c91b81a6f6828 Mon Sep 17 00:00:00 2001 From: wchmiel Date: Fri, 26 Sep 2025 08:00:10 +0200 Subject: [PATCH 09/16] Fixed #185, grouped categories visible when the series is not visible --- CHANGELOG.md | 1 + bug2.html | 2 +- dist/grouped-categories.js | 2 +- dist/grouped-categories.min.js | 2 +- dist/grouped-categories.min.js.map | 2 +- ts/groupedCategories.ts | 2 +- 6 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4853c83..beba328 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ All notable changes to this project will be documented in this file. ### Fixed - #181 +- #185 - #227 - #228 - #206 diff --git a/bug2.html b/bug2.html index 820124c..47541ca 100644 --- a/bug2.html +++ b/bug2.html @@ -25,7 +25,7 @@ chart: { renderTo: "chart", type: "column", - inverted: true, + inverted: false, // styledMode: true }, series: [{ diff --git a/dist/grouped-categories.js b/dist/grouped-categories.js index b37dcad..dd21d0a 100644 --- a/dist/grouped-categories.js +++ b/dist/grouped-categories.js @@ -186,7 +186,7 @@ axisProto.render = function () { const left = axis.left; const right = left + axis.width; const bottom = top + axis.height; - const visible = axis.hasVisibleSeries || axis.hasData; + const visible = axis.hasVisibleSeries || axis.hasData(); // #185 let depth = axis.labelsDepth || 0; let grid = axis.labelsGrid; const horiz = axis.horiz; diff --git a/dist/grouped-categories.min.js b/dist/grouped-categories.min.js index aeeff30..42837de 100644 --- a/dist/grouped-categories.min.js +++ b/dist/grouped-categories.min.js @@ -4,7 +4,7 @@ b=b.parent;return a.join(", ")}}const E=(a,b,c,e,g=0)=>{c.depth=c.depth||0;for(l window.getComputedStyle?c&&O.prototype.getStyle.call(c,"font-size"):a||c?.styles.fontSize||b?.renderer.style.fontSize;if(x(a)&&a.includes("px"))a=K(a);else if(!y(a)||isNaN(a))a=12;b=24>a?a+3:Math.round(1.2*a);return{h:b,b:Math.round(.8*b),f:a}};r=N.prototype;const v=M.prototype,Q=r.init,R=r.render,S=r.setCategories,G=v.getLabelSize,T=v.addLabel,U=v.destroy,V=v.render,W=v.replaceMovedLabel;r.init=function(a,b,c){Q.call(this,a,b,c);C(b)&&b.categories&&this.setupGroups(b)};r.setupGroups=function(a){const b= this.chart;var c=JSON.parse(JSON.stringify(a.categories||[]));const e=[],g={depth:0};var d=this.options.labels;const k=d&&d.groupedOptions;d=d&&d.style;E(c,e,g);if(this.isGrouped=0!==g.depth)for(this.categoriesTree=c,this.categories=e,this.labelsDepth=g.depth,this.labelsSizes=[],this.labelsGridPath=[],this.tickLength=a.tickLength||this.tickLength||null,this.tickWidth=A(a.tickWidth,this.isXAxis?1:0),this.directionFactor=[-1,1,1,-1][this.side],this.options.lineWidth=A(a.lineWidth,1),this.groupFontHeights= [],a=0;a<=g.depth;a++)c=k&&k[a-1]&&k[a-1].style?z(d,k[a-1].style):d,this.groupFontHeights[a]=Math.round(.3*F(c.fontSize?c.fontSize:0,b).b)};r.render=function(){const a=this;a.isGrouped&&(a.labelsGridPath=[]);void 0===a.originalTickLength&&(a.originalTickLength=a.options.tickLength);a.options.tickLength=a.isGrouped?.001:a.originalTickLength;R.call(a);if(!a.isGrouped)return a.labelsGrid&&a.labelsGrid.attr({visibility:"hidden"}),!1;var b=a.options;const c=a.top,e=a.left,g=e+a.width,d=c+a.height,k=a.hasVisibleSeries|| -a.hasData;let p=a.labelsDepth||0,f=a.labelsGrid;const n=a.horiz,q=a.labelsGridPath;let l=!1===b.drawHorizontalBorders?p+1:0,m=a.opposite?n?c:g:n?d:e;const t=a.tickWidth||0;a.userTickLength&&--p;f||(f=a.labelsGrid=a.chart.renderer.path().attr({strokeWidth:t,"stroke-width":t,stroke:b.tickColor||""}).add(a.axisGroup),b.tickColor||f.addClass("highcharts-tick"));for(;l<=p;)m+=a.groupSize(l),b=n?[e,m,g,m]:[m,c,m,d],w(q,b,t),l++;f.attr({d:q,visibility:k?"visible":"hidden"});a.labelGroup?.attr({visibility:k? +a.hasData();let p=a.labelsDepth||0,f=a.labelsGrid;const n=a.horiz,q=a.labelsGridPath;let l=!1===b.drawHorizontalBorders?p+1:0,m=a.opposite?n?c:g:n?d:e;const t=a.tickWidth||0;a.userTickLength&&--p;f||(f=a.labelsGrid=a.chart.renderer.path().attr({strokeWidth:t,"stroke-width":t,stroke:b.tickColor||""}).add(a.axisGroup),b.tickColor||f.addClass("highcharts-tick"));for(;l<=p;)m+=a.groupSize(l),b=n?[e,m,g,m]:[m,c,m,d],w(q,b,t),l++;f.attr({d:q,visibility:k?"visible":"hidden"});a.labelGroup?.attr({visibility:k? "visible":"hidden"});B(a.categoriesTree||[],"categories",h=>{h=h.tick;if(!h)return!1;a.min&&h.startAt+h.leaves-1a.max?(h.label?.hide(),h.destroyed=0):h.label?.attr({visibility:k?"visible":"hidden"});return!0});return!0};r.setCategories=function(a,b){this.categories&&this.cleanGroups();this.setupGroups({categories:a});this.categories=this.userOptions.categories=a;this.categories.every(c=>x(c))&&S.call(this,this.categories,b)};r.cleanGroups=function(){const a=this.ticks;for(const b in a)a[b].parent&& delete a[b].parent;B(this.categoriesTree||[],"categories",b=>{const c=b.tick;if(!c)return!1;c.label?.destroy();J(c,(e,g)=>delete c[g]);delete b.tick;return!0});this.labelsGrid=null};r.groupSize=function(a,b){const c=this.labelsSizes||[],e=this.directionFactor||1,g=this.options.labels&&this.options.labels.groupedOptions&&y(a)?this.options.labels.groupedOptions[a-1]:!1;let d=0;g&&(d=-1===e?g.x||0:g.y||0);y(a)&&void 0!==b&&(c[a]=Math.max(c[a]||0,b+10+Math.abs(d)));return!0===a?D(c)*e:y(a)&&c[a]?c[a]* e:0};v.addLabel=function(){const a=this.axis;var b=A(this.options&&this.options.labels,a.options.labels);let c;a.topLabelSize=0;T.call(this);if(!a.isGrouped||!a.categories||!(c=a.categories[this.pos]))return!1;if(this.label){var e=this.label,g=e.attr;var d={axis:a,tick:this,chart:a.chart,isFirst:!!this.isFirst,isLast:!!this.isLast,value:C(c)?c.name:c,pos:this.pos};b.formatter?b=b.formatter.call(d,d):b.format?(d.text=a.defaultLabelFormatter.call(d),b=L(b.format,d,a.chart)):b=a.defaultLabelFormatter.call(d); diff --git a/dist/grouped-categories.min.js.map b/dist/grouped-categories.min.js.map index eb86137..effa97e 100644 --- a/dist/grouped-categories.min.js.map +++ b/dist/grouped-categories.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["grouped-categories.js"],"names":["factory","module","exports","Highcharts","merge","pick","objectEach","isNumber","isObject","isString","pInt","format","Tick","Axis","SVGElement","sum","arr","reduce","acc","val","walk","key","fn","i","length","children","Category","constructor","obj","parent","userOptions","JSON","parse","stringify","name","toString","parts","cat","push","join","buildTree","cats","out","options","depth","categories","unshift","currentParent","leaves","Math","max","addGridPart","path","d","width","round","fontMetrics","fontSize","chart","elem","fontSizeNum","renderer","styledMode","includes","window","getComputedStyle","prototype","getStyle","call","styles","style","isNaN","lineHeight","h","b","f","axisProto","tickProto","protoAxisInit","init","protoAxisRender","render","protoAxisSetCategories","setCategories","protoTickGetLabelSize","getLabelSize","protoTickAddLabel","addLabel","protoTickDestroy","destroy","protoTickRender","protoTickReplaceMovedLabel","replaceMovedLabel","axisProto.init","coll","setupGroups","axisProto.setupGroups","axis","reverseTree","stats","labelOptions","labels","userAttr","groupedOptions","css","isGrouped","categoriesTree","labelsDepth","labelsSizes","labelsGridPath","tickLength","tickWidth","isXAxis","directionFactor","side","lineWidth","groupFontHeights","mergedCSS","hasOptions","axisProto.render","undefined","originalTickLength","labelsGrid","attr","visibility","top","left","right","bottom","height","visible","hasVisibleSeries","hasData","grid","horiz","drawHorizontalBorders","offset","opposite","userTickLength","strokeWidth","stroke","tickColor","add","axisGroup","addClass","groupSize","part","labelGroup","group","tick","min","startAt","label","hide","destroyed","axisProto.setCategories","newCategories","doRedraw","cleanGroups","every","axisProto.cleanGroups","ticks","n","_v","axisProto.groupSize","level","position","positions","direction","userXY","x","y","abs","tickProto.addLabel","category","topLabelSize","pos","isFirst","isLast","value","formatter","ctx","text","defaultLabelFormatter","textPxLength","getBBox","enabled","addGroupedLabels","tickProto.addGroupedLabels","useHTML","align","rotation","sizeKey","currentTick","currentCategory","mergedAttrs","childCount","labelOffsets","tickProto.render","index","old","opacity","treeCat","xy","getPosition","tickmarkOffset","start","baseLine","size","reverseCrisp","len","gridAttrs","ret","indexOf","userX","userY","minPos","maxPos","fix","bBox","lvlSize","attrs","tickProto.destroy","tickProto.getLabelSize","distance","tickProto.replaceMovedLabel","movedLabel"],"mappings":"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","file":"grouped-categories.min.js","sourcesContent":[]} \ No newline at end of file +{"version":3,"sources":["grouped-categories.js"],"names":["factory","module","exports","Highcharts","merge","pick","objectEach","isNumber","isObject","isString","pInt","format","Tick","Axis","SVGElement","sum","arr","reduce","acc","val","walk","key","fn","i","length","children","Category","constructor","obj","parent","userOptions","JSON","parse","stringify","name","toString","parts","cat","push","join","buildTree","cats","out","options","depth","categories","unshift","currentParent","leaves","Math","max","addGridPart","path","d","width","round","fontMetrics","fontSize","chart","elem","fontSizeNum","renderer","styledMode","includes","window","getComputedStyle","prototype","getStyle","call","styles","style","isNaN","lineHeight","h","b","f","axisProto","tickProto","protoAxisInit","init","protoAxisRender","render","protoAxisSetCategories","setCategories","protoTickGetLabelSize","getLabelSize","protoTickAddLabel","addLabel","protoTickDestroy","destroy","protoTickRender","protoTickReplaceMovedLabel","replaceMovedLabel","axisProto.init","coll","setupGroups","axisProto.setupGroups","axis","reverseTree","stats","labelOptions","labels","userAttr","groupedOptions","css","isGrouped","categoriesTree","labelsDepth","labelsSizes","labelsGridPath","tickLength","tickWidth","isXAxis","directionFactor","side","lineWidth","groupFontHeights","mergedCSS","hasOptions","axisProto.render","undefined","originalTickLength","labelsGrid","attr","visibility","top","left","right","bottom","height","visible","hasVisibleSeries","hasData","grid","horiz","drawHorizontalBorders","offset","opposite","userTickLength","strokeWidth","stroke","tickColor","add","axisGroup","addClass","groupSize","part","labelGroup","group","tick","min","startAt","label","hide","destroyed","axisProto.setCategories","newCategories","doRedraw","cleanGroups","every","axisProto.cleanGroups","ticks","n","_v","axisProto.groupSize","level","position","positions","direction","userXY","x","y","abs","tickProto.addLabel","category","topLabelSize","pos","isFirst","isLast","value","formatter","ctx","text","defaultLabelFormatter","textPxLength","getBBox","enabled","addGroupedLabels","tickProto.addGroupedLabels","useHTML","align","rotation","sizeKey","currentTick","currentCategory","mergedAttrs","childCount","labelOffsets","tickProto.render","index","old","opacity","treeCat","xy","getPosition","tickmarkOffset","start","baseLine","size","reverseCrisp","len","gridAttrs","ret","indexOf","userX","userY","minPos","maxPos","fix","bBox","lvlSize","attrs","tickProto.destroy","tickProto.getLabelSize","distance","tickProto.replaceMovedLabel","movedLabel"],"mappings":"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","file":"grouped-categories.min.js","sourcesContent":[]} \ No newline at end of file diff --git a/ts/groupedCategories.ts b/ts/groupedCategories.ts index 2030a17..5710e27 100644 --- a/ts/groupedCategories.ts +++ b/ts/groupedCategories.ts @@ -249,7 +249,7 @@ axisProto.render = function (this: GroupedAxis): boolean | void { const left = axis.left; const right = left + axis.width; const bottom = top + axis.height; - const visible = axis.hasVisibleSeries || axis.hasData; + const visible = axis.hasVisibleSeries || axis.hasData(); // #185 let depth = axis.labelsDepth || 0; let grid = axis.labelsGrid; const horiz = axis.horiz; From 4407aff7ad30761c60ef58a6bbd87d20c24d5ab4 Mon Sep 17 00:00:00 2001 From: wchmiel Date: Mon, 29 Sep 2025 10:07:24 +0200 Subject: [PATCH 10/16] Fixed #220, adjust grouped ticks labels overflow --- bug2.html | 6 +- bug3.html | 709 +++++++++++++++++++++++++++++ dist/grouped-categories.js | 44 ++ dist/grouped-categories.min.js | 32 +- dist/grouped-categories.min.js.map | 2 +- ts/groupedCategories.ts | 56 +++ 6 files changed, 830 insertions(+), 19 deletions(-) create mode 100644 bug3.html diff --git a/bug2.html b/bug2.html index 47541ca..c32ffc4 100644 --- a/bug2.html +++ b/bug2.html @@ -37,14 +37,14 @@ style: { color: 'red' // set red font for labels in 1st-Level }, - rotation: -30, + rotation: -90, }, { style: { color: 'green' // set red font for labels in 1st-Level }, - rotation: 45, + rotation: 90, }], - rotation: -55, // 0-level options aren't changed, use them as always + rotation: -90, // 0-level options aren't changed, use them as always distance: 10 }, xcategories: ["Apple", "Banana", "Orange", "Carrot", "Potato", "Tomato", "Cod", "Salmon", "Tuna"], diff --git a/bug3.html b/bug3.html new file mode 100644 index 0000000..f41a0d3 --- /dev/null +++ b/bug3.html @@ -0,0 +1,709 @@ + + + + + + + +Grouped Categories - Highcharts module + + + + + + + +
+ +
+ +
+ + + + + + diff --git a/dist/grouped-categories.js b/dist/grouped-categories.js index dd21d0a..bff0269 100644 --- a/dist/grouped-categories.js +++ b/dist/grouped-categories.js @@ -114,6 +114,45 @@ const fontMetrics = (fontSize, chart, elem) => { f: fontSizeNum }; }; +// Adjusts the tick label's CSS to handle overflow, hiding or truncating the label +// if it does not fit within its allocated slot width. This ensures that labels +// do not overlap or extend beyond their bounds in grouped category axes. +// #220 +const adjustTickLabelOverflow = (axis, groupedTick, leaves) => { + const horiz = axis.horiz; + const categoriesLength = axis.categories?.length || 1; + const groupSlotWidth = horiz ? (axis.width / categoriesLength) * leaves : (axis.height / categoriesLength) * leaves; + if (axis.options.labels.step === 1 && groupedTick.label) { + if (groupSlotWidth < 15) { + groupedTick.label.css({ + display: 'none', + width: undefined, + textOverflow: undefined + }); + } + else if (groupedTick.rotation !== -90 && + groupedTick.rotation !== 90 && + groupedTick.label && + (groupedTick.label.getBBox().width > groupSlotWidth || + (groupedTick.label.getBBox().width === 0 && + groupedTick.label.styles.width && + groupSlotWidth === +groupedTick.label.styles.width))) { + groupedTick.label.css({ + display: 'block', + width: groupSlotWidth.toString(), + textOverflow: 'ellipsis' + }); + } + else if (groupedTick.label.styles.textOverflow !== 'ellipsis' || + (groupedTick.label.styles.width && groupSlotWidth > +groupedTick.label.styles.width)) { + groupedTick.label.css({ + display: 'block', + width: undefined, + textOverflow: undefined + }); + } + } +}; // Main plugin implementation // Cache prototypes const axisProto = Axis.prototype; @@ -129,6 +168,9 @@ const protoTickRender = tickProto.render; const protoTickReplaceMovedLabel = tickProto.replaceMovedLabel; // Axis prototype extensions axisProto.init = function (chart, options, coll) { + if (typeof options === 'object' && options.categories) { + options = merge(options, { labels: { step: 1 } }); // #220 + } protoAxisInit.call(this, chart, options, coll); if (isObject(options) && options.categories) { this.setupGroups(options); @@ -446,6 +488,7 @@ tickProto.render = function (index, old, opacity) { } return ret; } + adjustTickLabelOverflow(axis, tick, tick.parent?.leaves || 1); // #220 while (group.parent) { group = group.parent; const fix = fixOffset(treeCat); @@ -456,6 +499,7 @@ tickProto.render = function (index, old, opacity) { bBox = group.label?.getBBox(true); lvlSize = axis.groupSize(depth); reverseCrisp = ((horiz && maxPos.x === axis.pos + axis.len) || (!horiz && maxPos.y === axis.pos)) ? -1 : 0; + adjustTickLabelOverflow(axis, group, group.leaves || 1); // #220 // TODO - check y position calculation attrs = horiz ? { x: (minPos.x + maxPos.x) / 2 + userX, diff --git a/dist/grouped-categories.min.js b/dist/grouped-categories.min.js index 42837de..2829f7e 100644 --- a/dist/grouped-categories.min.js +++ b/dist/grouped-categories.min.js @@ -1,19 +1,21 @@ (function(){ -(function(r){"object"===typeof module&&module.exports?module.exports=r:r(Highcharts)})(function(r){const {merge:z,pick:A,objectEach:J,isNumber:y,isObject:C,isString:x,pInt:K,format:L,Tick:M,Axis:N,SVGElement:O}=r,D=a=>a.reduce((b,c)=>b+c,0),B=(a,b,c)=>{for(let e=a.length-1;0<=e;e--){const g=a[e][b];g&&B(g,b,c);c(a[e])}};class P{constructor(a,b){this.userOptions=JSON.parse(JSON.stringify(a));this.name="string"===typeof a?a:a.name||"";this.parent=b}toString(){const a=[];let b=this;for(;b;)a.push(b.name), -b=b.parent;return a.join(", ")}}const E=(a,b,c,e,g=0)=>{c.depth=c.depth||0;for(let k=a.length-1;0<=k;k--){const p=a[k];if("object"===typeof p&&p.categories)e&&(p.parent=e),E(p.categories,b,c,p,g+1);else{var d=e;for(b.unshift(new P(p,d));d;)d.leaves=(d.leaves||0)+1,d=d.parent}}c.depth=Math.max(c.depth,g)},w=(a,b,c)=>{b[0]===b[2]&&(b[0]=b[2]=Math.round(b[0])-c%2/2);b[1]===b[3]&&(b[1]=b[3]=Math.round(b[1])+c%2/2);a.push("M",b[0],b[1],"L",b[2],b[3])},F=(a,b,c)=>{a=(b?.renderer.styledMode||x(a)&&a.includes("px"))&& -window.getComputedStyle?c&&O.prototype.getStyle.call(c,"font-size"):a||c?.styles.fontSize||b?.renderer.style.fontSize;if(x(a)&&a.includes("px"))a=K(a);else if(!y(a)||isNaN(a))a=12;b=24>a?a+3:Math.round(1.2*a);return{h:b,b:Math.round(.8*b),f:a}};r=N.prototype;const v=M.prototype,Q=r.init,R=r.render,S=r.setCategories,G=v.getLabelSize,T=v.addLabel,U=v.destroy,V=v.render,W=v.replaceMovedLabel;r.init=function(a,b,c){Q.call(this,a,b,c);C(b)&&b.categories&&this.setupGroups(b)};r.setupGroups=function(a){const b= -this.chart;var c=JSON.parse(JSON.stringify(a.categories||[]));const e=[],g={depth:0};var d=this.options.labels;const k=d&&d.groupedOptions;d=d&&d.style;E(c,e,g);if(this.isGrouped=0!==g.depth)for(this.categoriesTree=c,this.categories=e,this.labelsDepth=g.depth,this.labelsSizes=[],this.labelsGridPath=[],this.tickLength=a.tickLength||this.tickLength||null,this.tickWidth=A(a.tickWidth,this.isXAxis?1:0),this.directionFactor=[-1,1,1,-1][this.side],this.options.lineWidth=A(a.lineWidth,1),this.groupFontHeights= -[],a=0;a<=g.depth;a++)c=k&&k[a-1]&&k[a-1].style?z(d,k[a-1].style):d,this.groupFontHeights[a]=Math.round(.3*F(c.fontSize?c.fontSize:0,b).b)};r.render=function(){const a=this;a.isGrouped&&(a.labelsGridPath=[]);void 0===a.originalTickLength&&(a.originalTickLength=a.options.tickLength);a.options.tickLength=a.isGrouped?.001:a.originalTickLength;R.call(a);if(!a.isGrouped)return a.labelsGrid&&a.labelsGrid.attr({visibility:"hidden"}),!1;var b=a.options;const c=a.top,e=a.left,g=e+a.width,d=c+a.height,k=a.hasVisibleSeries|| -a.hasData();let p=a.labelsDepth||0,f=a.labelsGrid;const n=a.horiz,q=a.labelsGridPath;let l=!1===b.drawHorizontalBorders?p+1:0,m=a.opposite?n?c:g:n?d:e;const t=a.tickWidth||0;a.userTickLength&&--p;f||(f=a.labelsGrid=a.chart.renderer.path().attr({strokeWidth:t,"stroke-width":t,stroke:b.tickColor||""}).add(a.axisGroup),b.tickColor||f.addClass("highcharts-tick"));for(;l<=p;)m+=a.groupSize(l),b=n?[e,m,g,m]:[m,c,m,d],w(q,b,t),l++;f.attr({d:q,visibility:k?"visible":"hidden"});a.labelGroup?.attr({visibility:k? -"visible":"hidden"});B(a.categoriesTree||[],"categories",h=>{h=h.tick;if(!h)return!1;a.min&&h.startAt+h.leaves-1a.max?(h.label?.hide(),h.destroyed=0):h.label?.attr({visibility:k?"visible":"hidden"});return!0});return!0};r.setCategories=function(a,b){this.categories&&this.cleanGroups();this.setupGroups({categories:a});this.categories=this.userOptions.categories=a;this.categories.every(c=>x(c))&&S.call(this,this.categories,b)};r.cleanGroups=function(){const a=this.ticks;for(const b in a)a[b].parent&& -delete a[b].parent;B(this.categoriesTree||[],"categories",b=>{const c=b.tick;if(!c)return!1;c.label?.destroy();J(c,(e,g)=>delete c[g]);delete b.tick;return!0});this.labelsGrid=null};r.groupSize=function(a,b){const c=this.labelsSizes||[],e=this.directionFactor||1,g=this.options.labels&&this.options.labels.groupedOptions&&y(a)?this.options.labels.groupedOptions[a-1]:!1;let d=0;g&&(d=-1===e?g.x||0:g.y||0);y(a)&&void 0!==b&&(c[a]=Math.max(c[a]||0,b+10+Math.abs(d)));return!0===a?D(c)*e:y(a)&&c[a]?c[a]* -e:0};v.addLabel=function(){const a=this.axis;var b=A(this.options&&this.options.labels,a.options.labels);let c;a.topLabelSize=0;T.call(this);if(!a.isGrouped||!a.categories||!(c=a.categories[this.pos]))return!1;if(this.label){var e=this.label,g=e.attr;var d={axis:a,tick:this,chart:a.chart,isFirst:!!this.isFirst,isLast:!!this.isLast,value:C(c)?c.name:c,pos:this.pos};b.formatter?b=b.formatter.call(d,d):b.format?(d.text=a.defaultLabelFormatter.call(d),b=L(b.format,d,a.chart)):b=a.defaultLabelFormatter.call(d); -g.call(e,"text",b);this.label.textPxLength=this.label.getBBox().width}a.isGrouped&&a.options.labels.enabled&&!x(c)&&this.addGroupedLabels(c);return!0};v.addGroupedLabels=function(a){const b=this.axis,c=b.chart,e=b.options.labels,g=e.useHTML,d=e.style,k=e.groupedOptions,p={align:"center",rotation:e.rotation,x:0,y:0,style:void 0},f=b.horiz?"height":"width";let n=0;let q=this;for(;q;){if(a&&0a.max)){c=this.isFirst;var e=a.max,g=a.min,d=a.horiz,k=a.labelsGridPath,p=a.tickWidth||0,f=this.getPosition(this.axis.horiz,this.pos,this.axis.tickmarkOffset),n=d?f.y:f.x,q=F(a.options.labels.style.fontSize?a.options.labels.style.fontSize:0,a.chart).b,l=this,m=a.groupSize(0),t=1,h=d&&f.x===a.pos+a.len||!d&&f.y===a.pos?-1:0;if(c){var u=d?[a.left,f.y,a.left,f.y+a.groupSize(!0)]:a.isXAxis?[f.x,a.top,f.x+a.groupSize(!0), -a.top]:[f.x,a.top+a.len,f.x+a.groupSize(!0),a.top+a.len];w(k,u,p)}d&&a.lefth?0:h);const H=l.labelOffsets?.x||0,I=l.labelOffsets?.y||0;n=this.getPosition(this.axis.horiz,g?Math.max(l.startAt-1,g-1):l.startAt-1,this.axis.tickmarkOffset);u=this.getPosition(this.axis.horiz,e?Math.min(l.startAt+l.leaves-1-h,e):l.startAt+l.leaves- -1-h,this.axis.tickmarkOffset);var X=l.label?.getBBox(!0);f=a.groupSize(t);h=d&&u.x===a.pos+a.len||!d&&u.y===a.pos?-1:0;n=d?{x:(n.x+u.x)/2+H,y:m+(a.groupFontHeights?.[t]||0)+f/2+I/2}:{x:m+f/2+H,y:(n.y+u.y-(X?.height||0))/2+q+I};isNaN(n.x)||isNaN(n.y)||(l.label?.attr(n),k&&(d&&a.lefta.reduce((b,c)=>b+c,0),B=(a,b,c)=>{for(let e=a.length-1;0<=e;e--){const h=a[e][b];h&&B(h,b,c);c(a[e])}};class Q{constructor(a,b){this.userOptions=JSON.parse(JSON.stringify(a));this.name="string"===typeof a?a:a.name||"";this.parent=b}toString(){const a=[];let b=this;for(;b;)a.push(b.name), +b=b.parent;return a.join(", ")}}const E=(a,b,c,e,h=0)=>{c.depth=c.depth||0;for(let l=a.length-1;0<=l;l--){const p=a[l];if("object"===typeof p&&p.categories)e&&(p.parent=e),E(p.categories,b,c,p,h+1);else{var d=e;for(b.unshift(new Q(p,d));d;)d.leaves=(d.leaves||0)+1,d=d.parent}}c.depth=Math.max(c.depth,h)},w=(a,b,c)=>{b[0]===b[2]&&(b[0]=b[2]=Math.round(b[0])-c%2/2);b[1]===b[3]&&(b[1]=b[3]=Math.round(b[1])+c%2/2);a.push("M",b[0],b[1],"L",b[2],b[3])},F=(a,b,c)=>{a=(b?.renderer.styledMode||x(a)&&a.includes("px"))&& +window.getComputedStyle?c&&P.prototype.getStyle.call(c,"font-size"):a||c?.styles.fontSize||b?.renderer.style.fontSize;if(x(a)&&a.includes("px"))a=L(a);else if(!z(a)||isNaN(a))a=12;b=24>a?a+3:Math.round(1.2*a);return{h:b,b:Math.round(.8*b),f:a}},G=(a,b,c)=>{const e=a.categories?.length||1;c=a.horiz?a.width/e*c:a.height/e*c;1===a.options.labels.step&&b.label&&(15>c?b.label.css({display:"none",width:void 0,textOverflow:void 0}):-90!==b.rotation&&90!==b.rotation&&b.label&&(b.label.getBBox().width>c|| +0===b.label.getBBox().width&&b.label.styles.width&&c===+b.label.styles.width)?b.label.css({display:"block",width:c.toString(),textOverflow:"ellipsis"}):("ellipsis"!==b.label.styles.textOverflow||b.label.styles.width&&c>+b.label.styles.width)&&b.label.css({display:"block",width:void 0,textOverflow:void 0}))};r=O.prototype;const v=N.prototype,R=r.init,S=r.render,T=r.setCategories,H=v.getLabelSize,U=v.addLabel,V=v.destroy,W=v.render,X=v.replaceMovedLabel;r.init=function(a,b,c){"object"===typeof b&&b.categories&& +(b=y(b,{labels:{step:1}}));R.call(this,a,b,c);C(b)&&b.categories&&this.setupGroups(b)};r.setupGroups=function(a){const b=this.chart;var c=JSON.parse(JSON.stringify(a.categories||[]));const e=[],h={depth:0};var d=this.options.labels;const l=d&&d.groupedOptions;d=d&&d.style;E(c,e,h);if(this.isGrouped=0!==h.depth)for(this.categoriesTree=c,this.categories=e,this.labelsDepth=h.depth,this.labelsSizes=[],this.labelsGridPath=[],this.tickLength=a.tickLength||this.tickLength||null,this.tickWidth=A(a.tickWidth, +this.isXAxis?1:0),this.directionFactor=[-1,1,1,-1][this.side],this.options.lineWidth=A(a.lineWidth,1),this.groupFontHeights=[],a=0;a<=h.depth;a++)c=l&&l[a-1]&&l[a-1].style?y(d,l[a-1].style):d,this.groupFontHeights[a]=Math.round(.3*F(c.fontSize?c.fontSize:0,b).b)};r.render=function(){const a=this;a.isGrouped&&(a.labelsGridPath=[]);void 0===a.originalTickLength&&(a.originalTickLength=a.options.tickLength);a.options.tickLength=a.isGrouped?.001:a.originalTickLength;S.call(a);if(!a.isGrouped)return a.labelsGrid&& +a.labelsGrid.attr({visibility:"hidden"}),!1;var b=a.options;const c=a.top,e=a.left,h=e+a.width,d=c+a.height,l=a.hasVisibleSeries||a.hasData();let p=a.labelsDepth||0,f=a.labelsGrid;const n=a.horiz,q=a.labelsGridPath;let g=!1===b.drawHorizontalBorders?p+1:0,m=a.opposite?n?c:h:n?d:e;const t=a.tickWidth||0;a.userTickLength&&--p;f||(f=a.labelsGrid=a.chart.renderer.path().attr({strokeWidth:t,"stroke-width":t,stroke:b.tickColor||""}).add(a.axisGroup),b.tickColor||f.addClass("highcharts-tick"));for(;g<=p;)m+= +a.groupSize(g),b=n?[e,m,h,m]:[m,c,m,d],w(q,b,t),g++;f.attr({d:q,visibility:l?"visible":"hidden"});a.labelGroup?.attr({visibility:l?"visible":"hidden"});B(a.categoriesTree||[],"categories",k=>{k=k.tick;if(!k)return!1;a.min&&k.startAt+k.leaves-1a.max?(k.label?.hide(),k.destroyed=0):k.label?.attr({visibility:l?"visible":"hidden"});return!0});return!0};r.setCategories=function(a,b){this.categories&&this.cleanGroups();this.setupGroups({categories:a});this.categories=this.userOptions.categories= +a;this.categories.every(c=>x(c))&&T.call(this,this.categories,b)};r.cleanGroups=function(){const a=this.ticks;for(const b in a)a[b].parent&&delete a[b].parent;B(this.categoriesTree||[],"categories",b=>{const c=b.tick;if(!c)return!1;c.label?.destroy();K(c,(e,h)=>delete c[h]);delete b.tick;return!0});this.labelsGrid=null};r.groupSize=function(a,b){const c=this.labelsSizes||[],e=this.directionFactor||1,h=this.options.labels&&this.options.labels.groupedOptions&&z(a)?this.options.labels.groupedOptions[a- +1]:!1;let d=0;h&&(d=-1===e?h.x||0:h.y||0);z(a)&&void 0!==b&&(c[a]=Math.max(c[a]||0,b+10+Math.abs(d)));return!0===a?D(c)*e:z(a)&&c[a]?c[a]*e:0};v.addLabel=function(){const a=this.axis;var b=A(this.options&&this.options.labels,a.options.labels);let c;a.topLabelSize=0;U.call(this);if(!a.isGrouped||!a.categories||!(c=a.categories[this.pos]))return!1;if(this.label){var e=this.label,h=e.attr;var d={axis:a,tick:this,chart:a.chart,isFirst:!!this.isFirst,isLast:!!this.isLast,value:C(c)?c.name:c,pos:this.pos}; +b.formatter?b=b.formatter.call(d,d):b.format?(d.text=a.defaultLabelFormatter.call(d),b=M(b.format,d,a.chart)):b=a.defaultLabelFormatter.call(d);h.call(e,"text",b);this.label.textPxLength=this.label.getBBox().width}a.isGrouped&&a.options.labels.enabled&&!x(c)&&this.addGroupedLabels(c);return!0};v.addGroupedLabels=function(a){const b=this.axis,c=b.chart,e=b.options.labels,h=e.useHTML,d=e.style,l=e.groupedOptions,p={align:"center",rotation:e.rotation,x:0,y:0,style:void 0},f=b.horiz?"height":"width"; +let n=0;let q=this;for(;q;){if(a&&0a.max)){c=this.isFirst;var e=a.max,h=a.min,d=a.horiz,l=a.labelsGridPath,p=a.tickWidth||0,f=this.getPosition(this.axis.horiz,this.pos,this.axis.tickmarkOffset),n=d?f.y:f.x,q=F(a.options.labels.style.fontSize?a.options.labels.style.fontSize:0,a.chart).b, +g=this,m=a.groupSize(0),t=1,k=d&&f.x===a.pos+a.len||!d&&f.y===a.pos?-1:0;if(c){var u=d?[a.left,f.y,a.left,f.y+a.groupSize(!0)]:a.isXAxis?[f.x,a.top,f.x+a.groupSize(!0),a.top]:[f.x,a.top+a.len,f.x+a.groupSize(!0),a.top+a.len];w(l,u,p)}d&&a.leftk?0:k);const I=g.labelOffsets?.x||0,J=g.labelOffsets?.y|| +0;n=this.getPosition(this.axis.horiz,h?Math.max(g.startAt-1,h-1):g.startAt-1,this.axis.tickmarkOffset);u=this.getPosition(this.axis.horiz,e?Math.min(g.startAt+g.leaves-1-k,e):g.startAt+g.leaves-1-k,this.axis.tickmarkOffset);var Y=g.label?.getBBox(!0);f=a.groupSize(t);k=d&&u.x===a.pos+a.len||!d&&u.y===a.pos?-1:0;G(a,g,g.leaves||1);n=d?{x:(n.x+u.x)/2+I,y:m+(a.groupFontHeights?.[t]||0)+f/2+J/2}:{x:m+f/2+I,y:(n.y+u.y-(Y?.height||0))/2+q+J};isNaN(n.x)||isNaN(n.y)||(g.label?.attr(n),l&&(d&&a.left { + const horiz = axis.horiz; + const categoriesLength = axis.categories?.length || 1; + const groupSlotWidth = + horiz ? (axis.width / categoriesLength) * leaves : (axis.height / categoriesLength) * leaves; + + if (axis.options.labels.step === 1 && groupedTick.label) { + if (groupSlotWidth < 15) { + groupedTick.label.css({ + display: 'none', + width: undefined, + textOverflow: undefined + }); + } else if ( + groupedTick.rotation !== -90 && + groupedTick.rotation !== 90 && + groupedTick.label && + ( + groupedTick.label.getBBox().width > groupSlotWidth || + ( + groupedTick.label.getBBox().width === 0 && + groupedTick.label.styles.width && + groupSlotWidth === +groupedTick.label.styles.width + ) + ) + ) { + groupedTick.label.css({ + display: 'block', + width: groupSlotWidth.toString(), + textOverflow: 'ellipsis' + }); + } else if ( + groupedTick.label.styles.textOverflow !== 'ellipsis' || + (groupedTick.label.styles.width && groupSlotWidth > +groupedTick.label.styles.width) + ) { + groupedTick.label.css({ + display: 'block', + width: undefined, + textOverflow: undefined + }); + } + } +}; + // Main plugin implementation // Cache prototypes @@ -180,6 +228,10 @@ axisProto.init = function ( options: Partial, coll?: AxisCollectionKey ): void { + if (typeof options === 'object' && options.categories) { + options = merge(options, { labels: { step: 1 } }); // #220 + } + protoAxisInit.call(this, chart, options, coll); if (isObject(options) && options.categories) { @@ -570,6 +622,8 @@ tickProto.render = function (index: number, old?: boolean, opacity?: number): vo return ret; } + adjustTickLabelOverflow(axis, tick, tick.parent?.leaves || 1); // #220 + while (group.parent) { group = group.parent; @@ -586,6 +640,8 @@ tickProto.render = function (index: number, old?: boolean, opacity?: number): vo lvlSize = axis.groupSize(depth); reverseCrisp = ((horiz && maxPos.x === axis.pos + axis.len) || (!horiz && maxPos.y === axis.pos)) ? -1 : 0; + adjustTickLabelOverflow(axis, group, group.leaves || 1); // #220 + // TODO - check y position calculation attrs = horiz ? { x: (minPos.x + maxPos.x) / 2 + userX, From 712f499fb79d9ca83a9cf6f60709d2e479bd0ac2 Mon Sep 17 00:00:00 2001 From: wchmiel Date: Mon, 29 Sep 2025 12:19:49 +0200 Subject: [PATCH 11/16] Small improvement to #220, verticall slot width calculations --- CHANGELOG.md | 3 +++ bug2.html | 4 ++-- dist/grouped-categories.js | 12 +++++++---- dist/grouped-categories.min.js | 32 +++++++++++++++--------------- dist/grouped-categories.min.js.map | 2 +- ts/groupedCategories.ts | 14 ++++++++----- 6 files changed, 39 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index beba328..aeb62f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,8 +25,11 @@ All notable changes to this project will be documented in this file. - Babel configuration to preserve ES2020 when desired. ### Fixed +- #111 +- #179 - #181 - #185 +- #220 - #227 - #228 - #206 diff --git a/bug2.html b/bug2.html index c32ffc4..8c4a804 100644 --- a/bug2.html +++ b/bug2.html @@ -29,10 +29,10 @@ // styledMode: true }, series: [{ - data: [4, 14, 18, 5, 6, 5, 14, 15, 18] + data: [4, 14, 18, 5, 6, 5, 14, 15, 18, 10, 11, 12] }], xAxis: { - labels: { + xlabels: { groupedOptions: [{ style: { color: 'red' // set red font for labels in 1st-Level diff --git a/dist/grouped-categories.js b/dist/grouped-categories.js index bff0269..604a0a3 100644 --- a/dist/grouped-categories.js +++ b/dist/grouped-categories.js @@ -118,10 +118,14 @@ const fontMetrics = (fontSize, chart, elem) => { // if it does not fit within its allocated slot width. This ensures that labels // do not overlap or extend beyond their bounds in grouped category axes. // #220 -const adjustTickLabelOverflow = (axis, groupedTick, leaves) => { +const adjustTickLabelOverflow = (axis, groupedTick, leaves, depth) => { const horiz = axis.horiz; const categoriesLength = axis.categories?.length || 1; - const groupSlotWidth = horiz ? (axis.width / categoriesLength) * leaves : (axis.height / categoriesLength) * leaves; + let groupSlotWidth = (axis.width / categoriesLength) * leaves; + if (groupedTick.label && !horiz) { + const width = axis.groupSize(depth, groupedTick.label.getBBox().width); + groupSlotWidth = width < 0 ? width * -1 : width; + } if (axis.options.labels.step === 1 && groupedTick.label) { if (groupSlotWidth < 15) { groupedTick.label.css({ @@ -488,7 +492,7 @@ tickProto.render = function (index, old, opacity) { } return ret; } - adjustTickLabelOverflow(axis, tick, tick.parent?.leaves || 1); // #220 + adjustTickLabelOverflow(axis, tick, tick.leaves || 1, 0); // #220 while (group.parent) { group = group.parent; const fix = fixOffset(treeCat); @@ -499,7 +503,7 @@ tickProto.render = function (index, old, opacity) { bBox = group.label?.getBBox(true); lvlSize = axis.groupSize(depth); reverseCrisp = ((horiz && maxPos.x === axis.pos + axis.len) || (!horiz && maxPos.y === axis.pos)) ? -1 : 0; - adjustTickLabelOverflow(axis, group, group.leaves || 1); // #220 + adjustTickLabelOverflow(axis, group, group.leaves || 1, depth); // #220 // TODO - check y position calculation attrs = horiz ? { x: (minPos.x + maxPos.x) / 2 + userX, diff --git a/dist/grouped-categories.min.js b/dist/grouped-categories.min.js index 2829f7e..dd46615 100644 --- a/dist/grouped-categories.min.js +++ b/dist/grouped-categories.min.js @@ -1,20 +1,20 @@ (function(){ -(function(r){"object"===typeof module&&module.exports?module.exports=r:r(Highcharts)})(function(r){const {merge:y,pick:A,objectEach:K,isNumber:z,isObject:C,isString:x,pInt:L,format:M,Tick:N,Axis:O,SVGElement:P}=r,D=a=>a.reduce((b,c)=>b+c,0),B=(a,b,c)=>{for(let e=a.length-1;0<=e;e--){const h=a[e][b];h&&B(h,b,c);c(a[e])}};class Q{constructor(a,b){this.userOptions=JSON.parse(JSON.stringify(a));this.name="string"===typeof a?a:a.name||"";this.parent=b}toString(){const a=[];let b=this;for(;b;)a.push(b.name), -b=b.parent;return a.join(", ")}}const E=(a,b,c,e,h=0)=>{c.depth=c.depth||0;for(let l=a.length-1;0<=l;l--){const p=a[l];if("object"===typeof p&&p.categories)e&&(p.parent=e),E(p.categories,b,c,p,h+1);else{var d=e;for(b.unshift(new Q(p,d));d;)d.leaves=(d.leaves||0)+1,d=d.parent}}c.depth=Math.max(c.depth,h)},w=(a,b,c)=>{b[0]===b[2]&&(b[0]=b[2]=Math.round(b[0])-c%2/2);b[1]===b[3]&&(b[1]=b[3]=Math.round(b[1])+c%2/2);a.push("M",b[0],b[1],"L",b[2],b[3])},F=(a,b,c)=>{a=(b?.renderer.styledMode||x(a)&&a.includes("px"))&& -window.getComputedStyle?c&&P.prototype.getStyle.call(c,"font-size"):a||c?.styles.fontSize||b?.renderer.style.fontSize;if(x(a)&&a.includes("px"))a=L(a);else if(!z(a)||isNaN(a))a=12;b=24>a?a+3:Math.round(1.2*a);return{h:b,b:Math.round(.8*b),f:a}},G=(a,b,c)=>{const e=a.categories?.length||1;c=a.horiz?a.width/e*c:a.height/e*c;1===a.options.labels.step&&b.label&&(15>c?b.label.css({display:"none",width:void 0,textOverflow:void 0}):-90!==b.rotation&&90!==b.rotation&&b.label&&(b.label.getBBox().width>c|| -0===b.label.getBBox().width&&b.label.styles.width&&c===+b.label.styles.width)?b.label.css({display:"block",width:c.toString(),textOverflow:"ellipsis"}):("ellipsis"!==b.label.styles.textOverflow||b.label.styles.width&&c>+b.label.styles.width)&&b.label.css({display:"block",width:void 0,textOverflow:void 0}))};r=O.prototype;const v=N.prototype,R=r.init,S=r.render,T=r.setCategories,H=v.getLabelSize,U=v.addLabel,V=v.destroy,W=v.render,X=v.replaceMovedLabel;r.init=function(a,b,c){"object"===typeof b&&b.categories&& -(b=y(b,{labels:{step:1}}));R.call(this,a,b,c);C(b)&&b.categories&&this.setupGroups(b)};r.setupGroups=function(a){const b=this.chart;var c=JSON.parse(JSON.stringify(a.categories||[]));const e=[],h={depth:0};var d=this.options.labels;const l=d&&d.groupedOptions;d=d&&d.style;E(c,e,h);if(this.isGrouped=0!==h.depth)for(this.categoriesTree=c,this.categories=e,this.labelsDepth=h.depth,this.labelsSizes=[],this.labelsGridPath=[],this.tickLength=a.tickLength||this.tickLength||null,this.tickWidth=A(a.tickWidth, -this.isXAxis?1:0),this.directionFactor=[-1,1,1,-1][this.side],this.options.lineWidth=A(a.lineWidth,1),this.groupFontHeights=[],a=0;a<=h.depth;a++)c=l&&l[a-1]&&l[a-1].style?y(d,l[a-1].style):d,this.groupFontHeights[a]=Math.round(.3*F(c.fontSize?c.fontSize:0,b).b)};r.render=function(){const a=this;a.isGrouped&&(a.labelsGridPath=[]);void 0===a.originalTickLength&&(a.originalTickLength=a.options.tickLength);a.options.tickLength=a.isGrouped?.001:a.originalTickLength;S.call(a);if(!a.isGrouped)return a.labelsGrid&& -a.labelsGrid.attr({visibility:"hidden"}),!1;var b=a.options;const c=a.top,e=a.left,h=e+a.width,d=c+a.height,l=a.hasVisibleSeries||a.hasData();let p=a.labelsDepth||0,f=a.labelsGrid;const n=a.horiz,q=a.labelsGridPath;let g=!1===b.drawHorizontalBorders?p+1:0,m=a.opposite?n?c:h:n?d:e;const t=a.tickWidth||0;a.userTickLength&&--p;f||(f=a.labelsGrid=a.chart.renderer.path().attr({strokeWidth:t,"stroke-width":t,stroke:b.tickColor||""}).add(a.axisGroup),b.tickColor||f.addClass("highcharts-tick"));for(;g<=p;)m+= -a.groupSize(g),b=n?[e,m,h,m]:[m,c,m,d],w(q,b,t),g++;f.attr({d:q,visibility:l?"visible":"hidden"});a.labelGroup?.attr({visibility:l?"visible":"hidden"});B(a.categoriesTree||[],"categories",k=>{k=k.tick;if(!k)return!1;a.min&&k.startAt+k.leaves-1a.max?(k.label?.hide(),k.destroyed=0):k.label?.attr({visibility:l?"visible":"hidden"});return!0});return!0};r.setCategories=function(a,b){this.categories&&this.cleanGroups();this.setupGroups({categories:a});this.categories=this.userOptions.categories= -a;this.categories.every(c=>x(c))&&T.call(this,this.categories,b)};r.cleanGroups=function(){const a=this.ticks;for(const b in a)a[b].parent&&delete a[b].parent;B(this.categoriesTree||[],"categories",b=>{const c=b.tick;if(!c)return!1;c.label?.destroy();K(c,(e,h)=>delete c[h]);delete b.tick;return!0});this.labelsGrid=null};r.groupSize=function(a,b){const c=this.labelsSizes||[],e=this.directionFactor||1,h=this.options.labels&&this.options.labels.groupedOptions&&z(a)?this.options.labels.groupedOptions[a- -1]:!1;let d=0;h&&(d=-1===e?h.x||0:h.y||0);z(a)&&void 0!==b&&(c[a]=Math.max(c[a]||0,b+10+Math.abs(d)));return!0===a?D(c)*e:z(a)&&c[a]?c[a]*e:0};v.addLabel=function(){const a=this.axis;var b=A(this.options&&this.options.labels,a.options.labels);let c;a.topLabelSize=0;U.call(this);if(!a.isGrouped||!a.categories||!(c=a.categories[this.pos]))return!1;if(this.label){var e=this.label,h=e.attr;var d={axis:a,tick:this,chart:a.chart,isFirst:!!this.isFirst,isLast:!!this.isLast,value:C(c)?c.name:c,pos:this.pos}; -b.formatter?b=b.formatter.call(d,d):b.format?(d.text=a.defaultLabelFormatter.call(d),b=M(b.format,d,a.chart)):b=a.defaultLabelFormatter.call(d);h.call(e,"text",b);this.label.textPxLength=this.label.getBBox().width}a.isGrouped&&a.options.labels.enabled&&!x(c)&&this.addGroupedLabels(c);return!0};v.addGroupedLabels=function(a){const b=this.axis,c=b.chart,e=b.options.labels,h=e.useHTML,d=e.style,l=e.groupedOptions,p={align:"center",rotation:e.rotation,x:0,y:0,style:void 0},f=b.horiz?"height":"width"; -let n=0;let q=this;for(;q;){if(a&&0a.max)){c=this.isFirst;var e=a.max,h=a.min,d=a.horiz,l=a.labelsGridPath,p=a.tickWidth||0,f=this.getPosition(this.axis.horiz,this.pos,this.axis.tickmarkOffset),n=d?f.y:f.x,q=F(a.options.labels.style.fontSize?a.options.labels.style.fontSize:0,a.chart).b, -g=this,m=a.groupSize(0),t=1,k=d&&f.x===a.pos+a.len||!d&&f.y===a.pos?-1:0;if(c){var u=d?[a.left,f.y,a.left,f.y+a.groupSize(!0)]:a.isXAxis?[f.x,a.top,f.x+a.groupSize(!0),a.top]:[f.x,a.top+a.len,f.x+a.groupSize(!0),a.top+a.len];w(l,u,p)}d&&a.leftk?0:k);const I=g.labelOffsets?.x||0,J=g.labelOffsets?.y|| -0;n=this.getPosition(this.axis.horiz,h?Math.max(g.startAt-1,h-1):g.startAt-1,this.axis.tickmarkOffset);u=this.getPosition(this.axis.horiz,e?Math.min(g.startAt+g.leaves-1-k,e):g.startAt+g.leaves-1-k,this.axis.tickmarkOffset);var Y=g.label?.getBBox(!0);f=a.groupSize(t);k=d&&u.x===a.pos+a.len||!d&&u.y===a.pos?-1:0;G(a,g,g.leaves||1);n=d?{x:(n.x+u.x)/2+I,y:m+(a.groupFontHeights?.[t]||0)+f/2+J/2}:{x:m+f/2+I,y:(n.y+u.y-(Y?.height||0))/2+q+J};isNaN(n.x)||isNaN(n.y)||(g.label?.attr(n),l&&(d&&a.lefta.reduce((b,c)=>b+c,0),B=(a,b,c)=>{for(let d=a.length-1;0<=d;d--){const g=a[d][b];g&&B(g,b,c);c(a[d])}};class Q{constructor(a,b){this.userOptions=JSON.parse(JSON.stringify(a));this.name="string"===typeof a?a:a.name||"";this.parent=b}toString(){const a=[];let b=this;for(;b;)a.push(b.name), +b=b.parent;return a.join(", ")}}const E=(a,b,c,d,g=0)=>{c.depth=c.depth||0;for(let l=a.length-1;0<=l;l--){const p=a[l];if("object"===typeof p&&p.categories)d&&(p.parent=d),E(p.categories,b,c,p,g+1);else{var e=d;for(b.unshift(new Q(p,e));e;)e.leaves=(e.leaves||0)+1,e=e.parent}}c.depth=Math.max(c.depth,g)},w=(a,b,c)=>{b[0]===b[2]&&(b[0]=b[2]=Math.round(b[0])-c%2/2);b[1]===b[3]&&(b[1]=b[3]=Math.round(b[1])+c%2/2);a.push("M",b[0],b[1],"L",b[2],b[3])},F=(a,b,c)=>{a=(b?.renderer.styledMode||x(a)&&a.includes("px"))&& +window.getComputedStyle?c&&P.prototype.getStyle.call(c,"font-size"):a||c?.styles.fontSize||b?.renderer.style.fontSize;if(x(a)&&a.includes("px"))a=L(a);else if(!z(a)||isNaN(a))a=12;b=24>a?a+3:Math.round(1.2*a);return{h:b,b:Math.round(.8*b),f:a}},G=(a,b,c,d)=>{const g=a.horiz;c*=a.width/(a.categories?.length||1);b.label&&!g&&(d=a.groupSize(d,b.label.getBBox().width),c=0>d?-1*d:d);1===a.options.labels.step&&b.label&&(15>c?b.label.css({display:"none",width:void 0,textOverflow:void 0}):-90!==b.rotation&& +90!==b.rotation&&b.label&&(b.label.getBBox().width>c||0===b.label.getBBox().width&&b.label.styles.width&&c===+b.label.styles.width)?b.label.css({display:"block",width:c.toString(),textOverflow:"ellipsis"}):("ellipsis"!==b.label.styles.textOverflow||b.label.styles.width&&c>+b.label.styles.width)&&b.label.css({display:"block",width:void 0,textOverflow:void 0}))};r=O.prototype;const v=N.prototype,R=r.init,S=r.render,T=r.setCategories,H=v.getLabelSize,U=v.addLabel,V=v.destroy,W=v.render,X=v.replaceMovedLabel; +r.init=function(a,b,c){"object"===typeof b&&b.categories&&(b=y(b,{labels:{step:1}}));R.call(this,a,b,c);C(b)&&b.categories&&this.setupGroups(b)};r.setupGroups=function(a){const b=this.chart;var c=JSON.parse(JSON.stringify(a.categories||[]));const d=[],g={depth:0};var e=this.options.labels;const l=e&&e.groupedOptions;e=e&&e.style;E(c,d,g);if(this.isGrouped=0!==g.depth)for(this.categoriesTree=c,this.categories=d,this.labelsDepth=g.depth,this.labelsSizes=[],this.labelsGridPath=[],this.tickLength=a.tickLength|| +this.tickLength||null,this.tickWidth=A(a.tickWidth,this.isXAxis?1:0),this.directionFactor=[-1,1,1,-1][this.side],this.options.lineWidth=A(a.lineWidth,1),this.groupFontHeights=[],a=0;a<=g.depth;a++)c=l&&l[a-1]&&l[a-1].style?y(e,l[a-1].style):e,this.groupFontHeights[a]=Math.round(.3*F(c.fontSize?c.fontSize:0,b).b)};r.render=function(){const a=this;a.isGrouped&&(a.labelsGridPath=[]);void 0===a.originalTickLength&&(a.originalTickLength=a.options.tickLength);a.options.tickLength=a.isGrouped?.001:a.originalTickLength; +S.call(a);if(!a.isGrouped)return a.labelsGrid&&a.labelsGrid.attr({visibility:"hidden"}),!1;var b=a.options;const c=a.top,d=a.left,g=d+a.width,e=c+a.height,l=a.hasVisibleSeries||a.hasData();let p=a.labelsDepth||0,f=a.labelsGrid;const n=a.horiz,q=a.labelsGridPath;let h=!1===b.drawHorizontalBorders?p+1:0,m=a.opposite?n?c:g:n?e:d;const t=a.tickWidth||0;a.userTickLength&&--p;f||(f=a.labelsGrid=a.chart.renderer.path().attr({strokeWidth:t,"stroke-width":t,stroke:b.tickColor||""}).add(a.axisGroup),b.tickColor|| +f.addClass("highcharts-tick"));for(;h<=p;)m+=a.groupSize(h),b=n?[d,m,g,m]:[m,c,m,e],w(q,b,t),h++;f.attr({d:q,visibility:l?"visible":"hidden"});a.labelGroup?.attr({visibility:l?"visible":"hidden"});B(a.categoriesTree||[],"categories",k=>{k=k.tick;if(!k)return!1;a.min&&k.startAt+k.leaves-1a.max?(k.label?.hide(),k.destroyed=0):k.label?.attr({visibility:l?"visible":"hidden"});return!0});return!0};r.setCategories=function(a,b){this.categories&&this.cleanGroups();this.setupGroups({categories:a}); +this.categories=this.userOptions.categories=a;this.categories.every(c=>x(c))&&T.call(this,this.categories,b)};r.cleanGroups=function(){const a=this.ticks;for(const b in a)a[b].parent&&delete a[b].parent;B(this.categoriesTree||[],"categories",b=>{const c=b.tick;if(!c)return!1;c.label?.destroy();K(c,(d,g)=>delete c[g]);delete b.tick;return!0});this.labelsGrid=null};r.groupSize=function(a,b){const c=this.labelsSizes||[],d=this.directionFactor||1,g=this.options.labels&&this.options.labels.groupedOptions&& +z(a)?this.options.labels.groupedOptions[a-1]:!1;let e=0;g&&(e=-1===d?g.x||0:g.y||0);z(a)&&void 0!==b&&(c[a]=Math.max(c[a]||0,b+10+Math.abs(e)));return!0===a?D(c)*d:z(a)&&c[a]?c[a]*d:0};v.addLabel=function(){const a=this.axis;var b=A(this.options&&this.options.labels,a.options.labels);let c;a.topLabelSize=0;U.call(this);if(!a.isGrouped||!a.categories||!(c=a.categories[this.pos]))return!1;if(this.label){var d=this.label,g=d.attr;var e={axis:a,tick:this,chart:a.chart,isFirst:!!this.isFirst,isLast:!!this.isLast, +value:C(c)?c.name:c,pos:this.pos};b.formatter?b=b.formatter.call(e,e):b.format?(e.text=a.defaultLabelFormatter.call(e),b=M(b.format,e,a.chart)):b=a.defaultLabelFormatter.call(e);g.call(d,"text",b);this.label.textPxLength=this.label.getBBox().width}a.isGrouped&&a.options.labels.enabled&&!x(c)&&this.addGroupedLabels(c);return!0};v.addGroupedLabels=function(a){const b=this.axis,c=b.chart,d=b.options.labels,g=d.useHTML,e=d.style,l=d.groupedOptions,p={align:"center",rotation:d.rotation,x:0,y:0,style:void 0}, +f=b.horiz?"height":"width";let n=0;let q=this;for(;q;){if(a&&0a.max)){c=this.isFirst;var d=a.max,g=a.min,e=a.horiz,l=a.labelsGridPath,p=a.tickWidth||0,f=this.getPosition(this.axis.horiz,this.pos,this.axis.tickmarkOffset),n=e?f.y:f.x,q=F(a.options.labels.style.fontSize?a.options.labels.style.fontSize: +0,a.chart).b,h=this,m=a.groupSize(0),t=1,k=e&&f.x===a.pos+a.len||!e&&f.y===a.pos?-1:0;if(c){var u=e?[a.left,f.y,a.left,f.y+a.groupSize(!0)]:a.isXAxis?[f.x,a.top,f.x+a.groupSize(!0),a.top]:[f.x,a.top+a.len,f.x+a.groupSize(!0),a.top+a.len];w(l,u,p)}e&&a.leftk?0:k);const I=h.labelOffsets?.x||0,J=h.labelOffsets?.y|| +0;n=this.getPosition(this.axis.horiz,g?Math.max(h.startAt-1,g-1):h.startAt-1,this.axis.tickmarkOffset);u=this.getPosition(this.axis.horiz,d?Math.min(h.startAt+h.leaves-1-k,d):h.startAt+h.leaves-1-k,this.axis.tickmarkOffset);var Y=h.label?.getBBox(!0);f=a.groupSize(t);k=e&&u.x===a.pos+a.len||!e&&u.y===a.pos?-1:0;G(a,h,h.leaves||1,t);n=e?{x:(n.x+u.x)/2+I,y:m+(a.groupFontHeights?.[t]||0)+f/2+J/2}:{x:m+f/2+I,y:(n.y+u.y-(Y?.height||0))/2+q+J};isNaN(n.x)||isNaN(n.y)||(h.label?.attr(n),l&&(e&&a.left { +const adjustTickLabelOverflow = (axis: GroupedAxis, groupedTick: GroupedTick, leaves: number, depth: number): void => { const horiz = axis.horiz; const categoriesLength = axis.categories?.length || 1; - const groupSlotWidth = - horiz ? (axis.width / categoriesLength) * leaves : (axis.height / categoriesLength) * leaves; + let groupSlotWidth = (axis.width / categoriesLength) * leaves; + + if (groupedTick.label && !horiz) { + const width = axis.groupSize(depth, groupedTick.label.getBBox().width); + groupSlotWidth = width < 0 ? width * -1 : width; + } if (axis.options.labels.step === 1 && groupedTick.label) { if (groupSlotWidth < 15) { @@ -622,7 +626,7 @@ tickProto.render = function (index: number, old?: boolean, opacity?: number): vo return ret; } - adjustTickLabelOverflow(axis, tick, tick.parent?.leaves || 1); // #220 + adjustTickLabelOverflow(axis, tick, tick.leaves || 1, 0); // #220 while (group.parent) { group = group.parent; @@ -640,7 +644,7 @@ tickProto.render = function (index: number, old?: boolean, opacity?: number): vo lvlSize = axis.groupSize(depth); reverseCrisp = ((horiz && maxPos.x === axis.pos + axis.len) || (!horiz && maxPos.y === axis.pos)) ? -1 : 0; - adjustTickLabelOverflow(axis, group, group.leaves || 1); // #220 + adjustTickLabelOverflow(axis, group, group.leaves || 1, depth); // #220 // TODO - check y position calculation attrs = horiz ? { From 0034b7e15ddec476e743e89d77a03b7611d381e8 Mon Sep 17 00:00:00 2001 From: wchmiel Date: Tue, 30 Sep 2025 11:58:21 +0200 Subject: [PATCH 12/16] Fixed #232, textPxLength should be calculated based on the rotation --- CHANGELOG.md | 1 + bug2.html | 13 +++++++------ dist/grouped-categories.js | 6 +++++- dist/grouped-categories.min.js | 14 +++++++------- dist/grouped-categories.min.js.map | 2 +- ts/groupedCategories.ts | 6 +++++- 6 files changed, 26 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aeb62f5..575735a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ All notable changes to this project will be documented in this file. - #228 - #206 - #212 +- #232 ### Notes / Migration - Use `npm run build` for TypeScript build only, `npm run build:gulp` for full pipeline. diff --git a/bug2.html b/bug2.html index 8c4a804..5717f48 100644 --- a/bug2.html +++ b/bug2.html @@ -29,26 +29,27 @@ // styledMode: true }, series: [{ - data: [4, 14, 18, 5, 6, 5, 14, 15, 18, 10, 11, 12] + data: [4, 14, 18, 5, 6, 5, 14, 15, 18] + // data: [4, 14, 18, 5, 6, 5, 14, 15, 18, 10, 11, 12] }], xAxis: { - xlabels: { + labels: { groupedOptions: [{ style: { color: 'red' // set red font for labels in 1st-Level }, - rotation: -90, + rotation: 90, }, { style: { color: 'green' // set red font for labels in 1st-Level }, rotation: 90, }], - rotation: -90, // 0-level options aren't changed, use them as always + rotation: 90, // 0-level options aren't changed, use them as always distance: 10 }, xcategories: ["Apple", "Banana", "Orange", "Carrot", "Potato", "Tomato", "Cod", "Salmon", "Tuna"], - xcategories: [{ + categories: [{ name: "Fruit", categories: ["Apple", "Banana", "Orange"] }, { @@ -58,7 +59,7 @@ name: "Fish", categories: ["Cod", "Salmon", "Tuna"] }], - categories: [{ + xcategories: [{ name: "America", categories: [{ name: "USA", diff --git a/dist/grouped-categories.js b/dist/grouped-categories.js index 604a0a3..842e8c3 100644 --- a/dist/grouped-categories.js +++ b/dist/grouped-categories.js @@ -377,7 +377,11 @@ tickProto.addLabel = function () { value: isObject(category) ? category.name : category, pos: tick.pos })); - tick.label.textPxLength = tick.label.getBBox().width; + // #232 - calculate textPxLength based on rotation + const absRotation = Math.abs(tick.label.rotation || 0); + const reducedRotation = absRotation > 180 ? absRotation - (Math.floor(absRotation / 180) * 180) : absRotation; + tick.label.textPxLength = reducedRotation > 45 && reducedRotation < 135 ? + tick.label.getBBox().height : tick.label.getBBox().width; } if (axis.isGrouped && axis.options.labels.enabled && !isString(category)) { tick.addGroupedLabels(category); diff --git a/dist/grouped-categories.min.js b/dist/grouped-categories.min.js index dd46615..2e4999c 100644 --- a/dist/grouped-categories.min.js +++ b/dist/grouped-categories.min.js @@ -9,13 +9,13 @@ S.call(a);if(!a.isGrouped)return a.labelsGrid&&a.labelsGrid.attr({visibility:"hi f.addClass("highcharts-tick"));for(;h<=p;)m+=a.groupSize(h),b=n?[d,m,g,m]:[m,c,m,e],w(q,b,t),h++;f.attr({d:q,visibility:l?"visible":"hidden"});a.labelGroup?.attr({visibility:l?"visible":"hidden"});B(a.categoriesTree||[],"categories",k=>{k=k.tick;if(!k)return!1;a.min&&k.startAt+k.leaves-1a.max?(k.label?.hide(),k.destroyed=0):k.label?.attr({visibility:l?"visible":"hidden"});return!0});return!0};r.setCategories=function(a,b){this.categories&&this.cleanGroups();this.setupGroups({categories:a}); this.categories=this.userOptions.categories=a;this.categories.every(c=>x(c))&&T.call(this,this.categories,b)};r.cleanGroups=function(){const a=this.ticks;for(const b in a)a[b].parent&&delete a[b].parent;B(this.categoriesTree||[],"categories",b=>{const c=b.tick;if(!c)return!1;c.label?.destroy();K(c,(d,g)=>delete c[g]);delete b.tick;return!0});this.labelsGrid=null};r.groupSize=function(a,b){const c=this.labelsSizes||[],d=this.directionFactor||1,g=this.options.labels&&this.options.labels.groupedOptions&& z(a)?this.options.labels.groupedOptions[a-1]:!1;let e=0;g&&(e=-1===d?g.x||0:g.y||0);z(a)&&void 0!==b&&(c[a]=Math.max(c[a]||0,b+10+Math.abs(e)));return!0===a?D(c)*d:z(a)&&c[a]?c[a]*d:0};v.addLabel=function(){const a=this.axis;var b=A(this.options&&this.options.labels,a.options.labels);let c;a.topLabelSize=0;U.call(this);if(!a.isGrouped||!a.categories||!(c=a.categories[this.pos]))return!1;if(this.label){var d=this.label,g=d.attr;var e={axis:a,tick:this,chart:a.chart,isFirst:!!this.isFirst,isLast:!!this.isLast, -value:C(c)?c.name:c,pos:this.pos};b.formatter?b=b.formatter.call(e,e):b.format?(e.text=a.defaultLabelFormatter.call(e),b=M(b.format,e,a.chart)):b=a.defaultLabelFormatter.call(e);g.call(d,"text",b);this.label.textPxLength=this.label.getBBox().width}a.isGrouped&&a.options.labels.enabled&&!x(c)&&this.addGroupedLabels(c);return!0};v.addGroupedLabels=function(a){const b=this.axis,c=b.chart,d=b.options.labels,g=d.useHTML,e=d.style,l=d.groupedOptions,p={align:"center",rotation:d.rotation,x:0,y:0,style:void 0}, -f=b.horiz?"height":"width";let n=0;let q=this;for(;q;){if(a&&0a.max)){c=this.isFirst;var d=a.max,g=a.min,e=a.horiz,l=a.labelsGridPath,p=a.tickWidth||0,f=this.getPosition(this.axis.horiz,this.pos,this.axis.tickmarkOffset),n=e?f.y:f.x,q=F(a.options.labels.style.fontSize?a.options.labels.style.fontSize: -0,a.chart).b,h=this,m=a.groupSize(0),t=1,k=e&&f.x===a.pos+a.len||!e&&f.y===a.pos?-1:0;if(c){var u=e?[a.left,f.y,a.left,f.y+a.groupSize(!0)]:a.isXAxis?[f.x,a.top,f.x+a.groupSize(!0),a.top]:[f.x,a.top+a.len,f.x+a.groupSize(!0),a.top+a.len];w(l,u,p)}e&&a.leftk?0:k);const I=h.labelOffsets?.x||0,J=h.labelOffsets?.y|| -0;n=this.getPosition(this.axis.horiz,g?Math.max(h.startAt-1,g-1):h.startAt-1,this.axis.tickmarkOffset);u=this.getPosition(this.axis.horiz,d?Math.min(h.startAt+h.leaves-1-k,d):h.startAt+h.leaves-1-k,this.axis.tickmarkOffset);var Y=h.label?.getBBox(!0);f=a.groupSize(t);k=e&&u.x===a.pos+a.len||!e&&u.y===a.pos?-1:0;G(a,h,h.leaves||1,t);n=e?{x:(n.x+u.x)/2+I,y:m+(a.groupFontHeights?.[t]||0)+f/2+J/2}:{x:m+f/2+I,y:(n.y+u.y-(Y?.height||0))/2+q+J};isNaN(n.x)||isNaN(n.y)||(h.label?.attr(n),l&&(e&&a.leftd?this.label.getBBox().height:this.label.getBBox().width}a.isGrouped&&a.options.labels.enabled&&!x(c)&&this.addGroupedLabels(c);return!0};v.addGroupedLabels=function(a){const b=this.axis,c=b.chart,d=b.options.labels, +g=d.useHTML,e=d.style,l=d.groupedOptions,p={align:"center",rotation:d.rotation,x:0,y:0,style:void 0},f=b.horiz?"height":"width";let n=0;let q=this;for(;q;){if(a&&0a.max)){c=this.isFirst;var d=a.max,g=a.min,e=a.horiz,l=a.labelsGridPath,p=a.tickWidth||0,f=this.getPosition(this.axis.horiz, +this.pos,this.axis.tickmarkOffset),n=e?f.y:f.x,q=F(a.options.labels.style.fontSize?a.options.labels.style.fontSize:0,a.chart).b,h=this,m=a.groupSize(0),t=1,k=e&&f.x===a.pos+a.len||!e&&f.y===a.pos?-1:0;if(c){var u=e?[a.left,f.y,a.left,f.y+a.groupSize(!0)]:a.isXAxis?[f.x,a.top,f.x+a.groupSize(!0),a.top]:[f.x,a.top+a.len,f.x+a.groupSize(!0),a.top+a.len];w(l,u,p)}e&&a.leftk?0:k);const I=h.labelOffsets?.x||0,J=h.labelOffsets?.y||0;n=this.getPosition(this.axis.horiz,g?Math.max(h.startAt-1,g-1):h.startAt-1,this.axis.tickmarkOffset);u=this.getPosition(this.axis.horiz,d?Math.min(h.startAt+h.leaves-1-k,d):h.startAt+h.leaves-1-k,this.axis.tickmarkOffset);var Y=h.label?.getBBox(!0);f=a.groupSize(t);k=e&&u.x===a.pos+a.len||!e&&u.y===a.pos?-1:0;G(a,h,h.leaves||1,t);n=e?{x:(n.x+u.x)/2+I,y:m+(a.groupFontHeights?.[t]|| +0)+f/2+J/2}:{x:m+f/2+I,y:(n.y+u.y-(Y?.height||0))/2+q+J};isNaN(n.x)||isNaN(n.y)||(h.label?.attr(n),l&&(e&&a.left 180 ? absRotation - (Math.floor(absRotation / 180) * 180) : absRotation; + tick.label.textPxLength = reducedRotation > 45 && reducedRotation < 135 ? + tick.label.getBBox().height : tick.label.getBBox().width; } if (axis.isGrouped && axis.options.labels.enabled && !isString(category)) { From 513d103e71370fa17a6b58c6689653720de770fa Mon Sep 17 00:00:00 2001 From: wchmiel Date: Wed, 1 Oct 2025 08:42:07 +0200 Subject: [PATCH 13/16] Fixed #197, inverted chart rotated labels wrong positioning --- CHANGELOG.md | 1 + bug2.html | 18 ++++++++-------- dist/grouped-categories.js | 11 ++++------ dist/grouped-categories.min.js | 33 +++++++++++++++--------------- dist/grouped-categories.min.js.map | 2 +- ts/groupedCategories.ts | 14 ++++--------- 6 files changed, 35 insertions(+), 44 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 575735a..cb264ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ All notable changes to this project will be documented in this file. - #179 - #181 - #185 +- #197 - #220 - #227 - #228 diff --git a/bug2.html b/bug2.html index 5717f48..6994dd2 100644 --- a/bug2.html +++ b/bug2.html @@ -14,7 +14,7 @@
-
+
@@ -25,12 +25,12 @@ chart: { renderTo: "chart", type: "column", - inverted: false, + inverted: true, // styledMode: true }, series: [{ - data: [4, 14, 18, 5, 6, 5, 14, 15, 18] - // data: [4, 14, 18, 5, 6, 5, 14, 15, 18, 10, 11, 12] + // data: [4, 14, 18, 5, 6, 5, 14, 15, 18] + data: [4, 14, 18, 5, 6, 5, 14, 15, 18, 10, 11, 12] }], xAxis: { labels: { @@ -38,18 +38,18 @@ style: { color: 'red' // set red font for labels in 1st-Level }, - rotation: 90, + rotation: -60, }, { style: { color: 'green' // set red font for labels in 1st-Level }, - rotation: 90, + rotation: -20, }], - rotation: 90, // 0-level options aren't changed, use them as always + rotation: 45, // 0-level options aren't changed, use them as always distance: 10 }, xcategories: ["Apple", "Banana", "Orange", "Carrot", "Potato", "Tomato", "Cod", "Salmon", "Tuna"], - categories: [{ + xcategories: [{ name: "Fruit", categories: ["Apple", "Banana", "Orange"] }, { @@ -59,7 +59,7 @@ name: "Fish", categories: ["Cod", "Salmon", "Tuna"] }], - xcategories: [{ + categories: [{ name: "America", categories: [{ name: "USA", diff --git a/dist/grouped-categories.js b/dist/grouped-categories.js index 842e8c3..c66607a 100644 --- a/dist/grouped-categories.js +++ b/dist/grouped-categories.js @@ -461,7 +461,6 @@ tickProto.render = function (index, old, opacity) { const tickWidth = axis.tickWidth || 0; const xy = tickPosition(tick, tickPos); const start = horiz ? xy.y : xy.x; - const baseLine = fontMetrics(axis.options.labels.style.fontSize ? axis.options.labels.style.fontSize : 0, axis.chart).b; let group = tick; let size = axis.groupSize(0); let depth = 1; @@ -471,7 +470,6 @@ tickProto.render = function (index, old, opacity) { let minPos; let maxPos; let attrs; - let bBox; if (isFirst) { gridAttrs = horiz ? [axis.left, xy.y, axis.left, xy.y + axis.groupSize(true)] : @@ -502,19 +500,18 @@ tickProto.render = function (index, old, opacity) { const fix = fixOffset(treeCat); const userX = group.labelOffsets?.x || 0; const userY = group.labelOffsets?.y || 0; + const groupFontHeight = axis.groupFontHeights?.[depth] || 0; minPos = tickPosition(tick, min ? Math.max(group.startAt - 1, min - 1) : group.startAt - 1); maxPos = tickPosition(tick, max ? Math.min(group.startAt + group.leaves - 1 - fix, max) : group.startAt + group.leaves - 1 - fix); - bBox = group.label?.getBBox(true); lvlSize = axis.groupSize(depth); reverseCrisp = ((horiz && maxPos.x === axis.pos + axis.len) || (!horiz && maxPos.y === axis.pos)) ? -1 : 0; adjustTickLabelOverflow(axis, group, group.leaves || 1, depth); // #220 - // TODO - check y position calculation attrs = horiz ? { x: (minPos.x + maxPos.x) / 2 + userX, - y: (size) + ((axis).groupFontHeights?.[depth] || 0) + lvlSize / 2 + userY / 2 + y: size + groupFontHeight + lvlSize / 2 + userY / 2 } : { - x: (size) + lvlSize / 2 + userX, - y: (minPos.y + maxPos.y - (bBox?.height || 0)) / 2 + baseLine + userY + x: size + lvlSize / 2 + userX, + y: (minPos.y + maxPos.y) / 2 + groupFontHeight + userY // #197 }; if (!isNaN(attrs.x) && !isNaN(attrs.y)) { group.label?.attr(attrs); diff --git a/dist/grouped-categories.min.js b/dist/grouped-categories.min.js index 2e4999c..f71da77 100644 --- a/dist/grouped-categories.min.js +++ b/dist/grouped-categories.min.js @@ -1,21 +1,20 @@ (function(){ -(function(r){"object"===typeof module&&module.exports?module.exports=r:r(Highcharts)})(function(r){const {merge:y,pick:A,objectEach:K,isNumber:z,isObject:C,isString:x,pInt:L,format:M,Tick:N,Axis:O,SVGElement:P}=r,D=a=>a.reduce((b,c)=>b+c,0),B=(a,b,c)=>{for(let d=a.length-1;0<=d;d--){const g=a[d][b];g&&B(g,b,c);c(a[d])}};class Q{constructor(a,b){this.userOptions=JSON.parse(JSON.stringify(a));this.name="string"===typeof a?a:a.name||"";this.parent=b}toString(){const a=[];let b=this;for(;b;)a.push(b.name), -b=b.parent;return a.join(", ")}}const E=(a,b,c,d,g=0)=>{c.depth=c.depth||0;for(let l=a.length-1;0<=l;l--){const p=a[l];if("object"===typeof p&&p.categories)d&&(p.parent=d),E(p.categories,b,c,p,g+1);else{var e=d;for(b.unshift(new Q(p,e));e;)e.leaves=(e.leaves||0)+1,e=e.parent}}c.depth=Math.max(c.depth,g)},w=(a,b,c)=>{b[0]===b[2]&&(b[0]=b[2]=Math.round(b[0])-c%2/2);b[1]===b[3]&&(b[1]=b[3]=Math.round(b[1])+c%2/2);a.push("M",b[0],b[1],"L",b[2],b[3])},F=(a,b,c)=>{a=(b?.renderer.styledMode||x(a)&&a.includes("px"))&& -window.getComputedStyle?c&&P.prototype.getStyle.call(c,"font-size"):a||c?.styles.fontSize||b?.renderer.style.fontSize;if(x(a)&&a.includes("px"))a=L(a);else if(!z(a)||isNaN(a))a=12;b=24>a?a+3:Math.round(1.2*a);return{h:b,b:Math.round(.8*b),f:a}},G=(a,b,c,d)=>{const g=a.horiz;c*=a.width/(a.categories?.length||1);b.label&&!g&&(d=a.groupSize(d,b.label.getBBox().width),c=0>d?-1*d:d);1===a.options.labels.step&&b.label&&(15>c?b.label.css({display:"none",width:void 0,textOverflow:void 0}):-90!==b.rotation&& -90!==b.rotation&&b.label&&(b.label.getBBox().width>c||0===b.label.getBBox().width&&b.label.styles.width&&c===+b.label.styles.width)?b.label.css({display:"block",width:c.toString(),textOverflow:"ellipsis"}):("ellipsis"!==b.label.styles.textOverflow||b.label.styles.width&&c>+b.label.styles.width)&&b.label.css({display:"block",width:void 0,textOverflow:void 0}))};r=O.prototype;const v=N.prototype,R=r.init,S=r.render,T=r.setCategories,H=v.getLabelSize,U=v.addLabel,V=v.destroy,W=v.render,X=v.replaceMovedLabel; -r.init=function(a,b,c){"object"===typeof b&&b.categories&&(b=y(b,{labels:{step:1}}));R.call(this,a,b,c);C(b)&&b.categories&&this.setupGroups(b)};r.setupGroups=function(a){const b=this.chart;var c=JSON.parse(JSON.stringify(a.categories||[]));const d=[],g={depth:0};var e=this.options.labels;const l=e&&e.groupedOptions;e=e&&e.style;E(c,d,g);if(this.isGrouped=0!==g.depth)for(this.categoriesTree=c,this.categories=d,this.labelsDepth=g.depth,this.labelsSizes=[],this.labelsGridPath=[],this.tickLength=a.tickLength|| -this.tickLength||null,this.tickWidth=A(a.tickWidth,this.isXAxis?1:0),this.directionFactor=[-1,1,1,-1][this.side],this.options.lineWidth=A(a.lineWidth,1),this.groupFontHeights=[],a=0;a<=g.depth;a++)c=l&&l[a-1]&&l[a-1].style?y(e,l[a-1].style):e,this.groupFontHeights[a]=Math.round(.3*F(c.fontSize?c.fontSize:0,b).b)};r.render=function(){const a=this;a.isGrouped&&(a.labelsGridPath=[]);void 0===a.originalTickLength&&(a.originalTickLength=a.options.tickLength);a.options.tickLength=a.isGrouped?.001:a.originalTickLength; -S.call(a);if(!a.isGrouped)return a.labelsGrid&&a.labelsGrid.attr({visibility:"hidden"}),!1;var b=a.options;const c=a.top,d=a.left,g=d+a.width,e=c+a.height,l=a.hasVisibleSeries||a.hasData();let p=a.labelsDepth||0,f=a.labelsGrid;const n=a.horiz,q=a.labelsGridPath;let h=!1===b.drawHorizontalBorders?p+1:0,m=a.opposite?n?c:g:n?e:d;const t=a.tickWidth||0;a.userTickLength&&--p;f||(f=a.labelsGrid=a.chart.renderer.path().attr({strokeWidth:t,"stroke-width":t,stroke:b.tickColor||""}).add(a.axisGroup),b.tickColor|| -f.addClass("highcharts-tick"));for(;h<=p;)m+=a.groupSize(h),b=n?[d,m,g,m]:[m,c,m,e],w(q,b,t),h++;f.attr({d:q,visibility:l?"visible":"hidden"});a.labelGroup?.attr({visibility:l?"visible":"hidden"});B(a.categoriesTree||[],"categories",k=>{k=k.tick;if(!k)return!1;a.min&&k.startAt+k.leaves-1a.max?(k.label?.hide(),k.destroyed=0):k.label?.attr({visibility:l?"visible":"hidden"});return!0});return!0};r.setCategories=function(a,b){this.categories&&this.cleanGroups();this.setupGroups({categories:a}); -this.categories=this.userOptions.categories=a;this.categories.every(c=>x(c))&&T.call(this,this.categories,b)};r.cleanGroups=function(){const a=this.ticks;for(const b in a)a[b].parent&&delete a[b].parent;B(this.categoriesTree||[],"categories",b=>{const c=b.tick;if(!c)return!1;c.label?.destroy();K(c,(d,g)=>delete c[g]);delete b.tick;return!0});this.labelsGrid=null};r.groupSize=function(a,b){const c=this.labelsSizes||[],d=this.directionFactor||1,g=this.options.labels&&this.options.labels.groupedOptions&& -z(a)?this.options.labels.groupedOptions[a-1]:!1;let e=0;g&&(e=-1===d?g.x||0:g.y||0);z(a)&&void 0!==b&&(c[a]=Math.max(c[a]||0,b+10+Math.abs(e)));return!0===a?D(c)*d:z(a)&&c[a]?c[a]*d:0};v.addLabel=function(){const a=this.axis;var b=A(this.options&&this.options.labels,a.options.labels);let c;a.topLabelSize=0;U.call(this);if(!a.isGrouped||!a.categories||!(c=a.categories[this.pos]))return!1;if(this.label){var d=this.label,g=d.attr;var e={axis:a,tick:this,chart:a.chart,isFirst:!!this.isFirst,isLast:!!this.isLast, -value:C(c)?c.name:c,pos:this.pos};b.formatter?b=b.formatter.call(e,e):b.format?(e.text=a.defaultLabelFormatter.call(e),b=M(b.format,e,a.chart)):b=a.defaultLabelFormatter.call(e);g.call(d,"text",b);d=Math.abs(this.label.rotation||0);d=180d?this.label.getBBox().height:this.label.getBBox().width}a.isGrouped&&a.options.labels.enabled&&!x(c)&&this.addGroupedLabels(c);return!0};v.addGroupedLabels=function(a){const b=this.axis,c=b.chart,d=b.options.labels, -g=d.useHTML,e=d.style,l=d.groupedOptions,p={align:"center",rotation:d.rotation,x:0,y:0,style:void 0},f=b.horiz?"height":"width";let n=0;let q=this;for(;q;){if(a&&0a.max)){c=this.isFirst;var d=a.max,g=a.min,e=a.horiz,l=a.labelsGridPath,p=a.tickWidth||0,f=this.getPosition(this.axis.horiz, -this.pos,this.axis.tickmarkOffset),n=e?f.y:f.x,q=F(a.options.labels.style.fontSize?a.options.labels.style.fontSize:0,a.chart).b,h=this,m=a.groupSize(0),t=1,k=e&&f.x===a.pos+a.len||!e&&f.y===a.pos?-1:0;if(c){var u=e?[a.left,f.y,a.left,f.y+a.groupSize(!0)]:a.isXAxis?[f.x,a.top,f.x+a.groupSize(!0),a.top]:[f.x,a.top+a.len,f.x+a.groupSize(!0),a.top+a.len];w(l,u,p)}e&&a.leftk?0:k);const I=h.labelOffsets?.x||0,J=h.labelOffsets?.y||0;n=this.getPosition(this.axis.horiz,g?Math.max(h.startAt-1,g-1):h.startAt-1,this.axis.tickmarkOffset);u=this.getPosition(this.axis.horiz,d?Math.min(h.startAt+h.leaves-1-k,d):h.startAt+h.leaves-1-k,this.axis.tickmarkOffset);var Y=h.label?.getBBox(!0);f=a.groupSize(t);k=e&&u.x===a.pos+a.len||!e&&u.y===a.pos?-1:0;G(a,h,h.leaves||1,t);n=e?{x:(n.x+u.x)/2+I,y:m+(a.groupFontHeights?.[t]|| -0)+f/2+J/2}:{x:m+f/2+I,y:(n.y+u.y-(Y?.height||0))/2+q+J};isNaN(n.x)||isNaN(n.y)||(h.label?.attr(n),l&&(e&&a.lefta.reduce((b,c)=>b+c,0),A=(a,b,c)=>{for(let d=a.length-1;0<=d;d--){const k=a[d][b];k&&A(k,b,c);c(a[d])}};class O{constructor(a,b){this.userOptions=JSON.parse(JSON.stringify(a));this.name="string"===typeof a?a:a.name||"";this.parent=b}toString(){const a=[];let b=this;for(;b;)a.push(b.name),b=b.parent; +return a.join(", ")}}const D=(a,b,c,d,k=0)=>{c.depth=c.depth||0;for(let m=a.length-1;0<=m;m--){const f=a[m];if("object"===typeof f&&f.categories)d&&(f.parent=d),D(f.categories,b,c,f,k+1);else{var e=d;for(b.unshift(new O(f,e));e;)e.leaves=(e.leaves||0)+1,e=e.parent}}c.depth=Math.max(c.depth,k)},v=(a,b,c)=>{b[0]===b[2]&&(b[0]=b[2]=Math.round(b[0])-c%2/2);b[1]===b[3]&&(b[1]=b[3]=Math.round(b[1])+c%2/2);a.push("M",b[0],b[1],"L",b[2],b[3])},E=(a,b,c,d)=>{const k=a.horiz;c*=a.width/(a.categories?.length|| +1);b.label&&!k&&(d=a.groupSize(d,b.label.getBBox().width),c=0>d?-1*d:d);1===a.options.labels.step&&b.label&&(15>c?b.label.css({display:"none",width:void 0,textOverflow:void 0}):-90!==b.rotation&&90!==b.rotation&&b.label&&(b.label.getBBox().width>c||0===b.label.getBBox().width&&b.label.styles.width&&c===+b.label.styles.width)?b.label.css({display:"block",width:c.toString(),textOverflow:"ellipsis"}):("ellipsis"!==b.label.styles.textOverflow||b.label.styles.width&&c>+b.label.styles.width)&&b.label.css({display:"block", +width:void 0,textOverflow:void 0}))};t=N.prototype;const u=M.prototype,P=t.init,Q=t.render,R=t.setCategories,F=u.getLabelSize,S=u.addLabel,T=u.destroy,U=u.render,V=u.replaceMovedLabel;t.init=function(a,b,c){"object"===typeof b&&b.categories&&(b=x(b,{labels:{step:1}}));P.call(this,a,b,c);B(b)&&b.categories&&this.setupGroups(b)};t.setupGroups=function(a){const b=this.chart;var c=JSON.parse(JSON.stringify(a.categories||[])),d=[];const k={depth:0};var e=this.options.labels;const m=e&&e.groupedOptions; +e=e&&e.style;D(c,d,k);if(this.isGrouped=0!==k.depth){this.categoriesTree=c;this.categories=d;this.labelsDepth=k.depth;this.labelsSizes=[];this.labelsGridPath=[];this.tickLength=a.tickLength||this.tickLength||null;this.tickWidth=z(a.tickWidth,this.isXAxis?1:0);this.directionFactor=[-1,1,1,-1][this.side];this.options.lineWidth=z(a.lineWidth,1);this.groupFontHeights=[];for(let l=0;l<=k.depth;l++){var f=m&&m[l-1]&&m[l-1].style?x(e,m[l-1].style):e;a=this.groupFontHeights;c=l;d=Math;var g=d.round;f=f.fontSize? +f.fontSize:0;f=(b?.renderer.styledMode||w(f)&&f.includes("px"))&&window.getComputedStyle?void 0:f||(void 0)?.styles.fontSize||b?.renderer.style.fontSize;if(w(f)&&f.includes("px"))f=K(f);else if(!y(f)||isNaN(f))f=12;f=Math.round(.8*(24>f?f+3:Math.round(1.2*f)));a[c]=g.call(d,.3*f)}}};t.render=function(){const a=this;a.isGrouped&&(a.labelsGridPath=[]);void 0===a.originalTickLength&&(a.originalTickLength=a.options.tickLength);a.options.tickLength=a.isGrouped?.001:a.originalTickLength;Q.call(a);if(!a.isGrouped)return a.labelsGrid&& +a.labelsGrid.attr({visibility:"hidden"}),!1;var b=a.options;const c=a.top,d=a.left,k=d+a.width,e=c+a.height,m=a.hasVisibleSeries||a.hasData();let f=a.labelsDepth||0,g=a.labelsGrid;const l=a.horiz,h=a.labelsGridPath;let p=!1===b.drawHorizontalBorders?f+1:0,r=a.opposite?l?c:k:l?e:d;const n=a.tickWidth||0;a.userTickLength&&--f;g||(g=a.labelsGrid=a.chart.renderer.path().attr({strokeWidth:n,"stroke-width":n,stroke:b.tickColor||""}).add(a.axisGroup),b.tickColor||g.addClass("highcharts-tick"));for(;p<=f;)r+= +a.groupSize(p),b=l?[d,r,k,r]:[r,c,r,e],v(h,b,n),p++;g.attr({d:h,visibility:m?"visible":"hidden"});a.labelGroup?.attr({visibility:m?"visible":"hidden"});A(a.categoriesTree||[],"categories",q=>{q=q.tick;if(!q)return!1;a.min&&q.startAt+q.leaves-1a.max?(q.label?.hide(),q.destroyed=0):q.label?.attr({visibility:m?"visible":"hidden"});return!0});return!0};t.setCategories=function(a,b){this.categories&&this.cleanGroups();this.setupGroups({categories:a});this.categories=this.userOptions.categories= +a;this.categories.every(c=>w(c))&&R.call(this,this.categories,b)};t.cleanGroups=function(){const a=this.ticks;for(const b in a)a[b].parent&&delete a[b].parent;A(this.categoriesTree||[],"categories",b=>{const c=b.tick;if(!c)return!1;c.label?.destroy();J(c,(d,k)=>delete c[k]);delete b.tick;return!0});this.labelsGrid=null};t.groupSize=function(a,b){const c=this.labelsSizes||[],d=this.directionFactor||1,k=this.options.labels&&this.options.labels.groupedOptions&&y(a)?this.options.labels.groupedOptions[a- +1]:!1;let e=0;k&&(e=-1===d?k.x||0:k.y||0);y(a)&&void 0!==b&&(c[a]=Math.max(c[a]||0,b+10+Math.abs(e)));return!0===a?C(c)*d:y(a)&&c[a]?c[a]*d:0};u.addLabel=function(){const a=this.axis;var b=z(this.options&&this.options.labels,a.options.labels);let c;a.topLabelSize=0;S.call(this);if(!a.isGrouped||!a.categories||!(c=a.categories[this.pos]))return!1;if(this.label){var d=this.label,k=d.attr;var e={axis:a,tick:this,chart:a.chart,isFirst:!!this.isFirst,isLast:!!this.isLast,value:B(c)?c.name:c,pos:this.pos}; +b.formatter?b=b.formatter.call(e,e):b.format?(e.text=a.defaultLabelFormatter.call(e),b=L(b.format,e,a.chart)):b=a.defaultLabelFormatter.call(e);k.call(d,"text",b);d=Math.abs(this.label.rotation||0);d=180d?this.label.getBBox().height:this.label.getBBox().width}a.isGrouped&&a.options.labels.enabled&&!w(c)&&this.addGroupedLabels(c);return!0};u.addGroupedLabels=function(a){const b=this.axis,c=b.chart,d=b.options.labels,k=d.useHTML,e=d.style, +m=d.groupedOptions,f={align:"center",rotation:d.rotation,x:0,y:0,style:void 0},g=b.horiz?"height":"width";let l=0;let h=this;for(;h;){if(a&&0a.max)){c=this.isFirst;var d=a.max,k=a.min,e=a.horiz,m=a.labelsGridPath,f=a.tickWidth||0,g=this.getPosition(this.axis.horiz,this.pos,this.axis.tickmarkOffset), +l=e?g.y:g.x,h=this,p=a.groupSize(0),r=1,n=e&&g.x===a.pos+a.len||!e&&g.y===a.pos?-1:0;if(c){var q=e?[a.left,g.y,a.left,g.y+a.groupSize(!0)]:a.isXAxis?[g.x,a.top,g.x+a.groupSize(!0),a.top]:[g.x,a.top+a.len,g.x+a.groupSize(!0),a.top+a.len];v(m,q,f)}e&&a.leftn?0:n);const G=h.labelOffsets?.x||0,H=h.labelOffsets?.y|| +0,I=a.groupFontHeights?.[r]||0;l=this.getPosition(this.axis.horiz,k?Math.max(h.startAt-1,k-1):h.startAt-1,this.axis.tickmarkOffset);q=this.getPosition(this.axis.horiz,d?Math.min(h.startAt+h.leaves-1-n,d):h.startAt+h.leaves-1-n,this.axis.tickmarkOffset);g=a.groupSize(r);n=e&&q.x===a.pos+a.len||!e&&q.y===a.pos?-1:0;E(a,h,h.leaves||1,r);l=e?{x:(l.x+q.x)/2+G,y:p+I+g/2+H/2}:{x:p+g/2+G,y:(l.y+q.y)/2+I+H};isNaN(l.x)||isNaN(l.y)||(h.label?.attr(l),m&&(e&&a.left Date: Wed, 1 Oct 2025 11:17:38 +0200 Subject: [PATCH 14/16] Fixed #177, overlapping grouped labels on inverted chart --- CHANGELOG.md | 1 + bug4.html | 50 ++++++++++++++++ dist/grouped-categories.js | 84 +++++++++++++++----------- dist/grouped-categories.min.js | 33 +++++----- dist/grouped-categories.min.js.map | 2 +- ts/groupedCategories.ts | 96 +++++++++++++++++------------- 6 files changed, 171 insertions(+), 95 deletions(-) create mode 100644 bug4.html diff --git a/CHANGELOG.md b/CHANGELOG.md index cb264ab..2f7f3bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ All notable changes to this project will be documented in this file. ### Fixed - #111 +- #177 - #179 - #181 - #185 diff --git a/bug4.html b/bug4.html new file mode 100644 index 0000000..7fd075d --- /dev/null +++ b/bug4.html @@ -0,0 +1,50 @@ + + + + + + + +Grouped Categories - Highcharts module + + + + + + + +
+ +
+ +
+ + + + + + diff --git a/dist/grouped-categories.js b/dist/grouped-categories.js index c66607a..22edf3e 100644 --- a/dist/grouped-categories.js +++ b/dist/grouped-categories.js @@ -117,43 +117,55 @@ const fontMetrics = (fontSize, chart, elem) => { // Adjusts the tick label's CSS to handle overflow, hiding or truncating the label // if it does not fit within its allocated slot width. This ensures that labels // do not overlap or extend beyond their bounds in grouped category axes. -// #220 const adjustTickLabelOverflow = (axis, groupedTick, leaves, depth) => { - const horiz = axis.horiz; - const categoriesLength = axis.categories?.length || 1; - let groupSlotWidth = (axis.width / categoriesLength) * leaves; - if (groupedTick.label && !horiz) { - const width = axis.groupSize(depth, groupedTick.label.getBBox().width); - groupSlotWidth = width < 0 ? width * -1 : width; - } - if (axis.options.labels.step === 1 && groupedTick.label) { - if (groupSlotWidth < 15) { - groupedTick.label.css({ - display: 'none', - width: undefined, - textOverflow: undefined - }); - } - else if (groupedTick.rotation !== -90 && - groupedTick.rotation !== 90 && - groupedTick.label && - (groupedTick.label.getBBox().width > groupSlotWidth || - (groupedTick.label.getBBox().width === 0 && - groupedTick.label.styles.width && - groupSlotWidth === +groupedTick.label.styles.width))) { - groupedTick.label.css({ - display: 'block', - width: groupSlotWidth.toString(), - textOverflow: 'ellipsis' - }); - } - else if (groupedTick.label.styles.textOverflow !== 'ellipsis' || - (groupedTick.label.styles.width && groupSlotWidth > +groupedTick.label.styles.width)) { - groupedTick.label.css({ - display: 'block', - width: undefined, - textOverflow: undefined - }); + if (groupedTick.label) { + const horiz = axis.horiz; + const categoriesLength = axis.categories?.length || 1; + const labelHeight = fontMetrics(groupedTick.label?.styles.fontSize || 0, axis.chart).h; + const groupSlotHeight = horiz ? + Math.abs(axis.groupSize(depth, groupedTick.label.getBBox().height)) : + (axis.height / categoriesLength) * leaves; + const groupSlotWidth = horiz ? + (axis.width / categoriesLength) * leaves : + Math.abs(axis.groupSize(depth, groupedTick.label.getBBox().width)); + if (axis.options.labels.step === 1) { + // Handle width case, #220 + if (groupSlotWidth < 15) { + groupedTick.label.css({ + display: 'none', + width: undefined, + textOverflow: undefined + }); + } + else if (groupedTick.rotation !== -90 && + groupedTick.rotation !== 90 && + groupedTick.label && + (groupedTick.label.getBBox().width > groupSlotWidth || + (groupedTick.label.getBBox().width === 0 && + groupedTick.label.styles.width && + groupSlotWidth === +groupedTick.label.styles.width))) { + groupedTick.label.css({ + display: 'block', + width: groupSlotWidth.toString(), + textOverflow: 'ellipsis' + }); + } + else if (groupedTick.label.styles.textOverflow !== 'ellipsis' || + (groupedTick.label.styles.width && groupSlotWidth > +groupedTick.label.styles.width)) { + groupedTick.label.css({ + display: 'block', + width: undefined, + textOverflow: undefined + }); + } + // Handle height case, #177 + if (labelHeight > groupSlotHeight) { + groupedTick.label.css({ + display: 'none', + width: undefined, + textOverflow: undefined + }); + } } } }; diff --git a/dist/grouped-categories.min.js b/dist/grouped-categories.min.js index f71da77..a45838e 100644 --- a/dist/grouped-categories.min.js +++ b/dist/grouped-categories.min.js @@ -1,20 +1,21 @@ (function(){ -(function(t){"object"===typeof module&&module.exports?module.exports=t:t(Highcharts)})(function(t){const {merge:x,pick:z,objectEach:J,isNumber:y,isObject:B,isString:w,pInt:K,format:L,Tick:M,Axis:N}=t,C=a=>a.reduce((b,c)=>b+c,0),A=(a,b,c)=>{for(let d=a.length-1;0<=d;d--){const k=a[d][b];k&&A(k,b,c);c(a[d])}};class O{constructor(a,b){this.userOptions=JSON.parse(JSON.stringify(a));this.name="string"===typeof a?a:a.name||"";this.parent=b}toString(){const a=[];let b=this;for(;b;)a.push(b.name),b=b.parent; -return a.join(", ")}}const D=(a,b,c,d,k=0)=>{c.depth=c.depth||0;for(let m=a.length-1;0<=m;m--){const f=a[m];if("object"===typeof f&&f.categories)d&&(f.parent=d),D(f.categories,b,c,f,k+1);else{var e=d;for(b.unshift(new O(f,e));e;)e.leaves=(e.leaves||0)+1,e=e.parent}}c.depth=Math.max(c.depth,k)},v=(a,b,c)=>{b[0]===b[2]&&(b[0]=b[2]=Math.round(b[0])-c%2/2);b[1]===b[3]&&(b[1]=b[3]=Math.round(b[1])+c%2/2);a.push("M",b[0],b[1],"L",b[2],b[3])},E=(a,b,c,d)=>{const k=a.horiz;c*=a.width/(a.categories?.length|| -1);b.label&&!k&&(d=a.groupSize(d,b.label.getBBox().width),c=0>d?-1*d:d);1===a.options.labels.step&&b.label&&(15>c?b.label.css({display:"none",width:void 0,textOverflow:void 0}):-90!==b.rotation&&90!==b.rotation&&b.label&&(b.label.getBBox().width>c||0===b.label.getBBox().width&&b.label.styles.width&&c===+b.label.styles.width)?b.label.css({display:"block",width:c.toString(),textOverflow:"ellipsis"}):("ellipsis"!==b.label.styles.textOverflow||b.label.styles.width&&c>+b.label.styles.width)&&b.label.css({display:"block", -width:void 0,textOverflow:void 0}))};t=N.prototype;const u=M.prototype,P=t.init,Q=t.render,R=t.setCategories,F=u.getLabelSize,S=u.addLabel,T=u.destroy,U=u.render,V=u.replaceMovedLabel;t.init=function(a,b,c){"object"===typeof b&&b.categories&&(b=x(b,{labels:{step:1}}));P.call(this,a,b,c);B(b)&&b.categories&&this.setupGroups(b)};t.setupGroups=function(a){const b=this.chart;var c=JSON.parse(JSON.stringify(a.categories||[])),d=[];const k={depth:0};var e=this.options.labels;const m=e&&e.groupedOptions; -e=e&&e.style;D(c,d,k);if(this.isGrouped=0!==k.depth){this.categoriesTree=c;this.categories=d;this.labelsDepth=k.depth;this.labelsSizes=[];this.labelsGridPath=[];this.tickLength=a.tickLength||this.tickLength||null;this.tickWidth=z(a.tickWidth,this.isXAxis?1:0);this.directionFactor=[-1,1,1,-1][this.side];this.options.lineWidth=z(a.lineWidth,1);this.groupFontHeights=[];for(let l=0;l<=k.depth;l++){var f=m&&m[l-1]&&m[l-1].style?x(e,m[l-1].style):e;a=this.groupFontHeights;c=l;d=Math;var g=d.round;f=f.fontSize? -f.fontSize:0;f=(b?.renderer.styledMode||w(f)&&f.includes("px"))&&window.getComputedStyle?void 0:f||(void 0)?.styles.fontSize||b?.renderer.style.fontSize;if(w(f)&&f.includes("px"))f=K(f);else if(!y(f)||isNaN(f))f=12;f=Math.round(.8*(24>f?f+3:Math.round(1.2*f)));a[c]=g.call(d,.3*f)}}};t.render=function(){const a=this;a.isGrouped&&(a.labelsGridPath=[]);void 0===a.originalTickLength&&(a.originalTickLength=a.options.tickLength);a.options.tickLength=a.isGrouped?.001:a.originalTickLength;Q.call(a);if(!a.isGrouped)return a.labelsGrid&& -a.labelsGrid.attr({visibility:"hidden"}),!1;var b=a.options;const c=a.top,d=a.left,k=d+a.width,e=c+a.height,m=a.hasVisibleSeries||a.hasData();let f=a.labelsDepth||0,g=a.labelsGrid;const l=a.horiz,h=a.labelsGridPath;let p=!1===b.drawHorizontalBorders?f+1:0,r=a.opposite?l?c:k:l?e:d;const n=a.tickWidth||0;a.userTickLength&&--f;g||(g=a.labelsGrid=a.chart.renderer.path().attr({strokeWidth:n,"stroke-width":n,stroke:b.tickColor||""}).add(a.axisGroup),b.tickColor||g.addClass("highcharts-tick"));for(;p<=f;)r+= -a.groupSize(p),b=l?[d,r,k,r]:[r,c,r,e],v(h,b,n),p++;g.attr({d:h,visibility:m?"visible":"hidden"});a.labelGroup?.attr({visibility:m?"visible":"hidden"});A(a.categoriesTree||[],"categories",q=>{q=q.tick;if(!q)return!1;a.min&&q.startAt+q.leaves-1a.max?(q.label?.hide(),q.destroyed=0):q.label?.attr({visibility:m?"visible":"hidden"});return!0});return!0};t.setCategories=function(a,b){this.categories&&this.cleanGroups();this.setupGroups({categories:a});this.categories=this.userOptions.categories= -a;this.categories.every(c=>w(c))&&R.call(this,this.categories,b)};t.cleanGroups=function(){const a=this.ticks;for(const b in a)a[b].parent&&delete a[b].parent;A(this.categoriesTree||[],"categories",b=>{const c=b.tick;if(!c)return!1;c.label?.destroy();J(c,(d,k)=>delete c[k]);delete b.tick;return!0});this.labelsGrid=null};t.groupSize=function(a,b){const c=this.labelsSizes||[],d=this.directionFactor||1,k=this.options.labels&&this.options.labels.groupedOptions&&y(a)?this.options.labels.groupedOptions[a- -1]:!1;let e=0;k&&(e=-1===d?k.x||0:k.y||0);y(a)&&void 0!==b&&(c[a]=Math.max(c[a]||0,b+10+Math.abs(e)));return!0===a?C(c)*d:y(a)&&c[a]?c[a]*d:0};u.addLabel=function(){const a=this.axis;var b=z(this.options&&this.options.labels,a.options.labels);let c;a.topLabelSize=0;S.call(this);if(!a.isGrouped||!a.categories||!(c=a.categories[this.pos]))return!1;if(this.label){var d=this.label,k=d.attr;var e={axis:a,tick:this,chart:a.chart,isFirst:!!this.isFirst,isLast:!!this.isLast,value:B(c)?c.name:c,pos:this.pos}; -b.formatter?b=b.formatter.call(e,e):b.format?(e.text=a.defaultLabelFormatter.call(e),b=L(b.format,e,a.chart)):b=a.defaultLabelFormatter.call(e);k.call(d,"text",b);d=Math.abs(this.label.rotation||0);d=180d?this.label.getBBox().height:this.label.getBBox().width}a.isGrouped&&a.options.labels.enabled&&!w(c)&&this.addGroupedLabels(c);return!0};u.addGroupedLabels=function(a){const b=this.axis,c=b.chart,d=b.options.labels,k=d.useHTML,e=d.style, -m=d.groupedOptions,f={align:"center",rotation:d.rotation,x:0,y:0,style:void 0},g=b.horiz?"height":"width";let l=0;let h=this;for(;h;){if(a&&0a.max)){c=this.isFirst;var d=a.max,k=a.min,e=a.horiz,m=a.labelsGridPath,f=a.tickWidth||0,g=this.getPosition(this.axis.horiz,this.pos,this.axis.tickmarkOffset), -l=e?g.y:g.x,h=this,p=a.groupSize(0),r=1,n=e&&g.x===a.pos+a.len||!e&&g.y===a.pos?-1:0;if(c){var q=e?[a.left,g.y,a.left,g.y+a.groupSize(!0)]:a.isXAxis?[g.x,a.top,g.x+a.groupSize(!0),a.top]:[g.x,a.top+a.len,g.x+a.groupSize(!0),a.top+a.len];v(m,q,f)}e&&a.leftn?0:n);const G=h.labelOffsets?.x||0,H=h.labelOffsets?.y|| -0,I=a.groupFontHeights?.[r]||0;l=this.getPosition(this.axis.horiz,k?Math.max(h.startAt-1,k-1):h.startAt-1,this.axis.tickmarkOffset);q=this.getPosition(this.axis.horiz,d?Math.min(h.startAt+h.leaves-1-n,d):h.startAt+h.leaves-1-n,this.axis.tickmarkOffset);g=a.groupSize(r);n=e&&q.x===a.pos+a.len||!e&&q.y===a.pos?-1:0;E(a,h,h.leaves||1,r);l=e?{x:(l.x+q.x)/2+G,y:p+I+g/2+H/2}:{x:p+g/2+G,y:(l.y+q.y)/2+I+H};isNaN(l.x)||isNaN(l.y)||(h.label?.attr(l),m&&(e&&a.lefta.reduce((b,c)=>b+c,0),A=(a,b,c)=>{for(let d=a.length-1;0<=d;d--){const h=a[d][b];h&&A(h,b,c);c(a[d])}};class Q{constructor(a,b){this.userOptions=JSON.parse(JSON.stringify(a));this.name="string"===typeof a?a:a.name||"";this.parent=b}toString(){const a=[];let b=this;for(;b;)a.push(b.name), +b=b.parent;return a.join(", ")}}const D=(a,b,c,d,h=0)=>{c.depth=c.depth||0;for(let k=a.length-1;0<=k;k--){const p=a[k];if("object"===typeof p&&p.categories)d&&(p.parent=d),D(p.categories,b,c,p,h+1);else{var e=d;for(b.unshift(new Q(p,e));e;)e.leaves=(e.leaves||0)+1,e=e.parent}}c.depth=Math.max(c.depth,h)},v=(a,b,c)=>{b[0]===b[2]&&(b[0]=b[2]=Math.round(b[0])-c%2/2);b[1]===b[3]&&(b[1]=b[3]=Math.round(b[1])+c%2/2);a.push("M",b[0],b[1],"L",b[2],b[3])},E=(a,b,c)=>{a=(b?.renderer.styledMode||w(a)&&a.includes("px"))&& +window.getComputedStyle?c&&P.prototype.getStyle.call(c,"font-size"):a||c?.styles.fontSize||b?.renderer.style.fontSize;if(w(a)&&a.includes("px"))a=L(a);else if(!y(a)||isNaN(a))a=12;b=24>a?a+3:Math.round(1.2*a);return{h:b,b:Math.round(.8*b),f:a}},F=(a,b,c,d)=>{if(b.label){const h=a.horiz,e=a.categories?.length||1,k=E(b.label?.styles.fontSize||0,a.chart).h,p=h?Math.abs(a.groupSize(d,b.label.getBBox().height)):a.height/e*c;c=h?a.width/e*c:Math.abs(a.groupSize(d,b.label.getBBox().width));1===a.options.labels.step&& +(15>c?b.label.css({display:"none",width:void 0,textOverflow:void 0}):-90!==b.rotation&&90!==b.rotation&&b.label&&(b.label.getBBox().width>c||0===b.label.getBBox().width&&b.label.styles.width&&c===+b.label.styles.width)?b.label.css({display:"block",width:c.toString(),textOverflow:"ellipsis"}):("ellipsis"!==b.label.styles.textOverflow||b.label.styles.width&&c>+b.label.styles.width)&&b.label.css({display:"block",width:void 0,textOverflow:void 0}),k>p&&b.label.css({display:"none",width:void 0,textOverflow:void 0}))}}; +t=O.prototype;const u=N.prototype,R=t.init,S=t.render,T=t.setCategories,G=u.getLabelSize,U=u.addLabel,V=u.destroy,W=u.render,X=u.replaceMovedLabel;t.init=function(a,b,c){"object"===typeof b&&b.categories&&(b=x(b,{labels:{step:1}}));R.call(this,a,b,c);B(b)&&b.categories&&this.setupGroups(b)};t.setupGroups=function(a){const b=this.chart;var c=JSON.parse(JSON.stringify(a.categories||[]));const d=[],h={depth:0};var e=this.options.labels;const k=e&&e.groupedOptions;e=e&&e.style;D(c,d,h);if(this.isGrouped= +0!==h.depth)for(this.categoriesTree=c,this.categories=d,this.labelsDepth=h.depth,this.labelsSizes=[],this.labelsGridPath=[],this.tickLength=a.tickLength||this.tickLength||null,this.tickWidth=z(a.tickWidth,this.isXAxis?1:0),this.directionFactor=[-1,1,1,-1][this.side],this.options.lineWidth=z(a.lineWidth,1),this.groupFontHeights=[],a=0;a<=h.depth;a++)c=k&&k[a-1]&&k[a-1].style?x(e,k[a-1].style):e,this.groupFontHeights[a]=Math.round(.3*E(c.fontSize?c.fontSize:0,b).b)};t.render=function(){const a=this; +a.isGrouped&&(a.labelsGridPath=[]);void 0===a.originalTickLength&&(a.originalTickLength=a.options.tickLength);a.options.tickLength=a.isGrouped?.001:a.originalTickLength;S.call(a);if(!a.isGrouped)return a.labelsGrid&&a.labelsGrid.attr({visibility:"hidden"}),!1;var b=a.options;const c=a.top,d=a.left,h=d+a.width,e=c+a.height,k=a.hasVisibleSeries||a.hasData();let p=a.labelsDepth||0,f=a.labelsGrid;const q=a.horiz,g=a.labelsGridPath;let m=!1===b.drawHorizontalBorders?p+1:0,r=a.opposite?q?c:h:q?e:d;const l= +a.tickWidth||0;a.userTickLength&&--p;f||(f=a.labelsGrid=a.chart.renderer.path().attr({strokeWidth:l,"stroke-width":l,stroke:b.tickColor||""}).add(a.axisGroup),b.tickColor||f.addClass("highcharts-tick"));for(;m<=p;)r+=a.groupSize(m),b=q?[d,r,h,r]:[r,c,r,e],v(g,b,l),m++;f.attr({d:g,visibility:k?"visible":"hidden"});a.labelGroup?.attr({visibility:k?"visible":"hidden"});A(a.categoriesTree||[],"categories",n=>{n=n.tick;if(!n)return!1;a.min&&n.startAt+n.leaves-1a.max?(n.label?.hide(), +n.destroyed=0):n.label?.attr({visibility:k?"visible":"hidden"});return!0});return!0};t.setCategories=function(a,b){this.categories&&this.cleanGroups();this.setupGroups({categories:a});this.categories=this.userOptions.categories=a;this.categories.every(c=>w(c))&&T.call(this,this.categories,b)};t.cleanGroups=function(){const a=this.ticks;for(const b in a)a[b].parent&&delete a[b].parent;A(this.categoriesTree||[],"categories",b=>{const c=b.tick;if(!c)return!1;c.label?.destroy();K(c,(d,h)=>delete c[h]); +delete b.tick;return!0});this.labelsGrid=null};t.groupSize=function(a,b){const c=this.labelsSizes||[],d=this.directionFactor||1,h=this.options.labels&&this.options.labels.groupedOptions&&y(a)?this.options.labels.groupedOptions[a-1]:!1;let e=0;h&&(e=-1===d?h.x||0:h.y||0);y(a)&&void 0!==b&&(c[a]=Math.max(c[a]||0,b+10+Math.abs(e)));return!0===a?C(c)*d:y(a)&&c[a]?c[a]*d:0};u.addLabel=function(){const a=this.axis;var b=z(this.options&&this.options.labels,a.options.labels);let c;a.topLabelSize=0;U.call(this); +if(!a.isGrouped||!a.categories||!(c=a.categories[this.pos]))return!1;if(this.label){var d=this.label,h=d.attr;var e={axis:a,tick:this,chart:a.chart,isFirst:!!this.isFirst,isLast:!!this.isLast,value:B(c)?c.name:c,pos:this.pos};b.formatter?b=b.formatter.call(e,e):b.format?(e.text=a.defaultLabelFormatter.call(e),b=M(b.format,e,a.chart)):b=a.defaultLabelFormatter.call(e);h.call(d,"text",b);d=Math.abs(this.label.rotation||0);d=180d?this.label.getBBox().height: +this.label.getBBox().width}a.isGrouped&&a.options.labels.enabled&&!w(c)&&this.addGroupedLabels(c);return!0};u.addGroupedLabels=function(a){const b=this.axis,c=b.chart,d=b.options.labels,h=d.useHTML,e=d.style,k=d.groupedOptions,p={align:"center",rotation:d.rotation,x:0,y:0,style:void 0},f=b.horiz?"height":"width";let q=0;let g=this;for(;g;){if(a&&0a.max)){c=this.isFirst;var d=a.max,h=a.min,e=a.horiz,k=a.labelsGridPath,p=a.tickWidth||0,f=this.getPosition(this.axis.horiz,this.pos,this.axis.tickmarkOffset),q=e?f.y:f.x,g=this,m=a.groupSize(0),r=1,l=e&&f.x===a.pos+a.len||!e&&f.y===a.pos?-1:0;if(c){var n=e?[a.left,f.y,a.left,f.y+a.groupSize(!0)]:a.isXAxis?[f.x,a.top,f.x+a.groupSize(!0),a.top]:[f.x,a.top+a.len,f.x+a.groupSize(!0),a.top+a.len];v(k,n,p)}e&&a.leftl?0:l);const H=g.labelOffsets?.x||0,I=g.labelOffsets?.y||0,J=a.groupFontHeights?.[r]||0;q=this.getPosition(this.axis.horiz,h?Math.max(g.startAt-1,h-1):g.startAt-1,this.axis.tickmarkOffset);n=this.getPosition(this.axis.horiz,d?Math.min(g.startAt+g.leaves-1-l,d):g.startAt+g.leaves-1-l,this.axis.tickmarkOffset);f=a.groupSize(r); +l=e&&n.x===a.pos+a.len||!e&&n.y===a.pos?-1:0;F(a,g,g.leaves||1,r);q=e?{x:(q.x+n.x)/2+H,y:m+J+f/2+I/2}:{x:m+f/2+H,y:(q.y+n.y)/2+J+I};isNaN(q.x)||isNaN(q.y)||(g.label?.attr(q),k&&(e&&a.left { - const horiz = axis.horiz; - const categoriesLength = axis.categories?.length || 1; - let groupSlotWidth = (axis.width / categoriesLength) * leaves; - - if (groupedTick.label && !horiz) { - const width = axis.groupSize(depth, groupedTick.label.getBBox().width); - groupSlotWidth = width < 0 ? width * -1 : width; - } - - if (axis.options.labels.step === 1 && groupedTick.label) { - if (groupSlotWidth < 15) { - groupedTick.label.css({ - display: 'none', - width: undefined, - textOverflow: undefined - }); - } else if ( - groupedTick.rotation !== -90 && - groupedTick.rotation !== 90 && - groupedTick.label && - ( - groupedTick.label.getBBox().width > groupSlotWidth || + if (groupedTick.label) { + const horiz = axis.horiz; + const categoriesLength = axis.categories?.length || 1; + const labelHeight = fontMetrics(groupedTick.label?.styles.fontSize || 0, axis.chart).h; + const groupSlotHeight = horiz ? + Math.abs(axis.groupSize(depth, groupedTick.label.getBBox().height)) : + (axis.height / categoriesLength) * leaves; + const groupSlotWidth = horiz ? + (axis.width / categoriesLength) * leaves : + Math.abs(axis.groupSize(depth, groupedTick.label.getBBox().width)); + + if (axis.options.labels.step === 1) { + // Handle width case, #220 + if (groupSlotWidth < 15) { + groupedTick.label.css({ + display: 'none', + width: undefined, + textOverflow: undefined + }); + } else if ( + groupedTick.rotation !== -90 && + groupedTick.rotation !== 90 && + groupedTick.label && ( - groupedTick.label.getBBox().width === 0 && - groupedTick.label.styles.width && - groupSlotWidth === +groupedTick.label.styles.width + groupedTick.label.getBBox().width > groupSlotWidth || + ( + groupedTick.label.getBBox().width === 0 && + groupedTick.label.styles.width && + groupSlotWidth === +groupedTick.label.styles.width + ) ) - ) - ) { - groupedTick.label.css({ - display: 'block', - width: groupSlotWidth.toString(), - textOverflow: 'ellipsis' - }); - } else if ( - groupedTick.label.styles.textOverflow !== 'ellipsis' || - (groupedTick.label.styles.width && groupSlotWidth > +groupedTick.label.styles.width) - ) { - groupedTick.label.css({ - display: 'block', - width: undefined, - textOverflow: undefined - }); + ) { + groupedTick.label.css({ + display: 'block', + width: groupSlotWidth.toString(), + textOverflow: 'ellipsis' + }); + } else if ( + groupedTick.label.styles.textOverflow !== 'ellipsis' || + (groupedTick.label.styles.width && groupSlotWidth > +groupedTick.label.styles.width) + ) { + groupedTick.label.css({ + display: 'block', + width: undefined, + textOverflow: undefined + }); + } + + // Handle height case, #177 + if (labelHeight > groupSlotHeight) { + groupedTick.label.css({ + display: 'none', + width: undefined, + textOverflow: undefined + }); + } } } }; From 2bf3a3fe3b34bd8f443abec69c50ef93d9bae204 Mon Sep 17 00:00:00 2001 From: wchmiel Date: Thu, 2 Oct 2025 10:38:14 +0200 Subject: [PATCH 15/16] Fixed #163, duplicated labels after update when useHTML enabled --- CHANGELOG.md | 1 + bug5.html | 82 ++++++++++++++++++++++++++++++ dist/grouped-categories.js | 9 +++- dist/grouped-categories.min.js | 4 +- dist/grouped-categories.min.js.map | 2 +- ts/groupedCategories.ts | 11 +++- 6 files changed, 102 insertions(+), 7 deletions(-) create mode 100644 bug5.html diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f7f3bb..3ad7f35 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ All notable changes to this project will be documented in this file. ### Fixed - #111 +- #163 - #177 - #179 - #181 diff --git a/bug5.html b/bug5.html new file mode 100644 index 0000000..13b9926 --- /dev/null +++ b/bug5.html @@ -0,0 +1,82 @@ + + + + + + + +Grouped Categories - Highcharts module + + + + + + + +
+ +
+ +
+ + + + + + diff --git a/dist/grouped-categories.js b/dist/grouped-categories.js index 22edf3e..e888d4c 100644 --- a/dist/grouped-categories.js +++ b/dist/grouped-categories.js @@ -285,7 +285,7 @@ axisProto.render = function () { } if ((axis.min && tick.startAt + tick.leaves - 1 < axis.min) || (axis.max && tick.startAt > axis.max)) { tick.label?.hide(); - tick.destroyed = 0; + tick.destroyed = 0; // TODO do we need this? } else { tick.label?.attr({ visibility: visible ? 'visible' : 'hidden' }); @@ -542,9 +542,14 @@ tickProto.render = function (index, old, opacity) { }; tickProto.destroy = function () { const tick = this; + const useHTML = tick.axis.options.labels.useHTML; let group = tick.parent; while (group) { - group.destroyed = group.destroyed ? (group.destroyed + 1) : 1; + group.destroyed = group.destroyed ? (group.destroyed + 1) : 1; // TODO do we need this? + // Destroy label if it's HTML, #163 + if (useHTML && group.label?.element) { + group.label?.destroy(); + } group = group.parent; } protoTickDestroy.call(tick); diff --git a/dist/grouped-categories.min.js b/dist/grouped-categories.min.js index a45838e..28a1c88 100644 --- a/dist/grouped-categories.min.js +++ b/dist/grouped-categories.min.js @@ -14,8 +14,8 @@ this.label.getBBox().width}a.isGrouped&&a.options.labels.enabled&&!w(c)&&this.ad d.formatter.call(m,m):a.name;var l=k&&k[q-1];m=l?x(p,k[q-1]):p;l=l&&k[q-1].style?x(e,k[q-1].style):e;delete m.style;(r=c.renderer.text(r,0,0,h).attr(m).add(b.labelGroup))&&!c.styledMode&&r.css(l);g.startAt=this.pos;g.childCount=(a.categories||[]).length;g.leaves=a.leaves;g.visible=!!g.childCount;g.label=r;g.labelOffsets={x:m.x,y:m.y};a.tick=g}g&&g.label&&b.groupSize(q,g.label.getBBox()[f]);g=(a=a?.parent)?g.parent=a.tick||{}:void 0;q++}};u.render=function(a,b,c){W.call(this,a,b,c);a=this.axis;b=a.categories&& a.categories[this.pos];if(a.isGrouped&&b&&!(a.max&&this.pos>a.max)){c=this.isFirst;var d=a.max,h=a.min,e=a.horiz,k=a.labelsGridPath,p=a.tickWidth||0,f=this.getPosition(this.axis.horiz,this.pos,this.axis.tickmarkOffset),q=e?f.y:f.x,g=this,m=a.groupSize(0),r=1,l=e&&f.x===a.pos+a.len||!e&&f.y===a.pos?-1:0;if(c){var n=e?[a.left,f.y,a.left,f.y+a.groupSize(!0)]:a.isXAxis?[f.x,a.top,f.x+a.groupSize(!0),a.top]:[f.x,a.top+a.len,f.x+a.groupSize(!0),a.top+a.len];v(k,n,p)}e&&a.leftl?0:l);const H=g.labelOffsets?.x||0,I=g.labelOffsets?.y||0,J=a.groupFontHeights?.[r]||0;q=this.getPosition(this.axis.horiz,h?Math.max(g.startAt-1,h-1):g.startAt-1,this.axis.tickmarkOffset);n=this.getPosition(this.axis.horiz,d?Math.min(g.startAt+g.leaves-1-l,d):g.startAt+g.leaves-1-l,this.axis.tickmarkOffset);f=a.groupSize(r); -l=e&&n.x===a.pos+a.len||!e&&n.y===a.pos?-1:0;F(a,g,g.leaves||1,r);q=e?{x:(q.x+n.x)/2+H,y:m+J+f/2+I/2}:{x:m+f/2+H,y:(q.y+n.y)/2+J+I};isNaN(q.x)||isNaN(q.y)||(g.label?.attr(q),k&&(e&&a.left axis.max)) { tick.label?.hide(); - tick.destroyed = 0; + tick.destroyed = 0; // TODO do we need this? } else { tick.label?.attr({ visibility: visible ? 'visible' : 'hidden' }); } @@ -692,10 +692,17 @@ tickProto.render = function (index: number, old?: boolean, opacity?: number): vo tickProto.destroy = function (): void { const tick = this; + const useHTML = tick.axis.options.labels.useHTML; let group = tick.parent; while (group) { - group.destroyed = group.destroyed ? (group.destroyed + 1) : 1; + group.destroyed = group.destroyed ? (group.destroyed + 1) : 1; // TODO do we need this? + + // Destroy label if it's HTML, #163 + if (useHTML && group.label?.element) { + group.label?.destroy(); + } + group = group.parent; } From 29fa585d4988dc73661c23a5ac1c46bdc8277323 Mon Sep 17 00:00:00 2001 From: wchmiel Date: Fri, 3 Oct 2025 11:17:23 +0200 Subject: [PATCH 16/16] Fixed #144, first tick grid line for reversed axis --- CHANGELOG.md | 4 ++++ bug2.html | 5 ++++- bug4.html | 14 ++++++++------ dist/grouped-categories.js | 18 ++++++++++++------ dist/grouped-categories.min.js | 28 ++++++++++++++-------------- dist/grouped-categories.min.js.map | 2 +- ts/groupedCategories.ts | 17 +++++++++++------ 7 files changed, 54 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ad7f35..7225562 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,10 @@ All notable changes to this project will be documented in this file. ### Fixed - #111 +- #144 +- #148 +- #149 +- #150 - #163 - #177 - #179 diff --git a/bug2.html b/bug2.html index 6994dd2..0fe2062 100644 --- a/bug2.html +++ b/bug2.html @@ -7,7 +7,8 @@ Grouped Categories - Highcharts module - + + @@ -31,6 +32,8 @@ series: [{ // data: [4, 14, 18, 5, 6, 5, 14, 15, 18] data: [4, 14, 18, 5, 6, 5, 14, 15, 18, 10, 11, 12] + }, { + data: [5, 12, 18, 5, 7, 5, 11, 19, 15, 11, 14, 10] }], xAxis: { labels: { diff --git a/bug4.html b/bug4.html index 7fd075d..b9bb750 100644 --- a/bug4.html +++ b/bug4.html @@ -15,7 +15,7 @@
-
+
@@ -24,23 +24,25 @@ window.chart = new Highcharts.Chart({ chart: { - inverted: false, + inverted: true, renderTo: "chart", type: "column" }, series: [ { - data: [4, 14, 18, 5, 6, 5, 14, 15, 18, 4, 14, 18, 5, 6, 5, 14, 15, 18] + data: [4, 14, 18, 5, 6, 5, 14, 15, 18] + // data: [4, 14, 18, 5, 6, 5, 14, 15, 18, 4, 14, 18, 5, 6, 5, 14, 15, 18] } ], xAxis: { + reversed: false, categories: [ { name: "Fruit", categories: ["Apple", "Banana", "Orange"] }, { name: "Vegetable", categories: ["Carrot", "Potato", "Tomato"] }, { name: "Fish", categories: ["Cod", "Salmon", "Tuna"] }, - { name: "Fruit2", categories: ["Apple2", "Banana2", "Orange2"] }, - { name: "Vegetable2", categories: ["Carrot2", "Potato2", "Tomato2"] }, - { name: "Fish2", categories: ["Cod2", "Salmon2", "Tuna2"] } + // { name: "Fruit2", categories: ["Apple2", "Banana2", "Orange2"] }, + // { name: "Vegetable2", categories: ["Carrot2", "Potato2", "Tomato2"] }, + // { name: "Fish2", categories: ["Cod2", "Salmon2", "Tuna2"] } ] } }); diff --git a/dist/grouped-categories.js b/dist/grouped-categories.js index e888d4c..bde495a 100644 --- a/dist/grouped-categories.js +++ b/dist/grouped-categories.js @@ -483,14 +483,20 @@ tickProto.render = function (index, old, opacity) { let maxPos; let attrs; if (isFirst) { - gridAttrs = horiz ? - [axis.left, xy.y, axis.left, xy.y + axis.groupSize(true)] : - axis.isXAxis ? - [xy.x, axis.top, xy.x + axis.groupSize(true), axis.top] : + // Grid part for first tick, handles reversed axis, #144 + if (horiz) { + const gridX = axis.reversed ? axis.pos + axis.len : axis.left; + gridAttrs = [gridX, xy.y, gridX, xy.y + axis.groupSize(true)]; + } + else { + const gridY = axis.reversed ? axis.top : axis.pos + axis.len; + gridAttrs = axis.isXAxis ? + [xy.x, gridY, xy.x + axis.groupSize(true), gridY] : [xy.x, axis.top + axis.len, xy.x + axis.groupSize(true), axis.top + axis.len]; + } addGridPart(grid, gridAttrs, tickWidth); } - if (horiz && axis.left < xy.x) { + if (horiz && axis.left <= xy.x) { addGridPart(grid, [xy.x - reverseCrisp, xy.y, xy.x - reverseCrisp, xy.y + size], tickWidth); } else if (!horiz && axis.top <= xy.y) { @@ -528,7 +534,7 @@ tickProto.render = function (index, old, opacity) { if (!isNaN(attrs.x) && !isNaN(attrs.y)) { group.label?.attr(attrs); if (grid) { - if (horiz && axis.left < maxPos.x) { + if (horiz && axis.left <= maxPos.x) { addGridPart(grid, [maxPos.x - reverseCrisp, size, maxPos.x - reverseCrisp, size + lvlSize], tickWidth); } else if (!horiz && axis.top <= maxPos.y) { diff --git a/dist/grouped-categories.min.js b/dist/grouped-categories.min.js index 28a1c88..4ed90e1 100644 --- a/dist/grouped-categories.min.js +++ b/dist/grouped-categories.min.js @@ -1,21 +1,21 @@ (function(){ (function(t){"object"===typeof module&&module.exports?module.exports=t:t(Highcharts)})(function(t){const {merge:x,pick:z,objectEach:K,isNumber:y,isObject:B,isString:w,pInt:L,format:M,Tick:N,Axis:O,SVGElement:P}=t,C=a=>a.reduce((b,c)=>b+c,0),A=(a,b,c)=>{for(let d=a.length-1;0<=d;d--){const h=a[d][b];h&&A(h,b,c);c(a[d])}};class Q{constructor(a,b){this.userOptions=JSON.parse(JSON.stringify(a));this.name="string"===typeof a?a:a.name||"";this.parent=b}toString(){const a=[];let b=this;for(;b;)a.push(b.name), -b=b.parent;return a.join(", ")}}const D=(a,b,c,d,h=0)=>{c.depth=c.depth||0;for(let k=a.length-1;0<=k;k--){const p=a[k];if("object"===typeof p&&p.categories)d&&(p.parent=d),D(p.categories,b,c,p,h+1);else{var e=d;for(b.unshift(new Q(p,e));e;)e.leaves=(e.leaves||0)+1,e=e.parent}}c.depth=Math.max(c.depth,h)},v=(a,b,c)=>{b[0]===b[2]&&(b[0]=b[2]=Math.round(b[0])-c%2/2);b[1]===b[3]&&(b[1]=b[3]=Math.round(b[1])+c%2/2);a.push("M",b[0],b[1],"L",b[2],b[3])},E=(a,b,c)=>{a=(b?.renderer.styledMode||w(a)&&a.includes("px"))&& -window.getComputedStyle?c&&P.prototype.getStyle.call(c,"font-size"):a||c?.styles.fontSize||b?.renderer.style.fontSize;if(w(a)&&a.includes("px"))a=L(a);else if(!y(a)||isNaN(a))a=12;b=24>a?a+3:Math.round(1.2*a);return{h:b,b:Math.round(.8*b),f:a}},F=(a,b,c,d)=>{if(b.label){const h=a.horiz,e=a.categories?.length||1,k=E(b.label?.styles.fontSize||0,a.chart).h,p=h?Math.abs(a.groupSize(d,b.label.getBBox().height)):a.height/e*c;c=h?a.width/e*c:Math.abs(a.groupSize(d,b.label.getBBox().width));1===a.options.labels.step&& -(15>c?b.label.css({display:"none",width:void 0,textOverflow:void 0}):-90!==b.rotation&&90!==b.rotation&&b.label&&(b.label.getBBox().width>c||0===b.label.getBBox().width&&b.label.styles.width&&c===+b.label.styles.width)?b.label.css({display:"block",width:c.toString(),textOverflow:"ellipsis"}):("ellipsis"!==b.label.styles.textOverflow||b.label.styles.width&&c>+b.label.styles.width)&&b.label.css({display:"block",width:void 0,textOverflow:void 0}),k>p&&b.label.css({display:"none",width:void 0,textOverflow:void 0}))}}; -t=O.prototype;const u=N.prototype,R=t.init,S=t.render,T=t.setCategories,G=u.getLabelSize,U=u.addLabel,V=u.destroy,W=u.render,X=u.replaceMovedLabel;t.init=function(a,b,c){"object"===typeof b&&b.categories&&(b=x(b,{labels:{step:1}}));R.call(this,a,b,c);B(b)&&b.categories&&this.setupGroups(b)};t.setupGroups=function(a){const b=this.chart;var c=JSON.parse(JSON.stringify(a.categories||[]));const d=[],h={depth:0};var e=this.options.labels;const k=e&&e.groupedOptions;e=e&&e.style;D(c,d,h);if(this.isGrouped= -0!==h.depth)for(this.categoriesTree=c,this.categories=d,this.labelsDepth=h.depth,this.labelsSizes=[],this.labelsGridPath=[],this.tickLength=a.tickLength||this.tickLength||null,this.tickWidth=z(a.tickWidth,this.isXAxis?1:0),this.directionFactor=[-1,1,1,-1][this.side],this.options.lineWidth=z(a.lineWidth,1),this.groupFontHeights=[],a=0;a<=h.depth;a++)c=k&&k[a-1]&&k[a-1].style?x(e,k[a-1].style):e,this.groupFontHeights[a]=Math.round(.3*E(c.fontSize?c.fontSize:0,b).b)};t.render=function(){const a=this; -a.isGrouped&&(a.labelsGridPath=[]);void 0===a.originalTickLength&&(a.originalTickLength=a.options.tickLength);a.options.tickLength=a.isGrouped?.001:a.originalTickLength;S.call(a);if(!a.isGrouped)return a.labelsGrid&&a.labelsGrid.attr({visibility:"hidden"}),!1;var b=a.options;const c=a.top,d=a.left,h=d+a.width,e=c+a.height,k=a.hasVisibleSeries||a.hasData();let p=a.labelsDepth||0,f=a.labelsGrid;const q=a.horiz,g=a.labelsGridPath;let m=!1===b.drawHorizontalBorders?p+1:0,r=a.opposite?q?c:h:q?e:d;const l= -a.tickWidth||0;a.userTickLength&&--p;f||(f=a.labelsGrid=a.chart.renderer.path().attr({strokeWidth:l,"stroke-width":l,stroke:b.tickColor||""}).add(a.axisGroup),b.tickColor||f.addClass("highcharts-tick"));for(;m<=p;)r+=a.groupSize(m),b=q?[d,r,h,r]:[r,c,r,e],v(g,b,l),m++;f.attr({d:g,visibility:k?"visible":"hidden"});a.labelGroup?.attr({visibility:k?"visible":"hidden"});A(a.categoriesTree||[],"categories",n=>{n=n.tick;if(!n)return!1;a.min&&n.startAt+n.leaves-1a.max?(n.label?.hide(), -n.destroyed=0):n.label?.attr({visibility:k?"visible":"hidden"});return!0});return!0};t.setCategories=function(a,b){this.categories&&this.cleanGroups();this.setupGroups({categories:a});this.categories=this.userOptions.categories=a;this.categories.every(c=>w(c))&&T.call(this,this.categories,b)};t.cleanGroups=function(){const a=this.ticks;for(const b in a)a[b].parent&&delete a[b].parent;A(this.categoriesTree||[],"categories",b=>{const c=b.tick;if(!c)return!1;c.label?.destroy();K(c,(d,h)=>delete c[h]); +b=b.parent;return a.join(", ")}}const D=(a,b,c,d,h=0)=>{c.depth=c.depth||0;for(let l=a.length-1;0<=l;l--){const p=a[l];if("object"===typeof p&&p.categories)d&&(p.parent=d),D(p.categories,b,c,p,h+1);else{var e=d;for(b.unshift(new Q(p,e));e;)e.leaves=(e.leaves||0)+1,e=e.parent}}c.depth=Math.max(c.depth,h)},v=(a,b,c)=>{b[0]===b[2]&&(b[0]=b[2]=Math.round(b[0])-c%2/2);b[1]===b[3]&&(b[1]=b[3]=Math.round(b[1])+c%2/2);a.push("M",b[0],b[1],"L",b[2],b[3])},E=(a,b,c)=>{a=(b?.renderer.styledMode||w(a)&&a.includes("px"))&& +window.getComputedStyle?c&&P.prototype.getStyle.call(c,"font-size"):a||c?.styles.fontSize||b?.renderer.style.fontSize;if(w(a)&&a.includes("px"))a=L(a);else if(!y(a)||isNaN(a))a=12;b=24>a?a+3:Math.round(1.2*a);return{h:b,b:Math.round(.8*b),f:a}},F=(a,b,c,d)=>{if(b.label){const h=a.horiz,e=a.categories?.length||1,l=E(b.label?.styles.fontSize||0,a.chart).h,p=h?Math.abs(a.groupSize(d,b.label.getBBox().height)):a.height/e*c;c=h?a.width/e*c:Math.abs(a.groupSize(d,b.label.getBBox().width));1===a.options.labels.step&& +(15>c?b.label.css({display:"none",width:void 0,textOverflow:void 0}):-90!==b.rotation&&90!==b.rotation&&b.label&&(b.label.getBBox().width>c||0===b.label.getBBox().width&&b.label.styles.width&&c===+b.label.styles.width)?b.label.css({display:"block",width:c.toString(),textOverflow:"ellipsis"}):("ellipsis"!==b.label.styles.textOverflow||b.label.styles.width&&c>+b.label.styles.width)&&b.label.css({display:"block",width:void 0,textOverflow:void 0}),l>p&&b.label.css({display:"none",width:void 0,textOverflow:void 0}))}}; +t=O.prototype;const u=N.prototype,R=t.init,S=t.render,T=t.setCategories,G=u.getLabelSize,U=u.addLabel,V=u.destroy,W=u.render,X=u.replaceMovedLabel;t.init=function(a,b,c){"object"===typeof b&&b.categories&&(b=x(b,{labels:{step:1}}));R.call(this,a,b,c);B(b)&&b.categories&&this.setupGroups(b)};t.setupGroups=function(a){const b=this.chart;var c=JSON.parse(JSON.stringify(a.categories||[]));const d=[],h={depth:0};var e=this.options.labels;const l=e&&e.groupedOptions;e=e&&e.style;D(c,d,h);if(this.isGrouped= +0!==h.depth)for(this.categoriesTree=c,this.categories=d,this.labelsDepth=h.depth,this.labelsSizes=[],this.labelsGridPath=[],this.tickLength=a.tickLength||this.tickLength||null,this.tickWidth=z(a.tickWidth,this.isXAxis?1:0),this.directionFactor=[-1,1,1,-1][this.side],this.options.lineWidth=z(a.lineWidth,1),this.groupFontHeights=[],a=0;a<=h.depth;a++)c=l&&l[a-1]&&l[a-1].style?x(e,l[a-1].style):e,this.groupFontHeights[a]=Math.round(.3*E(c.fontSize?c.fontSize:0,b).b)};t.render=function(){const a=this; +a.isGrouped&&(a.labelsGridPath=[]);void 0===a.originalTickLength&&(a.originalTickLength=a.options.tickLength);a.options.tickLength=a.isGrouped?.001:a.originalTickLength;S.call(a);if(!a.isGrouped)return a.labelsGrid&&a.labelsGrid.attr({visibility:"hidden"}),!1;var b=a.options;const c=a.top,d=a.left,h=d+a.width,e=c+a.height,l=a.hasVisibleSeries||a.hasData();let p=a.labelsDepth||0,f=a.labelsGrid;const q=a.horiz,g=a.labelsGridPath;let n=!1===b.drawHorizontalBorders?p+1:0,r=a.opposite?q?c:h:q?e:d;const m= +a.tickWidth||0;a.userTickLength&&--p;f||(f=a.labelsGrid=a.chart.renderer.path().attr({strokeWidth:m,"stroke-width":m,stroke:b.tickColor||""}).add(a.axisGroup),b.tickColor||f.addClass("highcharts-tick"));for(;n<=p;)r+=a.groupSize(n),b=q?[d,r,h,r]:[r,c,r,e],v(g,b,m),n++;f.attr({d:g,visibility:l?"visible":"hidden"});a.labelGroup?.attr({visibility:l?"visible":"hidden"});A(a.categoriesTree||[],"categories",k=>{k=k.tick;if(!k)return!1;a.min&&k.startAt+k.leaves-1a.max?(k.label?.hide(), +k.destroyed=0):k.label?.attr({visibility:l?"visible":"hidden"});return!0});return!0};t.setCategories=function(a,b){this.categories&&this.cleanGroups();this.setupGroups({categories:a});this.categories=this.userOptions.categories=a;this.categories.every(c=>w(c))&&T.call(this,this.categories,b)};t.cleanGroups=function(){const a=this.ticks;for(const b in a)a[b].parent&&delete a[b].parent;A(this.categoriesTree||[],"categories",b=>{const c=b.tick;if(!c)return!1;c.label?.destroy();K(c,(d,h)=>delete c[h]); delete b.tick;return!0});this.labelsGrid=null};t.groupSize=function(a,b){const c=this.labelsSizes||[],d=this.directionFactor||1,h=this.options.labels&&this.options.labels.groupedOptions&&y(a)?this.options.labels.groupedOptions[a-1]:!1;let e=0;h&&(e=-1===d?h.x||0:h.y||0);y(a)&&void 0!==b&&(c[a]=Math.max(c[a]||0,b+10+Math.abs(e)));return!0===a?C(c)*d:y(a)&&c[a]?c[a]*d:0};u.addLabel=function(){const a=this.axis;var b=z(this.options&&this.options.labels,a.options.labels);let c;a.topLabelSize=0;U.call(this); if(!a.isGrouped||!a.categories||!(c=a.categories[this.pos]))return!1;if(this.label){var d=this.label,h=d.attr;var e={axis:a,tick:this,chart:a.chart,isFirst:!!this.isFirst,isLast:!!this.isLast,value:B(c)?c.name:c,pos:this.pos};b.formatter?b=b.formatter.call(e,e):b.format?(e.text=a.defaultLabelFormatter.call(e),b=M(b.format,e,a.chart)):b=a.defaultLabelFormatter.call(e);h.call(d,"text",b);d=Math.abs(this.label.rotation||0);d=180d?this.label.getBBox().height: -this.label.getBBox().width}a.isGrouped&&a.options.labels.enabled&&!w(c)&&this.addGroupedLabels(c);return!0};u.addGroupedLabels=function(a){const b=this.axis,c=b.chart,d=b.options.labels,h=d.useHTML,e=d.style,k=d.groupedOptions,p={align:"center",rotation:d.rotation,x:0,y:0,style:void 0},f=b.horiz?"height":"width";let q=0;let g=this;for(;g;){if(a&&0a.max)){c=this.isFirst;var d=a.max,h=a.min,e=a.horiz,k=a.labelsGridPath,p=a.tickWidth||0,f=this.getPosition(this.axis.horiz,this.pos,this.axis.tickmarkOffset),q=e?f.y:f.x,g=this,m=a.groupSize(0),r=1,l=e&&f.x===a.pos+a.len||!e&&f.y===a.pos?-1:0;if(c){var n=e?[a.left,f.y,a.left,f.y+a.groupSize(!0)]:a.isXAxis?[f.x,a.top,f.x+a.groupSize(!0),a.top]:[f.x,a.top+a.len,f.x+a.groupSize(!0),a.top+a.len];v(k,n,p)}e&&a.leftl?0:l);const H=g.labelOffsets?.x||0,I=g.labelOffsets?.y||0,J=a.groupFontHeights?.[r]||0;q=this.getPosition(this.axis.horiz,h?Math.max(g.startAt-1,h-1):g.startAt-1,this.axis.tickmarkOffset);n=this.getPosition(this.axis.horiz,d?Math.min(g.startAt+g.leaves-1-l,d):g.startAt+g.leaves-1-l,this.axis.tickmarkOffset);f=a.groupSize(r); -l=e&&n.x===a.pos+a.len||!e&&n.y===a.pos?-1:0;F(a,g,g.leaves||1,r);q=e?{x:(q.x+n.x)/2+H,y:m+J+f/2+I/2}:{x:m+f/2+H,y:(q.y+n.y)/2+J+I};isNaN(q.x)||isNaN(q.y)||(g.label?.attr(q),k&&(e&&a.lefta.max)){c=this.isFirst;var d=a.max,h=a.min,e=a.horiz,l=a.labelsGridPath,p=a.tickWidth||0,f=this.getPosition(this.axis.horiz,this.pos,this.axis.tickmarkOffset),q=e?f.y:f.x,g=this,n=a.groupSize(0),r=1,m=e&&f.x===a.pos+a.len||!e&&f.y===a.pos?-1:0;if(c){if(e){var k=a.reversed?a.pos+a.len:a.left;k=[k,f.y,k,f.y+a.groupSize(!0)]}else k=a.reversed?a.top:a.pos+a.len,k=a.isXAxis?[f.x,k,f.x+a.groupSize(!0),k]:[f.x,a.top+a.len,f.x+a.groupSize(!0),a.top+ +a.len];v(l,k,p)}e&&a.left<=f.x?v(l,[f.x-m,f.y,f.x-m,f.y+n],p):!e&&a.top<=f.y&&v(l,[f.x,f.y+m,f.x+n,f.y+m],p);n=q+n;for(F(a,this,this.leaves||1,0);g.parent;){g=g.parent;m=0;c&&!w(b)&&(m=(b.parent?.categories||[]).indexOf(b.name),m=0>m?0:m);const H=g.labelOffsets?.x||0,I=g.labelOffsets?.y||0,J=a.groupFontHeights?.[r]||0;q=this.getPosition(this.axis.horiz,h?Math.max(g.startAt-1,h-1):g.startAt-1,this.axis.tickmarkOffset);k=this.getPosition(this.axis.horiz,d?Math.min(g.startAt+g.leaves-1-m,d):g.startAt+ +g.leaves-1-m,this.axis.tickmarkOffset);f=a.groupSize(r);m=e&&k.x===a.pos+a.len||!e&&k.y===a.pos?-1:0;F(a,g,g.leaves||1,r);q=e?{x:(q.x+k.x)/2+H,y:n+J+f/2+I/2}:{x:n+f/2+H,y:(q.y+k.y)/2+J+I};isNaN(q.x)||isNaN(q.y)||(g.label?.attr(q),l&&(e&&a.left<=k.x?v(l,[k.x-m,n,k.x-m,n+f],p):!e&&a.top<=k.y&&v(l,[n,k.y+m,n+f,k.y+m],p)));n+=f;r++}}};u.destroy=function(){const a=this.axis.options.labels.useHTML;let b=this.parent;for(;b;)b.destroyed=b.destroyed?b.destroyed+1:1,a&&b.label?.element&&b.label?.destroy(), +b=b.parent;V.call(this)};u.getLabelSize=function(){const a=this.axis;if(!a.isGrouped)return G.call(this);a.labelsSizes||(a.labelsSizes=[]);const b=a.options.labels.distance||8,c=a.labelsSizes[0]||0,d=G.call(this)+2*b;c