UNPKG

highcharts

Version:
257 lines (256 loc) 9.02 kB
/* * * * Highcharts funnel3d series module * * (c) 2010-2025 Highsoft AS * * Author: Kacper Madej * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * */ 'use strict'; import Funnel3DComposition from './Funnel3DComposition.js'; import Funnel3DSeriesDefaults from './Funnel3DSeriesDefaults.js'; import Funnel3DPoint from './Funnel3DPoint.js'; import H from '../../Core/Globals.js'; const { noop } = H; import Math3D from '../../Core/Math3D.js'; const { perspective } = Math3D; import SeriesRegistry from '../../Core/Series/SeriesRegistry.js'; const { series: Series, seriesTypes: { column: ColumnSeries } } = SeriesRegistry; import U from '../../Core/Utilities.js'; const { extend, merge, pick, relativeLength } = U; /* * * * Class * * */ /** * The funnel3d series type. * * @private * @class * @name Highcharts.seriesTypes.funnel3d * @augments seriesTypes.column * @requires highcharts-3d * @requires modules/cylinder * @requires modules/funnel3d */ class Funnel3DSeries extends ColumnSeries { /* * * * Functions * * */ /** * @private */ alignDataLabel(point, _dataLabel, options) { const series = this, dlBoxRaw = point.dlBoxRaw, inverted = series.chart.inverted, below = point.plotY > pick(series.translatedThreshold, series.yAxis.len), inside = pick(options.inside, !!series.options.stacking), dlBox = { x: dlBoxRaw.x, y: dlBoxRaw.y, height: 0 }; options.align = pick(options.align, !inverted || inside ? 'center' : below ? 'right' : 'left'); options.verticalAlign = pick(options.verticalAlign, inverted || inside ? 'middle' : below ? 'top' : 'bottom'); if (options.verticalAlign !== 'top') { dlBox.y += dlBoxRaw.bottom / (options.verticalAlign === 'bottom' ? 1 : 2); } dlBox.width = series.getWidthAt(dlBox.y); if (series.options.reversed) { dlBox.width = dlBoxRaw.fullWidth - dlBox.width; } if (inside) { dlBox.x -= dlBox.width / 2; } else { // Swap for inside if (options.align === 'left') { options.align = 'right'; dlBox.x -= dlBox.width * 1.5; } else if (options.align === 'right') { options.align = 'left'; dlBox.x += dlBox.width / 2; } else { dlBox.x -= dlBox.width / 2; } } point.dlBox = dlBox; ColumnSeries.prototype.alignDataLabel.apply(series, arguments); } /** * Override default axis options with series required options for axes. * @private */ bindAxes() { Series.prototype.bindAxes.apply(this, arguments); extend(this.xAxis.options, { gridLineWidth: 0, lineWidth: 0, title: void 0, tickPositions: [] }); merge(true, this.yAxis.options, { gridLineWidth: 0, title: void 0, labels: { enabled: false } }); } /** * @private */ translate() { Series.prototype.translate.apply(this, arguments); const series = this, chart = series.chart, options = series.options, reversed = options.reversed, ignoreHiddenPoint = options.ignoreHiddenPoint, plotWidth = chart.plotWidth, plotHeight = chart.plotHeight, center = options.center, centerX = relativeLength(center[0], plotWidth), centerY = relativeLength(center[1], plotHeight), width = relativeLength(options.width, plotWidth), height = relativeLength(options.height, plotHeight), neckWidth = relativeLength(options.neckWidth, plotWidth), neckHeight = relativeLength(options.neckHeight, plotHeight), neckY = (centerY - height / 2) + height - neckHeight, points = series.points; let sum = 0, cumulative = 0, // Start at top tempWidth, getWidthAt, fraction, tooltipPos, // y1, y3, y5, // h, shapeArgs; // @todo: Type it. It's an extended SVGAttributes. // Return the width at a specific y coordinate series.getWidthAt = getWidthAt = function (y) { const top = (centerY - height / 2); return (y > neckY || height === neckHeight) ? neckWidth : neckWidth + (width - neckWidth) * (1 - (y - top) / (height - neckHeight)); }; // Expose series.center = [centerX, centerY, height]; series.centerX = centerX; /* * Individual point coordinate naming: * * _________centerX,y1________ * \ / * \ / * \ / * \ / * \ / * ___centerX,y3___ * * Additional for the base of the neck: * * | | * | | * | | * ___centerX,y5___ */ // get the total sum for (const point of points) { if (!ignoreHiddenPoint || point.visible !== false) { sum += point.y; } } for (const point of points) { // Set start and end positions y5 = null; fraction = sum ? point.y / sum : 0; y1 = centerY - height / 2 + cumulative * height; y3 = y1 + fraction * height; tempWidth = getWidthAt(y1); h = y3 - y1; shapeArgs = { // For fill setter gradientForSides: pick(point.options.gradientForSides, options.gradientForSides), x: centerX, y: y1, height: h, width: tempWidth, z: 1, top: { width: tempWidth } }; tempWidth = getWidthAt(y3); shapeArgs.bottom = { fraction: fraction, width: tempWidth }; // The entire point is within the neck if (y1 >= neckY) { shapeArgs.isCylinder = true; } else if (y3 > neckY) { // The base of the neck y5 = y3; tempWidth = getWidthAt(neckY); y3 = neckY; shapeArgs.bottom.width = tempWidth; shapeArgs.middle = { fraction: h ? (neckY - y1) / h : 0, width: tempWidth }; } if (reversed) { shapeArgs.y = y1 = centerY + height / 2 - (cumulative + fraction) * height; if (shapeArgs.middle) { shapeArgs.middle.fraction = 1 - (h ? shapeArgs.middle.fraction : 0); } tempWidth = shapeArgs.width; shapeArgs.width = shapeArgs.bottom.width; shapeArgs.bottom.width = tempWidth; } point.shapeArgs = extend(point.shapeArgs, shapeArgs); // For tooltips and data labels context point.percentage = fraction * 100; point.plotX = centerX; if (reversed) { point.plotY = centerY + height / 2 - (cumulative + fraction / 2) * height; } else { point.plotY = (y1 + (y5 || y3)) / 2; } // Placement of tooltips and data labels in 3D tooltipPos = perspective([{ x: centerX, y: point.plotY, z: reversed ? -(width - getWidthAt(point.plotY)) / 2 : -(getWidthAt(point.plotY)) / 2 }], chart, true)[0]; point.tooltipPos = [tooltipPos.x, tooltipPos.y]; // Base to be used when alignment options are known point.dlBoxRaw = { x: centerX, width: getWidthAt(point.plotY), y: y1, bottom: shapeArgs.height || 0, fullWidth: width }; if (!ignoreHiddenPoint || point.visible !== false) { cumulative += fraction; } } } } /* * * * Static Properties * * */ Funnel3DSeries.compose = Funnel3DComposition.compose; Funnel3DSeries.defaultOptions = merge(ColumnSeries.defaultOptions, Funnel3DSeriesDefaults); extend(Funnel3DSeries.prototype, { pointClass: Funnel3DPoint, translate3dShapes: noop }); SeriesRegistry.registerSeriesType('funnel3d', Funnel3DSeries); /* * * * Default Export * * */ export default Funnel3DSeries;