PageSourceSearch

https://www.tourisme-paysdaubagne.fr/app/dist/aubagne/addons/woody…/code/es-modules/Series/Pie/PieDataLabel.js

js tourisme-paysdaubagne.fr collected 2026-10-02 18:26:24 UTC 24,314 bytes, 507 lines download raw bytes

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 DataLabel from '../../Core/Series/DataLabel.js';
12import H from '../../Core/Globals.js';
13const { composed, noop } = H;
14import R from '../../Core/Renderer/RendererUtilities.js';
15const { distribute } = R;
16import SeriesRegistry from '../../Core/Series/SeriesRegistry.js';
17const { series: Series } = SeriesRegistry;
18import U from '../../Core/Utilities.js';
19const { arrayMax, clamp, defined, pick, pushUnique, relativeLength } = U;
20/* *
21 *
22 *  Composition
23 *
24 * */
25var ColumnDataLabel;
26(function (ColumnDataLabel) {
27    /* *
28     *
29     *  Constants
30     *
31     * */
32    const dataLabelPositioners = {
33        // Based on the value computed in Highcharts' distribute algorithm.
34        radialDistributionY: function (point, dataLabel) {
35            return (dataLabel.dataLabelPosition?.top || 0) +
36                point.distributeBox.pos;
37        },
38        // Get the x - use the natural x position for labels near the top and
39        // bottom, to prevent the top and botton slice connectors from touching
40        // each other on either side. Based on the value computed in Highcharts'
41        // distribute algorithm.
42        radialDistributionX: function (series, point, y, naturalY, dataLabel) {
43            const pos = dataLabel.dataLabelPosition;
44            return series.getX(y < (pos?.top || 0) + 2 || y > (pos?.bottom || 0) - 2 ?
45                naturalY :
46                y, point.half, point, dataLabel);
47        },
48        // The dataLabels.distance determines the x position of the label
49        justify: function (point, dataLabel, radius, seriesCenter) {
50            return seriesCenter[0] + (point.half ? -1 : 1) *
51                (radius + (dataLabel.dataLabelPosition?.distance || 0));
52        },
53        // Left edges of the left-half labels touch the left edge of the plot
54        // area. Right edges of the right-half labels touch the right edge of
55        // the plot area.
56        alignToPlotEdges: function (dataLabel, half, plotWidth, plotLeft) {
57            const dataLabelWidth = dataLabel.getBBox().width;
58            return half ? dataLabelWidth + plotLeft :
59                plotWidth - dataLabelWidth - plotLeft;
60        },
61        // Connectors of each side end in the same x position. Labels are
62        // aligned to them. Left edge of the widest left-half label touches the
63        // left edge of the plot area. Right edge of the widest right-half label
64        // touches the right edge of the plot area.
65        alignToConnectors: function (points, half, plotWidth, plotLeft) {
66            let maxDataLabelWidth = 0, dataLabelWidth;
67            // Find widest data label
68            points.forEach(function (point) {
69                dataLabelWidth = point.dataLabel.getBBox().width;
70                if (dataLabelWidth > maxDataLabelWidth) {
71                    maxDataLabelWidth = dataLabelWidth;
72                }
73            });
74            return half ? maxDataLabelWidth + plotLeft :
75                plotWidth - maxDataLabelWidth - plotLeft;
76        }
77    };
78    /* *
79     *
80     *  Functions
81     *
82     * */
83    /** @private */
84    function compose(PieSeriesClass) {
85        DataLabel.compose(Series);
86        if (pushUnique(composed, 'PieDataLabel')) {
87            const pieProto = PieSeriesClass.prototype;
88            pieProto.dataLabelPositioners = dataLabelPositioners;
89            pieProto.alignDataLabel = noop;
90            pieProto.drawDataLabels = drawDataLabels;
91            pieProto.getDataLabelPosition = getDataLabelPosition;
92            pieProto.placeDataLabels = placeDataLabels;
93            pieProto.verifyDataLabelOverflow = verifyDataLabelOverflow;
94        }
95    }
96    ColumnDataLabel.compose = compose;
97    /** @private */
98    function getDataLabelPosition(point, distance) {
99        const halfPI = Math.PI / 2, { start = 0, end = 0 } = point.shapeArgs || {};
100        let angle = point.angle || 0;
101        // If a large slice is crossing the lowest point, prefer rendering it 45
102        // degrees out at either lower right or lower left. That's where there's
103        // most likely to be space available and avoid text being truncated
104        // (#22100). Technically this logic should also apply to the top point,
vendor: 19,944 bytes, lines 105-507
105        // but that is more of an edge case since the default start angle is at
106        // the top.
107        if (distance > 0 &&
108            // Crossing the bottom
109            start < halfPI && end > halfPI &&
110            // Angle within the bottom quadrant
111            angle > halfPI / 2 && angle < halfPI * 1.5) {
112            angle = angle <= halfPI ?
113                Math.max(halfPI / 2, (start + halfPI) / 2) :
114                Math.min(halfPI * 1.5, (halfPI + end) / 2);
115        }
116        const { center, options } = this, r = center[2] / 2, cosAngle = Math.cos(angle), sinAngle = Math.sin(angle), x = center[0] + cosAngle * r, y = center[1] + sinAngle * r, finalConnectorOffset = Math.min((options.slicedOffset || 0) + (options.borderWidth || 0), distance / 5); // #1678
117        return {
118            natural: {
119                // Initial position of the data label - it's utilized for
120                // finding the final position for the label
121                x: x + cosAngle * distance,
122                y: y + sinAngle * distance
123            },
124            computed: {
125            // Used for generating connector path - initialized later in
126            // drawDataLabels function x: undefined, y: undefined
127            },
128            // Left - pie on the left side of the data label
129            // Right - pie on the right side of the data label
130            // Center - data label overlaps the pie
131            alignment: distance < 0 ? 'center' : point.half ? 'right' : 'left',
132            connectorPosition: {
133                angle,
134                breakAt: {
135                    x: x + cosAngle * finalConnectorOffset,
136                    y: y + sinAngle * finalConnectorOffset
137                },
138                touchingSliceAt: {
139                    x,
140                    y
141                }
142            },
143            distance
144        };
145    }
146    /**
147     * Override the base drawDataLabels method by pie specific functionality
148     * @private
149     */
150    function drawDataLabels() {
151        const series = this, points = series.points, chart = series.chart, plotWidth = chart.plotWidth, plotHeight = chart.plotHeight, plotLeft = chart.plotLeft, maxWidth = Math.round(chart.chartWidth / 3), seriesCenter = series.center, radius = seriesCenter[2] / 2, centerY = seriesCenter[1], halves = [
152            [], // Right
153            [] // Left
154        ], overflow = [0, 0, 0, 0], // Top, right, bottom, left
155        dataLabelPositioners = series.dataLabelPositioners;
156        let connector, dataLabelWidth, labelHeight, maxLabelDistance = 0;
157        // Get out if not enabled
158        if (!series.visible || !series.hasDataLabels?.()) {
159            return;
160        }
161        // Reset all labels that have been shortened
162        points.forEach((point) => {
163            (point.dataLabels || []).forEach((dataLabel) => {
164                if (dataLabel.shortened) {
165                    dataLabel
166                        .attr({
167                        width: 'auto'
168                    }).css({
169                        width: 'auto',
170                        textOverflow: 'clip'
171                    });
172                    dataLabel.shortened = false;
173                }
174            });
175        });
176        // Run parent method
177        Series.prototype.drawDataLabels.apply(series);
178        points.forEach((point) => {
179            (point.dataLabels || []).forEach((dataLabel, i) => {
180                const r = seriesCenter[2] / 2, dataLabelOptions = dataLabel.options, distance = relativeLength(dataLabelOptions?.distance || 0, r);
181                // Arrange points for collision detection
182                if (i === 0) {
183                    halves[point.half].push(point);
184                }
185                // Avoid long labels squeezing the pie size too far down
186                if (!defined(dataLabelOptions?.style?.width)) {
187                    if (dataLabel.getBBox().width > maxWidth) {
188                        dataLabel.css({
189                            // Use a fraction of the maxWidth to avoid wrapping
190                            // close to the end of the string.
191                            width: Math.round(maxWidth * 0.7) + 'px'
192                        });
193                        dataLabel.shortened = true;
194                    }
195                }
196                dataLabel.dataLabelPosition = this.getDataLabelPosition(point, distance);
197                maxLabelDistance = Math.max(maxLabelDistance, distance);
198            });
199        });
200        /* Loop over the points in each half, starting from the top and bottom
201         * of the pie to detect overlapping labels.
202         */
203        halves.forEach((points, halfIdx) => {
204            const length = points.length, positions = [];
205            let top, bottom, size = 0, distributionLength;
206            if (!length) {
207                return;
208            }
209            // Sort by angle
210            series.sortByAngle(points, halfIdx - 0.5);
211            // Only do anti-collision when we have dataLabels outside the pie
212            // and have connectors. (#856)
213            if (maxLabelDistance > 0) {
214                top = Math.max(0, centerY - radius - maxLabelDistance);
215                bottom = Math.min(centerY + radius + maxLabelDistance, chart.plotHeight);
216                points.forEach((point) => {
217                    // Check if specific points' label is outside the pie
218                    (point.dataLabels || []).forEach((dataLabel) => {
219                        const labelPosition = dataLabel.dataLabelPosition;
220                        if (labelPosition &&
221                            labelPosition.distance > 0) {
222                            // The point.top depends on point.labelDistance
223                            // value. Used for calculation of y value in getX
224                            // method
225                            labelPosition.top = Math.max(0, centerY - radius - labelPosition.distance);
226                            labelPosition.bottom = Math.min(centerY + radius + labelPosition.distance, chart.plotHeight);
227                            size = dataLabel.getBBox().height || 21;
228                            dataLabel.lineHeight = chart.renderer.fontMetrics(dataLabel.text || dataLabel).h + 2 * dataLabel.padding;
229                            point.distributeBox = {
230                                target: ((dataLabel.dataLabelPosition
231                                    ?.natural.y || 0) -
232                                    labelPosition.top +
233                                    dataLabel.lineHeight / 2),
234                                size,
235                                rank: point.y
236                            };
237                            positions.push(point.distributeBox);
238                        }
239                    });
240                });
241                distributionLength = bottom + size - top;
242                distribute(positions, distributionLength, distributionLength / 5);
243                // Uncomment this to visualize the boxes
244                /*
245                points.forEach((point): void => {
246                    const box = point.distributeBox;
247                    point.dlBox?.destroy();
248                    if (box?.pos) {
249                        point.dlBox = chart.renderer.rect(
250                            chart.plotLeft + this.center[0] + (
251                                halfIdx ?
252                                    -this.center[2] / 2 - 100 :
253                                    this.center[2] / 2
254                            ),
255                            chart.plotTop + box.pos,
256                            100,
257                            box.size
258                        )
259                            .attr({
260                                stroke: 'silver',
261                                'stroke-width': 1
262                            })
263                            .add();
264                    }
265                });
266                // */
267            }
268            // Now the used slots are sorted, fill them up sequentially
269            points.forEach((point) => {
270                (point.dataLabels || []).forEach((dataLabel) => {
271                    const dataLabelOptions = (dataLabel.options || {}), distributeBox = point.distributeBox, labelPosition = dataLabel.dataLabelPosition, naturalY = labelPosition?.natural.y || 0, connectorPadding = dataLabelOptions
272                        .connectorPadding || 0, lineHeight = dataLabel.lineHeight || 21, bBox = dataLabel.getBBox(), topOffset = (lineHeight - bBox.height) / 2;
273                    let x = 0, y = naturalY, visibility = 'inherit';
274                    if (labelPosition) {
275                        if (positions &&
276                            defined(distributeBox) &&
277                            labelPosition.distance > 0) {
278                            if (typeof distributeBox.pos === 'undefined') {
279                                visibility = 'hidden';
280                            }
281                            else {
282                                labelHeight = distributeBox.size;
283                                // Find label's y position
284                                y = dataLabelPositioners
285                                    .radialDistributionY(point, dataLabel);
286                            }
287                        }
288                        // Find label's x position. The justify option is
289                        // undocumented in the API - preserve support for it
290                        if (dataLabelOptions.justify) {
291                            x = dataLabelPositioners.justify(point, dataLabel, radius, seriesCenter);
292                        }
293                        else {
294                            switch (dataLabelOptions.alignTo) {
295                                case 'connectors':
296                                    x = dataLabelPositioners.alignToConnectors(points, halfIdx, plotWidth, plotLeft);
297                                    break;
298                                case 'plotEdges':
299                                    x = dataLabelPositioners.alignToPlotEdges(dataLabel, halfIdx, plotWidth, plotLeft);
300                                    break;
301                                default:
302                                    x = dataLabelPositioners.radialDistributionX(series, point, y - topOffset, naturalY, dataLabel);
303                            }
304                        }
305                        // Record the placement and visibility
306                        labelPosition.attribs = {
307                            visibility,
308                            align: labelPosition.alignment
309                        };
310                        labelPosition.posAttribs = {
311                            x: x +
312                                (dataLabelOptions.x || 0) + // (#12985)
313                                ({
314                                    left: connectorPadding,
315                                    right: -connectorPadding
316                                }[labelPosition.alignment] || 0),
317                            y: y +
318                                (dataLabelOptions.y || 0) - // (#12985)
319                                // Vertically center
320                                lineHeight / 2
321                        };
322                        labelPosition.computed.x = x;
323                        labelPosition.computed.y = y - topOffset;
324                        // Detect overflowing data labels
325                        if (pick(dataLabelOptions.crop, true)) {
326                            dataLabelWidth = dataLabel.getBBox().width;
327                            let sideOverflow;
328                            // Overflow left
329                            if (x - dataLabelWidth < connectorPadding &&
330                                halfIdx === 1 // Left half
331                            ) {
332                                sideOverflow = Math.round(dataLabelWidth - x + connectorPadding);
333                                overflow[3] = Math.max(sideOverflow, overflow[3]);
334                                // Overflow right
335                            }
336                            else if (x + dataLabelWidth >
337                                plotWidth - connectorPadding &&
338                                halfIdx === 0 // Right half
339                            ) {
340                                sideOverflow = Math.round(x +
341                                    dataLabelWidth -
342                                    plotWidth +
343                                    connectorPadding);
344                                overflow[1] = Math.max(sideOverflow, overflow[1]);
345                            }
346                            // Overflow top
347                            if (y - labelHeight / 2 < 0) {
348                                overflow[0] = Math.max(Math.round(-y + labelHeight / 2), overflow[0]);
349                                // Overflow left
350                            }
351                            else if (y + labelHeight / 2 > plotHeight) {
352                                overflow[2] = Math.max(Math.round(y + labelHeight / 2 - plotHeight), overflow[2]);
353                            }
354                            labelPosition.sideOverflow = sideOverflow;
355                        }
356                    }
357                }); // For each data label of the point
358            }); // For each point
359        }); // For each half
360        // Do not apply the final placement and draw the connectors until we
361        // have verified that labels are not spilling over.
362        if (arrayMax(overflow) === 0 ||
363            this.verifyDataLabelOverflow(overflow)) {
364            // Place the labels in the final position
365            this.placeDataLabels();
366            this.points.forEach((point) => {
367                (point.dataLabels || []).forEach((dataLabel) => {
368                    // #8864: every connector can have individual options
369                    const { connectorColor, connectorWidth = 1 } = (dataLabel.options || {}), labelPosition = dataLabel.dataLabelPosition;
370                    // Draw the connector
371                    if (connectorWidth) {
372                        let isNew;
373                        connector = dataLabel.connector;
374                        if (labelPosition && labelPosition.distance > 0) {
375                            isNew = !connector;
376                            if (!connector) {
377                                dataLabel.connector = connector = chart.renderer
378                                    .path()
379                                    .addClass('highcharts-data-label-connector ' +
380                                    ' highcharts-color-' +
381                                    point.colorIndex +
382                                    (point.className ?
383                                        ' ' + point.className :
384                                        ''))
385                                    .add(series.dataLabelsGroup);
386                            }
387                            if (!chart.styledMode) {
388                                connector.attr({
389                                    'stroke-width': connectorWidth,
390                                    'stroke': (connectorColor ||
391                                        point.color ||
392                                        "#666666" /* Palette.neutralColor60 */)
393                                });
394                            }
395                            connector[isNew ? 'attr' : 'animate']({
396                                d: point.getConnectorPath(dataLabel)
397                            });
398                            connector.attr({
399                                visibility: labelPosition.attribs?.visibility
400                            });
401                        }
402                        else if (connector) {
403                            dataLabel.connector = connector.destroy();
404                        }
405                    }
406                });
407            });
408        }
409    }
410    /**
411     * Perform the final placement of the data labels after we have verified
412     * that they fall within the plot area.
413     * @private
414     */
415    function placeDataLabels() {
416        this.points.forEach((point) => {
417            (point.dataLabels || []).forEach((dataLabel) => {
418                const labelPosition = dataLabel.dataLabelPosition;
419                if (labelPosition) {
420                    // Shorten data labels with ellipsis if they still overflow
421                    // after the pie has reached minSize (#223).
422                    if (labelPosition.sideOverflow) {
423                        dataLabel.css({
424                            width: (Math.max(dataLabel.getBBox().width -
425                                labelPosition.sideOverflow, 0)) + 'px',
426                            textOverflow: ((dataLabel.options?.style || {})
427                                .textOverflow ||
428                                'ellipsis')
429                        });
430                        dataLabel.shortened = true;
431                    }
432                    dataLabel.attr(labelPosition.attribs);
433                    dataLabel[dataLabel.moved ? 'animate' : 'attr'](labelPosition.posAttribs);
434                    dataLabel.moved = true;
435                }
436                else if (dataLabel) {
437                    dataLabel.attr({ y: -9999 });
438                }
439            });
440            // Clear for update
441            delete point.distributeBox;
442        }, this);
443    }
444    /**
445     * Verify whether the data labels are allowed to draw, or we should run more
446     * translation and data label positioning to keep them inside the plot area.
447     * Returns true when data labels are ready to draw.
448     * @private
449     */
450    function verifyDataLabelOverflow(overflow) {
451        const center = this.center, options = this.options, centerOption = options.center, minSize = options.minSize || 80;
452        let newSize = minSize, 
453        // If a size is set, return true and don't try to shrink the pie
454        // to fit the labels.
455        ret = options.size !== null;
456        if (!ret) {
457            // Handle horizontal size and center
458            if (centerOption[0] !== null) { // Fixed center
459                newSize = Math.max(center[2] -
460                    Math.max(overflow[1], overflow[3]), minSize);
461            }
462            else { // Auto center
463                newSize = Math.max(
464                // Horizontal overflow
465                center[2] - overflow[1] - overflow[3], minSize);
466                // Horizontal center
467                center[0] += (overflow[3] - overflow[1]) / 2;
468            }
469            // Handle vertical size and center
470            if (centerOption[1] !== null) { // Fixed center
471                newSize = clamp(newSize, minSize, center[2] - Math.max(overflow[0], overflow[2]));
472            }
473            else { // Auto center
474                newSize = clamp(newSize, minSize, 
475                // Vertical overflow
476                center[2] - overflow[0] - overflow[2]);
477                // Vertical center
478                center[1] += (overflow[0] - overflow[2]) / 2;
479            }
480            // If the size must be decreased, we need to run translate and
481            // drawDataLabels again
482            if (newSize < center[2]) {
483                center[2] = newSize;
484                center[3] = Math.min(// #3632
485                options.thickness ?
486                    Math.max(0, newSize - options.thickness * 2) :
487                    Math.max(0, relativeLength(options.innerSize || 0, newSize)), newSize); // #6647
488                this.translate(center);
489                if (this.drawDataLabels) {
490                    this.drawDataLabels();
491                }
492                // Else, return true to indicate that the pie and its labels is
493                // within the plot area
494            }
495            else {
496                ret = true;
497            }
498        }
499        return ret;
500    }
501})(ColumnDataLabel || (ColumnDataLabel = {}));
502/* *
503 *
504 *  Default Export
505 *
506 * */
507export default ColumnDataLabel;

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.