package.es-modules.Series.Flags.FlagsSeries.js Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of highcharts Show documentation
Show all versions of highcharts Show documentation
JavaScript charting framework
The newest version!
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
'use strict';
import FlagsPoint from './FlagsPoint.js';
import FlagsSeriesDefaults from './FlagsSeriesDefaults.js';
import FlagsSymbols from './FlagsSymbols.js';
import H from '../../Core/Globals.js';
const { noop } = H;
import OnSeriesComposition from '../OnSeriesComposition.js';
import R from '../../Core/Renderer/RendererUtilities.js';
const { distribute } = R;
import SeriesRegistry from '../../Core/Series/SeriesRegistry.js';
const { series: Series, seriesTypes: { column: ColumnSeries } } = SeriesRegistry;
import SVGElement from '../../Core/Renderer/SVG/SVGElement.js';
import U from '../../Core/Utilities.js';
const { addEvent, defined, extend, isNumber, merge, objectEach, wrap } = U;
/* *
*
* Classes
*
* */
/**
* The Flags series.
*
* @private
* @class
* @name Highcharts.seriesTypes.flags
*
* @augments Highcharts.Series
*/
class FlagsSeries extends ColumnSeries {
/* *
*
* Functions
*
* */
/**
* Disable animation, but keep clipping (#8546).
* @private
*/
animate(init) {
if (init) {
this.setClip();
}
}
/**
* Draw the markers.
* @private
*/
drawPoints() {
const series = this, points = series.points, chart = series.chart, renderer = chart.renderer, inverted = chart.inverted, options = series.options, optionsY = options.y, yAxis = series.yAxis, boxesMap = {}, boxes = [], borderRadius = isNumber(options.borderRadius) ?
options.borderRadius : 0;
let plotX, plotY, shape, i, point, graphic, stackIndex, anchorY, attribs, outsideRight, centered;
i = points.length;
while (i--) {
point = points[i];
outsideRight =
(inverted ? point.plotY : point.plotX) >
series.xAxis.len;
plotX = point.plotX;
stackIndex = point.stackIndex;
shape = point.options.shape || options.shape;
plotY = point.plotY;
if (typeof plotY !== 'undefined') {
plotY = point.plotY + optionsY -
(typeof stackIndex !== 'undefined' &&
(stackIndex * options.stackDistance));
}
// Skip connectors for higher level stacked points
point.anchorX = stackIndex ? void 0 : point.plotX;
anchorY = stackIndex ? void 0 : point.plotY;
centered = shape !== 'flag';
graphic = point.graphic;
// Only draw the point if y is defined and the flag is within
// the visible area
if (typeof plotY !== 'undefined' &&
plotX >= 0 &&
!outsideRight) {
// #15384
if (graphic && point.hasNewShapeType()) {
graphic = graphic.destroy();
}
// Create the flag
if (!graphic) {
graphic = point.graphic = renderer.label('', 0, void 0, shape, void 0, void 0, options.useHTML)
.addClass('highcharts-point')
.add(series.markerGroup);
// Add reference to the point for tracker (#6303)
if (point.graphic.div) {
point.graphic.div.point = point;
}
graphic.isNew = true;
}
graphic.attr({
align: centered ? 'center' : 'left',
width: options.width,
height: options.height,
'text-align': options.textAlign,
r: borderRadius
});
if (!chart.styledMode) {
graphic
.attr(series.pointAttribs(point))
.css(merge(options.style, point.style))
.shadow(options.shadow);
}
if (plotX > 0) { // #3119
plotX -= graphic.strokeWidth() % 2; // #4285
}
// Plant the flag
attribs = {
y: plotY,
anchorY: anchorY
};
if (options.allowOverlapX) {
attribs.x = plotX;
attribs.anchorX = point.anchorX;
}
graphic.attr({
// Allow empty string as a flag title (#20549)
text: point.options.title ?? options.title ?? 'A'
})[graphic.isNew ? 'attr' : 'animate'](attribs);
// Rig for the distribute function
if (!options.allowOverlapX) {
if (!boxesMap[point.plotX]) {
boxesMap[point.plotX] = {
align: centered ? 0.5 : 0,
size: graphic.width || 0,
target: plotX,
anchorX: plotX
};
}
else {
boxesMap[point.plotX].size = Math.max(boxesMap[point.plotX].size, graphic.width || 0);
}
}
// Set the tooltip anchor position
point.tooltipPos = [
plotX,
plotY + yAxis.pos - chart.plotTop
]; // #6327
}
else if (graphic) {
point.graphic = graphic.destroy();
}
}
// Handle X-dimension overlapping
if (!options.allowOverlapX) {
let maxDistance = 100;
objectEach(boxesMap, function (box) {
box.plotX = box.anchorX;
boxes.push(box);
maxDistance = Math.max(box.size, maxDistance);
});
// If necessary (for overlapping or long labels) distribute it
// depending on the label width or a hardcoded value, #16041.
distribute(boxes, inverted ? yAxis.len : this.xAxis.len, maxDistance);
for (const point of points) {
const plotX = point.plotX, graphic = point.graphic, box = graphic && boxesMap[plotX];
if (box && graphic) {
// Hide flag when its box position is not specified
// (#8573, #9299)
if (!defined(box.pos)) {
graphic.hide().isNew = true;
}
else {
graphic[graphic.isNew ? 'attr' : 'animate']({
x: box.pos + (box.align || 0) * box.size,
anchorX: point.anchorX
}).show().isNew = false;
}
}
}
}
// Can be a mix of SVG and HTML and we need events for both (#6303)
if (options.useHTML && series.markerGroup) {
wrap(series.markerGroup, 'on', function (proceed) {
return SVGElement.prototype.on.apply(
// For HTML
// eslint-disable-next-line no-invalid-this
proceed.apply(this, [].slice.call(arguments, 1)),
// And for SVG
[].slice.call(arguments, 1));
});
}
}
/**
* Extend the column trackers with listeners to expand and contract
* stacks.
* @private
*/
drawTracker() {
const series = this, points = series.points;
super.drawTracker();
/* *
* Bring each stacked flag up on mouse over, this allows readability
* of vertically stacked elements as well as tight points on the x
* axis. #1924.
*/
for (const point of points) {
const graphic = point.graphic;
if (graphic) {
if (point.unbindMouseOver) {
point.unbindMouseOver();
}
point.unbindMouseOver = addEvent(graphic.element, 'mouseover', function () {
// Raise this point
if (point.stackIndex > 0 &&
!point.raised) {
point._y = graphic.y;
graphic.attr({
y: point._y - 8
});
point.raised = true;
}
// Revert other raised points
for (const otherPoint of points) {
if (otherPoint !== point &&
otherPoint.raised &&
otherPoint.graphic) {
otherPoint.graphic.attr({
y: otherPoint._y
});
otherPoint.raised = false;
}
}
});
}
}
}
/**
* Get presentational attributes
* @private
*/
pointAttribs(point, state) {
const options = this.options, color = (point && point.color) || this.color;
let lineColor = options.lineColor, lineWidth = (point && point.lineWidth), fill = (point && point.fillColor) || options.fillColor;
if (state) {
fill = options.states[state].fillColor;
lineColor = options.states[state].lineColor;
lineWidth = options.states[state].lineWidth;
}
return {
fill: fill || color,
stroke: lineColor || color,
'stroke-width': lineWidth || options.lineWidth || 0
};
}
/**
* @private
*/
setClip() {
Series.prototype.setClip.apply(this, arguments);
if (this.options.clip !== false &&
this.sharedClipKey &&
this.markerGroup) {
this.markerGroup.clip(this.chart.sharedClips[this.sharedClipKey]);
}
}
}
/* *
*
* Static Properties
*
* */
FlagsSeries.compose = FlagsSymbols.compose;
FlagsSeries.defaultOptions = merge(ColumnSeries.defaultOptions, FlagsSeriesDefaults);
OnSeriesComposition.compose(FlagsSeries);
extend(FlagsSeries.prototype, {
allowDG: false,
forceCrop: true,
invertible: false, // Flags series group should not be invertible (#14063).
noSharedTooltip: true,
pointClass: FlagsPoint,
sorted: false,
takeOrdinalPosition: false, // #1074
trackerGroups: ['markerGroup'],
buildKDTree: noop,
/**
* Inherit the initialization from base Series.
* @private
*/
init: Series.prototype.init
});
SeriesRegistry.registerSeriesType('flags', FlagsSeries);
/* *
*
* Default Export
*
* */
export default FlagsSeries;
/* *
*
* API Declarations
*
* */
/**
* @typedef {"circlepin"|"flag"|"squarepin"} Highcharts.FlagsShapeValue
*/
''; // Detach doclets above