1/* * 2 * 3 * (c) 2010-2024 Torstein Honsi 4 * 5 * License: www.highcharts.com/license 6 * 7 * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! 8 * 9 * */ 10'use strict'; 11import T from '../../Templating.js'; 12const { format } = T; 13import SeriesRegistry from '../../Series/SeriesRegistry.js'; 14const { series: Series } = SeriesRegistry; 15import U from '../../Utilities.js'; 16const { destroyObjectProperties, fireEvent, getAlignFactor, isNumber, pick } = U; 17/* * 18 * 19 * Class 20 * 21 * */ 22/** 23 * The class for stacks. Each stack, on a specific X value and either negative 24 * or positive, has its own stack item. 25 * @private 26 */ 27class StackItem { 28 /* * 29 * 30 * Constructor 31 * 32 * */ 33 constructor(axis, options, negativeValue, x, stackOption) { 34 const inverted = axis.chart.inverted, reversed = axis.reversed; 35 this.axis = axis; 36 // The stack goes to the left either if the stack has negative value 37 // or when axis is reversed. XOR operator. 38 const isNegative = (this.isNegative = !!negativeValue !== !!reversed); 39 // Save the options to be able to style the label 40 this.options = options = options || {}; 41 // Save the x value to be able to position the label later 42 this.x = x; 43 // Initialize total value 44 this.total = null; 45 this.cumulative = null; 46 // This will keep each points' extremes stored by series.index and point 47 // index 48 this.points = {}; 49 this.hasValidPoints = false; 50 // Save the stack option on the series configuration object, 51 // and whether to treat it as percent 52 this.stack = stackOption; 53 this.leftCliff = 0; 54 this.rightCliff = 0; 55 // The align options and text align varies on whether the stack is 56 // negative and if the chart is inverted or not. 57 // First test the user supplied value, then use the dynamic. 58 this.alignOptions = { 59 align: options.align || 60 (inverted ? (isNegative ? 'left' : 'right') : 'center'), 61 verticalAlign: options.verticalAlign || 62 (inverted ? 'middle' : isNegative ? 'bottom' : 'top'), 63 y: options.y, 64 x: options.x 65 }; 66 this.textAlign = 67 options.textAlign || 68 (inverted ? (!isNegative ? 'left' : 'right') : 'center'); 69 } 70 /* * 71 * 72 * Functions 73 * 74 * */ 75 /** 76 * @private 77 */ 78 destroy() { 79 destroyObjectProperties(this, this.axis); 80 } 81 /** 82 * Renders the stack total label and adds it to the stack label group. 83 * @private 84 */ 85 render(group) { 86 const chart = this.axis.chart, options = this.options, formatOption = options.format, 87 // Format the text in the label. 88 str = formatOption ? 89 format(formatOption, this, chart) : 90 options.formatter.call(this); 91 // Change the text to reflect the new total and set visibility to hidden 92 // in case the series is hidden 93 if (this.label) { 94 this.label.attr({ text: str, visibility: 'hidden' }); 95 } 96 else { 97 // Create new label 98 this.label = chart.renderer.label(str, null, void 0, options.shape, void 0, void 0, options.useHTML, false, 'stack-labels'); 99 const attr = { 100 r: options.borderRadius || 0, 101 text: str, 102 // Set default padding to 5 as it is in datalabels #12308 103 padding: pick(options.padding, 5), 104 visibility: 'hidden' // Hidden until setOffset is called 105 }; 106 if (!chart.styledMode) { 107 attr.fill = options.backgroundColor; 108 attr.stroke = options.borderColor; 109 attr['stroke-width'] = options.borderWidth; 110 this.label.css(options.style || {}); 111 } 112 this.label.attr(attr); 113 if (!this.label.added) { 114 this.label.add(group); // Add to the labels-group 115 } 116 } 117 // Rank it higher than data labels (#8742) 118 this.label.labelrank = chart.plotSizeY;
119 fireEvent(this, 'afterRender'); 120 } 121 /** 122 * Sets the offset that the stack has from the x value and repositions the 123 * label. 124 * @private 125 */ 126 setOffset(xOffset, width, boxBottom, boxTop, defaultX, xAxis) { 127 const { alignOptions, axis, label, options, textAlign } = this, chart = axis.chart, stackBox = this.getStackBox({ 128 xOffset, 129 width, 130 boxBottom, 131 boxTop, 132 defaultX, 133 xAxis 134 }), { verticalAlign } = alignOptions; 135 if (label && stackBox) { 136 const labelBox = label.getBBox(void 0, 0), padding = label.padding; 137 let isJustify = pick(options.overflow, 'justify') === 'justify', visible; 138 // Reset alignOptions property after justify #12337 139 alignOptions.x = options.x || 0; 140 alignOptions.y = options.y || 0; 141 // Calculate the adjusted Stack position, to take into consideration 142 // The size if the labelBox and vertical alignment as 143 // well as the text alignment. It's need to be done to work with 144 // default SVGLabel.align/justify methods. 145 const { x, y } = this.adjustStackPosition({ 146 labelBox, 147 verticalAlign, 148 textAlign 149 }); 150 stackBox.x -= x; 151 stackBox.y -= y; 152 // Align the label to the adjusted box. 153 label.align(alignOptions, false, stackBox); 154 // Check if label is inside the plotArea #12294 155 visible = chart.isInsidePlot(label.alignAttr.x + alignOptions.x + x, label.alignAttr.y + alignOptions.y + y); 156 if (!visible) { 157 isJustify = false; 158 } 159 if (isJustify) { 160 // Justify stackLabel into the alignBox 161 Series.prototype.justifyDataLabel.call(axis, label, alignOptions, label.alignAttr, labelBox, stackBox); 162 } 163 // Add attr to avoid the default animation of justifyDataLabel. 164 // Also add correct rotation with its rotation origin. #15129 165 label.attr({ 166 x: label.alignAttr.x, 167 y: label.alignAttr.y, 168 rotation: options.rotation, 169 rotationOriginX: labelBox.width * 170 getAlignFactor(options.textAlign || 'center'), 171 rotationOriginY: labelBox.height / 2 172 }); 173 // Check if the dataLabel should be visible. 174 if (pick(!isJustify && options.crop, true)) { 175 visible = 176 isNumber(label.x) && 177 isNumber(label.y) && 178 chart.isInsidePlot(label.x - padding + (label.width || 0), label.y) && 179 chart.isInsidePlot(label.x + padding, label.y); 180 } 181 label[visible ? 'show' : 'hide'](); 182 } 183 fireEvent(this, 'afterSetOffset', { xOffset, width }); 184 } 185 /** 186 * Adjust the stack BBox position, to take into consideration the alignment 187 * of the dataLabel. This is necessary to make the stackDataLabel work with 188 * core methods like `SVGLabel.adjust` and `Series.justifyDataLabel`. 189 * @param AdjustStackPositionProps 190 * @return {{x: number, y: number}} Adjusted BBox position of the stack. 191 */ 192 adjustStackPosition({ labelBox, verticalAlign, textAlign }) { 193 return { 194 x: labelBox.width / 2 + 195 (labelBox.width / 2) * (2 * getAlignFactor(textAlign) - 1), 196 y: (labelBox.height / 2) * 2 * (1 - getAlignFactor(verticalAlign)) 197 }; 198 } 199 /** 200 * Get the bbox of the stack. 201 * @private 202 * @function Highcharts.StackItem#getStackBox 203 * @return {BBoxObject} The x, y, height, width of the stack. 204 */ 205 getStackBox(stackBoxProps) { 206 const stackItem = this, axis = this.axis, chart = axis.chart, { boxTop, defaultX, xOffset, width, boxBottom } = stackBoxProps, totalStackValue = axis.stacking.usePercentage ? 207 100 : 208 pick(boxTop, this.total, 0), y = axis.toPixels(totalStackValue), xAxis = stackBoxProps.xAxis || chart.xAxis[0], x = pick(defaultX, xAxis.translate(this.x)) + xOffset, yZero = axis.toPixels(boxBottom || 209 (isNumber(axis.min) && 210 axis.logarithmic && 211 axis.logarithmic.lin2log(axis.min)) || 212 0), height = Math.abs(y - yZero), inverted = chart.inverted, neg = stackItem.isNegative; 213 return inverted ? 214 { 215 x: (neg ? y : y - height) - chart.plotLeft, 216 y: xAxis.height - x - width + xAxis.top - chart.plotTop, 217 width: height, 218 height: width 219 } : { 220 x: x + xAxis.transB - chart.plotLeft, 221 y: (neg ? y - height : y) - chart.plotTop, 222 width: width, 223 height: height 224 }; 225 } 226} 227/* * 228 * 229 * Default Export 230 * 231 * */ 232export default StackItem; 233/* * 234 * 235 * API Declarations 236 * 237 * */ 238/** 239 * Stack of data points 240 * 241 * @product highcharts 242 * 243 * @interface Highcharts.StackItemObject 244 */ /** 245* Alignment settings 246* @name Highcharts.StackItemObject#alignOptions 247* @type {Highcharts.AlignObject} 248*/ /** 249* Related axis 250* @name Highcharts.StackItemObject#axis 251* @type {Highcharts.Axis} 252*/ /** 253* Cumulative value of the stacked data points 254* @name Highcharts.StackItemObject#cumulative 255* @type {number} 256*/ /** 257* True if on the negative side 258* @name Highcharts.StackItemObject#isNegative 259* @type {boolean} 260*/ /** 261* Related SVG element 262* @name Highcharts.StackItemObject#label 263* @type {Highcharts.SVGElement} 264*/ /** 265* Related stack options 266* @name Highcharts.StackItemObject#options 267* @type {Highcharts.YAxisStackLabelsOptions} 268*/ /** 269* Total value of the stacked data points 270* @name Highcharts.StackItemObject#total 271* @type {number} 272*/ /** 273* Shared x value of the stack 274* @name Highcharts.StackItemObject#x 275* @type {number} 276*/ 277''; // Keeps doclets above in JS file
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.