highcharts
Version:
JavaScript charting framework
464 lines (463 loc) • 17.2 kB
JavaScript
/* *
*
* (c) 2010-2025 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
'use strict';
import DataGroupingAxisComposition from './DataGroupingAxisComposition.js';
import DataGroupingDefaults from './DataGroupingDefaults.js';
import DataGroupingSeriesComposition from './DataGroupingSeriesComposition.js';
import F from '../../Core/Templating.js';
const { format } = F;
import H from '../../Core/Globals.js';
const { composed } = H;
import U from '../../Core/Utilities.js';
const { addEvent, extend, isNumber, pick, pushUnique } = U;
/* *
*
* Functions
*
* */
/**
* @private
*/
function compose(AxisClass, SeriesClass, TooltipClass) {
DataGroupingAxisComposition.compose(AxisClass);
DataGroupingSeriesComposition.compose(SeriesClass);
if (TooltipClass &&
pushUnique(composed, 'DataGrouping')) {
addEvent(TooltipClass, 'headerFormatter', onTooltipHeaderFormatter);
}
}
/**
* Extend the original method, make the tooltip's header reflect the grouped
* range.
* @private
*/
function onTooltipHeaderFormatter(e) {
const chart = this.chart, time = chart.time, point = e.point, series = point.series, options = series.options, tooltipOptions = series.tooltipOptions, dataGroupingOptions = options.dataGrouping, xAxis = series.xAxis;
let xDateFormat = tooltipOptions.xDateFormat || '', xDateFormatEnd, currentDataGrouping, dateTimeLabelFormats, labelFormats, formattedKey, formatString = tooltipOptions[e.isFooter ? 'footerFormat' : 'headerFormat'];
// Apply only to grouped series
if (xAxis &&
xAxis.options.type === 'datetime' &&
dataGroupingOptions &&
isNumber(point.key)) {
// Set variables
currentDataGrouping = series.currentDataGrouping;
dateTimeLabelFormats = dataGroupingOptions.dateTimeLabelFormats ||
// Fallback to commonOptions (#9693)
DataGroupingDefaults.common.dateTimeLabelFormats;
// If we have grouped data, use the grouping information to get the
// right format
if (currentDataGrouping) {
labelFormats = dateTimeLabelFormats[currentDataGrouping.unitName];
if (currentDataGrouping.count === 1) {
xDateFormat = labelFormats[0];
}
else {
xDateFormat = labelFormats[1];
xDateFormatEnd = labelFormats[2];
}
// If not grouped, and we don't have set the xDateFormat option, get the
// best fit, so if the least distance between points is one minute, show
// it, but if the least distance is one day, skip hours and minutes etc.
}
else if (!xDateFormat && dateTimeLabelFormats && xAxis.dateTime) {
xDateFormat = xAxis.dateTime.getXDateFormat(point.x, tooltipOptions.dateTimeLabelFormats);
}
const groupStart = pick(series.groupMap?.[point.index].groupStart, point.key), groupEnd = groupStart + (currentDataGrouping?.totalRange || 0) - 1;
formattedKey = time.dateFormat(xDateFormat, groupStart);
if (xDateFormatEnd) {
formattedKey += time.dateFormat(xDateFormatEnd, groupEnd);
}
// Replace default header style with class name
if (series.chart.styledMode) {
formatString = this.styledModeFormat(formatString);
}
// Return the replaced format
e.text = format(formatString, {
point: extend(point, { key: formattedKey }),
series
}, chart);
e.preventDefault();
}
}
/* *
*
* Default Export
*
* */
const DataGroupingComposition = {
compose,
groupData: DataGroupingSeriesComposition.groupData
};
export default DataGroupingComposition;
/* *
*
* API Declarations
*
* */
/**
* @typedef {"average"|"averages"|"open"|"high"|"low"|"close"|"sum"} Highcharts.DataGroupingApproximationValue
*/
/**
* The position of the point inside the group.
*
* @typedef {"start"|"middle"|"end"} Highcharts.DataGroupingAnchor
*/
/**
* The position of the first or last point in the series inside the group.
*
* @typedef {"start"|"middle"|"end"|"firstPoint"|"lastPoint"} Highcharts.DataGroupingAnchorExtremes
*/
/**
* Highcharts Stock only.
*
* @product highstock
* @interface Highcharts.DataGroupingInfoObject
*/ /**
* @name Highcharts.DataGroupingInfoObject#length
* @type {number}
*/ /**
* @name Highcharts.DataGroupingInfoObject#options
* @type {Highcharts.SeriesOptionsType|undefined}
*/ /**
* @name Highcharts.DataGroupingInfoObject#start
* @type {number}
*/
/**
* Highcharts Stock only.
*
* @product highstock
* @interface Highcharts.DataGroupingResultObject
*/ /**
* @name Highcharts.DataGroupingResultObject#groupedXData
* @type {Array<number>}
*/ /**
* @name Highcharts.DataGroupingResultObject#groupedYData
* @type {Array<(number|null|undefined)>|Array<Array<(number|null|undefined)>>}
*/ /**
* @name Highcharts.DataGroupingResultObject#groupMap
* @type {Array<DataGroupingInfoObject>}
*/
/**
* Highcharts Stock only. If a point object is created by data
* grouping, it doesn't reflect actual points in the raw
* data. In this case, the `dataGroup` property holds
* information that points back to the raw data.
*
* - `dataGroup.start` is the index of the first raw data
* point in the group.
*
* - `dataGroup.length` is the amount of points in the
* group.
*
* @sample stock/members/point-datagroup
* Click to inspect raw data points
*
* @product highstock
*
* @name Highcharts.Point#dataGroup
* @type {Highcharts.DataGroupingInfoObject|undefined}
*/
(''); // Detach doclets above
/* *
*
* API Options
*
* */
/**
* Data grouping is the concept of sampling the data values into larger
* blocks in order to ease readability and increase performance of the
* JavaScript charts. Highcharts Stock by default applies data grouping when
* the points become closer than a certain pixel value, determined by
* the `groupPixelWidth` option.
*
* If data grouping is applied, the grouping information of grouped
* points can be read from the [Point.dataGroup](
* /class-reference/Highcharts.Point#dataGroup). If point options other than
* the data itself are set, for example `name` or `color` or custom properties,
* the grouping logic doesn't know how to group it. In this case the options of
* the first point instance are copied over to the group point. This can be
* altered through a custom `approximation` callback function.
*
* @declare Highcharts.DataGroupingOptionsObject
* @product highstock
* @requires modules/stock
* @apioption plotOptions.series.dataGrouping
*/
/**
* Specifies how the points should be located on the X axis inside the group.
* Points that are extremes can be set separately. Available options:
*
* - `start` places the point at the beginning of the group
* (e.g. range 00:00:00 - 23:59:59 -> 00:00:00)
*
* - `middle` places the point in the middle of the group
* (e.g. range 00:00:00 - 23:59:59 -> 12:00:00)
*
* - `end` places the point at the end of the group
* (e.g. range 00:00:00 - 23:59:59 -> 23:59:59)
*
* @sample {highstock} stock/plotoptions/series-datagrouping-anchor
* Changing the point x-coordinate inside the group.
*
* @see [dataGrouping.firstAnchor](#plotOptions.series.dataGrouping.firstAnchor)
* @see [dataGrouping.lastAnchor](#plotOptions.series.dataGrouping.lastAnchor)
*
* @type {Highcharts.DataGroupingAnchor}
* @since 9.1.0
* @default start
* @apioption plotOptions.series.dataGrouping.anchor
*/
/**
* The method of approximation inside a group. When for example 30 days
* are grouped into one month, this determines what value should represent
* the group. Possible values are "average", "averages", "open", "high",
* "low", "close" and "sum". For OHLC and candlestick series the approximation
* is "ohlc" by default, which finds the open, high, low and close values
* within all the grouped data. For ranges, the approximation is "range",
* which finds the low and high values. For multi-dimensional data,
* like ranges and OHLC, "averages" will compute the average for each
* dimension.
*
* Custom aggregate methods can be added by assigning a callback function
* as the approximation. This function takes a numeric array as the
* argument and should return a single numeric value or `null`. Note
* that the numeric array will never contain null values, only true
* numbers. Instead, if null values are present in the raw data, the
* numeric array will have an `.hasNulls` property set to `true`. For
* single-value data sets the data is available in the first argument
* of the callback function. For OHLC data sets, all the open values
* are in the first argument, all high values in the second etc.
*
* Since v4.2.7, grouping meta data is available in the approximation
* callback from `this.dataGroupInfo`. It can be used to extract information
* from the raw data.
*
* Defaults to `average` for line-type series, `sum` for columns, `range`
* for range series, `hlc` for HLC, and `ohlc` for OHLC and candlestick.
*
* @sample {highstock} stock/plotoptions/series-datagrouping-approximation
* Approximation callback with custom data
* @sample {highstock} stock/plotoptions/series-datagrouping-simple-approximation
* Simple approximation demo
*
* @type {Highcharts.DataGroupingApproximationValue|Function}
* @apioption plotOptions.series.dataGrouping.approximation
*/
/**
* Datetime formats for the header of the tooltip in a stock chart.
* The format can vary within a chart depending on the currently selected
* time range and the current data grouping.
*
* The default formats are:
* ```js
* {
* millisecond: [
* '%A, %e %b, %H:%M:%S.%L', '%A, %e %b, %H:%M:%S.%L', '-%H:%M:%S.%L'
* ],
* second: ['%A, %e %b, %H:%M:%S', '%A, %e %b, %H:%M:%S', '-%H:%M:%S'],
* minute: ['%A, %e %b, %H:%M', '%A, %e %b, %H:%M', '-%H:%M'],
* hour: ['%A, %e %b, %H:%M', '%A, %e %b, %H:%M', '-%H:%M'],
* day: ['%A, %e %b %Y', '%A, %e %b', '-%A, %e %b %Y'],
* week: ['%v %A, %e %b %Y', '%A, %e %b', '-%A, %e %b %Y'],
* month: ['%B %Y', '%B', '-%B %Y'],
* year: ['%Y', '%Y', '-%Y']
* }
* ```
*
* For each of these array definitions, the first item is the format
* used when the active time span is one unit. For instance, if the
* current data applies to one week, the first item of the week array
* is used. The second and third items are used when the active time
* span is more than two units. For instance, if the current data applies
* to two weeks, the second and third item of the week array are used,
* and applied to the start and end date of the time span.
*
* @type {Object}
* @apioption plotOptions.series.dataGrouping.dateTimeLabelFormats
*/
/**
* Enable or disable data grouping.
*
* @type {boolean}
* @default true
* @apioption plotOptions.series.dataGrouping.enabled
*/
/**
* Specifies how the first grouped point is positioned on the xAxis.
* If firstAnchor and/or lastAnchor are defined, then those options take
* precedence over anchor for the first and/or last grouped points.
* Available options:
*
* -`start` places the point at the beginning of the group
* (e.g. range 00:00:00 - 23:59:59 -> 00:00:00)
*
* -`middle` places the point in the middle of the group
* (e.g. range 00:00:00 - 23:59:59 -> 12:00:00)
*
* -`end` places the point at the end of the group
* (e.g. range 00:00:00 - 23:59:59 -> 23:59:59)
*
* -`firstPoint` the first point in the group
* (e.g. points at 00:13, 00:35, 00:59 -> 00:13)
*
* -`lastPoint` the last point in the group
* (e.g. points at 00:13, 00:35, 00:59 -> 00:59)
*
* @sample {highstock} stock/plotoptions/series-datagrouping-first-anchor
* Applying first and last anchor.
*
* @see [dataGrouping.anchor](#plotOptions.series.dataGrouping.anchor)
*
* @type {Highcharts.DataGroupingAnchorExtremes}
* @since 9.1.0
* @default start
* @apioption plotOptions.series.dataGrouping.firstAnchor
*/
/**
* When data grouping is forced, it runs no matter how small the intervals
* are. This can be handy for example when the sum should be calculated
* for values appearing at random times within each hour.
*
* @type {boolean}
* @default false
* @apioption plotOptions.series.dataGrouping.forced
*/
/**
* The approximate pixel width of each group. If for example a series
* with 30 points is displayed over a 600 pixel wide plot area, no grouping
* is performed. If however the series contains so many points that
* the spacing is less than the groupPixelWidth, Highcharts will try
* to group it into appropriate groups so that each is more or less
* two pixels wide. If multiple series with different group pixel widths
* are drawn on the same x axis, all series will take the greatest width.
* For example, line series have 2px default group width, while column
* series have 10px. If combined, both the line and the column will
* have 10px by default.
*
* @type {number}
* @default 2
* @apioption plotOptions.series.dataGrouping.groupPixelWidth
*/
/**
* By default only points within the visible range are grouped. Enabling this
* option will force data grouping to calculate all grouped points for a given
* dataset. That option prevents for example a column series from calculating
* a grouped point partially. The effect is similar to
* [Series.getExtremesFromAll](#plotOptions.series.getExtremesFromAll) but does
* not affect yAxis extremes.
*
* @sample {highstock} stock/plotoptions/series-datagrouping-groupall/
* Two series with the same data but different groupAll setting
*
* @type {boolean}
* @default false
* @since 6.1.0
* @apioption plotOptions.series.dataGrouping.groupAll
*/
/**
* Specifies how the last grouped point is positioned on the xAxis.
* If firstAnchor and/or lastAnchor are defined, then those options take
* precedence over anchor for the first and/or last grouped points.
* Available options:
*
* -`start` places the point at the beginning of the group
* (e.g. range 00:00:00 - 23:59:59 -> 00:00:00)
*
* -`middle` places the point in the middle of the group
* (e.g. range 00:00:00 - 23:59:59 -> 12:00:00)
*
* -`end` places the point at the end of the group
* (e.g. range 00:00:00 - 23:59:59 -> 23:59:59)
*
* -`firstPoint` the first point in the group
* (e.g. points at 00:13, 00:35, 00:59 -> 00:13)
*
* -`lastPoint` the last point in the group
* (e.g. points at 00:13, 00:35, 00:59 -> 00:59)
*
* @sample {highstock} stock/plotoptions/series-datagrouping-first-anchor
* Applying first and last anchor.
*
* @sample {highstock} stock/plotoptions/series-datagrouping-last-anchor
* Applying the last anchor in the chart with live data.
*
* @see [dataGrouping.anchor](#plotOptions.series.dataGrouping.anchor)
*
* @type {Highcharts.DataGroupingAnchorExtremes}
* @since 9.1.0
* @default start
* @apioption plotOptions.series.dataGrouping.lastAnchor
*/
/**
* Normally, a group is indexed by the start of that group, so for example
* when 30 daily values are grouped into one month, that month's x value
* will be the 1st of the month. This apparently shifts the data to
* the left. When the smoothed option is true, this is compensated for.
* The data is shifted to the middle of the group, and min and max
* values are preserved. Internally, this is used in the Navigator series.
*
* @type {boolean}
* @default false
* @deprecated
* @apioption plotOptions.series.dataGrouping.smoothed
*/
/**
* An array determining what time intervals the data is allowed to be
* grouped to. Each array item is an array where the first value is
* the time unit and the second value another array of allowed multiples.
*
* Defaults to:
* ```js
* units: [[
* 'millisecond', // unit name
* [1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples
* ], [
* 'second',
* [1, 2, 5, 10, 15, 30]
* ], [
* 'minute',
* [1, 2, 5, 10, 15, 30]
* ], [
* 'hour',
* [1, 2, 3, 4, 6, 8, 12]
* ], [
* 'day',
* [1]
* ], [
* 'week',
* [1]
* ], [
* 'month',
* [1, 3, 6]
* ], [
* 'year',
* null
* ]]
* ```
*
* @type {Array<Array<string,(Array<number>|null)>>}
* @apioption plotOptions.series.dataGrouping.units
*/
/**
* The approximate pixel width of each group. If for example a series
* with 30 points is displayed over a 600 pixel wide plot area, no grouping
* is performed. If however the series contains so many points that
* the spacing is less than the groupPixelWidth, Highcharts will try
* to group it into appropriate groups so that each is more or less
* two pixels wide. Defaults to `10`.
*
* @sample {highstock} stock/plotoptions/series-datagrouping-grouppixelwidth/
* Two series with the same data density but different groupPixelWidth
*
* @type {number}
* @default 10
* @apioption plotOptions.column.dataGrouping.groupPixelWidth
*/
''; // Required by JSDoc parsing