Carga
Carga
This commit is contained in:
@@ -0,0 +1,218 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2009-2021 Øystein Moseng
|
||||
*
|
||||
* Handle forcing series markers.
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import U from '../../../Core/Utilities.js';
|
||||
var addEvent = U.addEvent, merge = U.merge;
|
||||
/* *
|
||||
*
|
||||
* Composition
|
||||
*
|
||||
* */
|
||||
var ForcedMarkersComposition;
|
||||
(function (ForcedMarkersComposition) {
|
||||
/* *
|
||||
*
|
||||
* Declarations
|
||||
*
|
||||
* */
|
||||
/* *
|
||||
*
|
||||
* Compositions
|
||||
*
|
||||
* */
|
||||
var composedClasses = [];
|
||||
/* *
|
||||
*
|
||||
* Functions
|
||||
*
|
||||
* */
|
||||
/* eslint-disable valid-jsdoc */
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function compose(SeriesClass) {
|
||||
if (composedClasses.indexOf(SeriesClass) === -1) {
|
||||
composedClasses.push(SeriesClass);
|
||||
addEvent(SeriesClass, 'afterSetOptions', seriesOnAfterSetOptions);
|
||||
addEvent(SeriesClass, 'render', seriesOnRender);
|
||||
addEvent(SeriesClass, 'afterRender', seriesOnAfterRender);
|
||||
}
|
||||
}
|
||||
ForcedMarkersComposition.compose = compose;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function forceZeroOpacityMarkerOptions(options) {
|
||||
merge(true, options, {
|
||||
marker: {
|
||||
enabled: true,
|
||||
states: {
|
||||
normal: {
|
||||
opacity: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function getPointMarkerOpacity(pointOptions) {
|
||||
return pointOptions.marker.states &&
|
||||
pointOptions.marker.states.normal &&
|
||||
pointOptions.marker.states.normal.opacity;
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function handleForcePointMarkers(series) {
|
||||
var i = series.points.length;
|
||||
while (i--) {
|
||||
var point = series.points[i];
|
||||
var pointOptions = point.options;
|
||||
var hadForcedMarker = point.hasForcedA11yMarker;
|
||||
delete point.hasForcedA11yMarker;
|
||||
if (pointOptions.marker) {
|
||||
var isStillForcedMarker = hadForcedMarker &&
|
||||
getPointMarkerOpacity(pointOptions) === 0;
|
||||
if (pointOptions.marker.enabled && !isStillForcedMarker) {
|
||||
unforcePointMarkerOptions(pointOptions);
|
||||
point.hasForcedA11yMarker = false;
|
||||
}
|
||||
else if (pointOptions.marker.enabled === false) {
|
||||
forceZeroOpacityMarkerOptions(pointOptions);
|
||||
point.hasForcedA11yMarker = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function hasIndividualPointMarkerOptions(series) {
|
||||
return !!(series._hasPointMarkers &&
|
||||
series.points &&
|
||||
series.points.length);
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function isWithinDescriptionThreshold(series) {
|
||||
var a11yOptions = series.chart.options.accessibility;
|
||||
return series.points.length <
|
||||
a11yOptions.series.pointDescriptionEnabledThreshold ||
|
||||
a11yOptions.series
|
||||
.pointDescriptionEnabledThreshold === false;
|
||||
}
|
||||
/**
|
||||
* Process marker graphics after render
|
||||
* @private
|
||||
*/
|
||||
function seriesOnAfterRender() {
|
||||
var series = this;
|
||||
// For styled mode the rendered graphic does not reflect the style
|
||||
// options, and we need to add/remove classes to achieve the same.
|
||||
if (series.chart.styledMode) {
|
||||
if (series.markerGroup) {
|
||||
series.markerGroup[series.a11yMarkersForced ? 'addClass' : 'removeClass']('highcharts-a11y-markers-hidden');
|
||||
}
|
||||
// Do we need to handle individual points?
|
||||
if (hasIndividualPointMarkerOptions(series)) {
|
||||
series.points.forEach(function (point) {
|
||||
if (point.graphic) {
|
||||
point.graphic[point.hasForcedA11yMarker ?
|
||||
'addClass' : 'removeClass']('highcharts-a11y-marker-hidden');
|
||||
point.graphic[point.hasForcedA11yMarker === false ?
|
||||
'addClass' :
|
||||
'removeClass']('highcharts-a11y-marker-visible');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Keep track of options to reset markers to if no longer forced.
|
||||
* @private
|
||||
*/
|
||||
function seriesOnAfterSetOptions(e) {
|
||||
this.resetA11yMarkerOptions = merge(e.options.marker || {}, this.userOptions.marker || {});
|
||||
}
|
||||
/**
|
||||
* Keep track of forcing markers.
|
||||
* @private
|
||||
*/
|
||||
function seriesOnRender() {
|
||||
var series = this, options = series.options;
|
||||
if (shouldForceMarkers(series)) {
|
||||
if (options.marker && options.marker.enabled === false) {
|
||||
series.a11yMarkersForced = true;
|
||||
forceZeroOpacityMarkerOptions(series.options);
|
||||
}
|
||||
if (hasIndividualPointMarkerOptions(series)) {
|
||||
handleForcePointMarkers(series);
|
||||
}
|
||||
}
|
||||
else if (series.a11yMarkersForced) {
|
||||
delete series.a11yMarkersForced;
|
||||
unforceSeriesMarkerOptions(series);
|
||||
delete series.resetA11yMarkerOptions;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function shouldForceMarkers(series) {
|
||||
var chart = series.chart, chartA11yEnabled = chart.options.accessibility.enabled, seriesA11yEnabled = (series.options.accessibility &&
|
||||
series.options.accessibility.enabled) !== false;
|
||||
return (chartA11yEnabled &&
|
||||
seriesA11yEnabled &&
|
||||
isWithinDescriptionThreshold(series));
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function unforcePointMarkerOptions(pointOptions) {
|
||||
merge(true, pointOptions.marker, {
|
||||
states: {
|
||||
normal: {
|
||||
opacity: getPointMarkerOpacity(pointOptions) || 1
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Reset markers to normal
|
||||
* @private
|
||||
*/
|
||||
function unforceSeriesMarkerOptions(series) {
|
||||
var resetMarkerOptions = series.resetA11yMarkerOptions;
|
||||
if (resetMarkerOptions) {
|
||||
var originalOpactiy = resetMarkerOptions.states &&
|
||||
resetMarkerOptions.states.normal &&
|
||||
resetMarkerOptions.states.normal.opacity;
|
||||
series.update({
|
||||
marker: {
|
||||
enabled: resetMarkerOptions.enabled,
|
||||
states: {
|
||||
normal: { opacity: originalOpactiy }
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
})(ForcedMarkersComposition || (ForcedMarkersComposition = {}));
|
||||
/* *
|
||||
*
|
||||
* Default Export
|
||||
*
|
||||
* */
|
||||
export default ForcedMarkersComposition;
|
||||
@@ -0,0 +1,324 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2009-2021 Øystein Moseng
|
||||
*
|
||||
* Handle announcing new data for a chart.
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../../../Core/Globals.js';
|
||||
import U from '../../../Core/Utilities.js';
|
||||
var addEvent = U.addEvent, defined = U.defined;
|
||||
import Announcer from '../../Utils/Announcer.js';
|
||||
import ChartUtilities from '../../Utils/ChartUtilities.js';
|
||||
var getChartTitle = ChartUtilities.getChartTitle;
|
||||
import EventProvider from '../../Utils/EventProvider.js';
|
||||
import SeriesDescriber from './SeriesDescriber.js';
|
||||
var defaultPointDescriptionFormatter = SeriesDescriber.defaultPointDescriptionFormatter, defaultSeriesDescriptionFormatter = SeriesDescriber.defaultSeriesDescriptionFormatter;
|
||||
/* *
|
||||
*
|
||||
* Functions
|
||||
*
|
||||
* */
|
||||
/* eslint-disable valid-jsdoc */
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function chartHasAnnounceEnabled(chart) {
|
||||
return !!chart.options.accessibility.announceNewData.enabled;
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function findPointInDataArray(point) {
|
||||
var candidates = point.series.data.filter(function (candidate) { return (point.x === candidate.x && point.y === candidate.y); });
|
||||
return candidates.length === 1 ? candidates[0] : point;
|
||||
}
|
||||
/**
|
||||
* Get array of unique series from two arrays
|
||||
* @private
|
||||
*/
|
||||
function getUniqueSeries(arrayA, arrayB) {
|
||||
var uniqueSeries = (arrayA || []).concat(arrayB || []).reduce(function (acc, cur) {
|
||||
acc[cur.name + cur.index] = cur;
|
||||
return acc;
|
||||
}, {});
|
||||
return Object
|
||||
.keys(uniqueSeries)
|
||||
.map(function (ix) { return uniqueSeries[ix]; });
|
||||
}
|
||||
/* *
|
||||
*
|
||||
* Class
|
||||
*
|
||||
* */
|
||||
/**
|
||||
* @private
|
||||
* @class
|
||||
*/
|
||||
var NewDataAnnouncer = /** @class */ (function () {
|
||||
/* *
|
||||
*
|
||||
* Constructor
|
||||
*
|
||||
* */
|
||||
function NewDataAnnouncer(chart) {
|
||||
/* *
|
||||
*
|
||||
* Public
|
||||
*
|
||||
* */
|
||||
this.announcer = void 0;
|
||||
this.dirty = {
|
||||
allSeries: {}
|
||||
};
|
||||
this.eventProvider = void 0;
|
||||
this.lastAnnouncementTime = 0;
|
||||
this.chart = chart;
|
||||
}
|
||||
/* *
|
||||
*
|
||||
* Functions
|
||||
*
|
||||
* */
|
||||
/* eslint-disable valid-jsdoc */
|
||||
/**
|
||||
* Initialize the new data announcer.
|
||||
* @private
|
||||
*/
|
||||
NewDataAnnouncer.prototype.init = function () {
|
||||
var chart = this.chart;
|
||||
var announceOptions = (chart.options.accessibility.announceNewData);
|
||||
var announceType = announceOptions.interruptUser ?
|
||||
'assertive' : 'polite';
|
||||
this.lastAnnouncementTime = 0;
|
||||
this.dirty = {
|
||||
allSeries: {}
|
||||
};
|
||||
this.eventProvider = new EventProvider();
|
||||
this.announcer = new Announcer(chart, announceType);
|
||||
this.addEventListeners();
|
||||
};
|
||||
/**
|
||||
* Remove traces of announcer.
|
||||
* @private
|
||||
*/
|
||||
NewDataAnnouncer.prototype.destroy = function () {
|
||||
this.eventProvider.removeAddedEvents();
|
||||
this.announcer.destroy();
|
||||
};
|
||||
/**
|
||||
* Add event listeners for the announcer
|
||||
* @private
|
||||
*/
|
||||
NewDataAnnouncer.prototype.addEventListeners = function () {
|
||||
var announcer = this, chart = this.chart, e = this.eventProvider;
|
||||
e.addEvent(chart, 'afterApplyDrilldown', function () {
|
||||
announcer.lastAnnouncementTime = 0;
|
||||
});
|
||||
e.addEvent(chart, 'afterAddSeries', function (e) {
|
||||
announcer.onSeriesAdded(e.series);
|
||||
});
|
||||
e.addEvent(chart, 'redraw', function () {
|
||||
announcer.announceDirtyData();
|
||||
});
|
||||
};
|
||||
/**
|
||||
* On new data series added, update dirty list.
|
||||
* @private
|
||||
* @param {Highcharts.Series} series
|
||||
*/
|
||||
NewDataAnnouncer.prototype.onSeriesAdded = function (series) {
|
||||
if (chartHasAnnounceEnabled(this.chart)) {
|
||||
this.dirty.hasDirty = true;
|
||||
this.dirty.allSeries[series.name + series.index] = series;
|
||||
// Add it to newSeries storage unless we already have one
|
||||
this.dirty.newSeries = defined(this.dirty.newSeries) ?
|
||||
void 0 : series;
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Gather what we know and announce the data to user.
|
||||
* @private
|
||||
*/
|
||||
NewDataAnnouncer.prototype.announceDirtyData = function () {
|
||||
var chart = this.chart, announcer = this;
|
||||
if (chart.options.accessibility.announceNewData &&
|
||||
this.dirty.hasDirty) {
|
||||
var newPoint = this.dirty.newPoint;
|
||||
// If we have a single new point, see if we can find it in the
|
||||
// data array. Otherwise we can only pass through options to
|
||||
// the description builder, and it is a bit sparse in info.
|
||||
if (newPoint) {
|
||||
newPoint = findPointInDataArray(newPoint);
|
||||
}
|
||||
this.queueAnnouncement(Object
|
||||
.keys(this.dirty.allSeries)
|
||||
.map(function (ix) {
|
||||
return announcer.dirty.allSeries[ix];
|
||||
}), this.dirty.newSeries, newPoint);
|
||||
// Reset
|
||||
this.dirty = {
|
||||
allSeries: {}
|
||||
};
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Announce to user that there is new data.
|
||||
* @private
|
||||
* @param {Array<Highcharts.Series>} dirtySeries
|
||||
* Array of series with new data.
|
||||
* @param {Highcharts.Series} [newSeries]
|
||||
* If a single new series was added, a reference to this series.
|
||||
* @param {Highcharts.Point} [newPoint]
|
||||
* If a single point was added, a reference to this point.
|
||||
*/
|
||||
NewDataAnnouncer.prototype.queueAnnouncement = function (dirtySeries, newSeries, newPoint) {
|
||||
var _this = this;
|
||||
var chart = this.chart;
|
||||
var annOptions = chart.options.accessibility.announceNewData;
|
||||
if (annOptions.enabled) {
|
||||
var now = +new Date();
|
||||
var dTime = now - this.lastAnnouncementTime;
|
||||
var time = Math.max(0, annOptions.minAnnounceInterval - dTime);
|
||||
// Add series from previously queued announcement.
|
||||
var allSeries = getUniqueSeries(this.queuedAnnouncement && this.queuedAnnouncement.series, dirtySeries);
|
||||
// Build message and announce
|
||||
var message = this.buildAnnouncementMessage(allSeries, newSeries, newPoint);
|
||||
if (message) {
|
||||
// Is there already one queued?
|
||||
if (this.queuedAnnouncement) {
|
||||
clearTimeout(this.queuedAnnouncementTimer);
|
||||
}
|
||||
// Build the announcement
|
||||
this.queuedAnnouncement = {
|
||||
time: now,
|
||||
message: message,
|
||||
series: allSeries
|
||||
};
|
||||
// Queue the announcement
|
||||
this.queuedAnnouncementTimer = setTimeout(function () {
|
||||
if (_this && _this.announcer) {
|
||||
_this.lastAnnouncementTime = +new Date();
|
||||
_this.announcer.announce(_this.queuedAnnouncement.message);
|
||||
delete _this.queuedAnnouncement;
|
||||
delete _this.queuedAnnouncementTimer;
|
||||
}
|
||||
}, time);
|
||||
}
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Get announcement message for new data.
|
||||
* @private
|
||||
* @param {Array<Highcharts.Series>} dirtySeries
|
||||
* Array of series with new data.
|
||||
* @param {Highcharts.Series} [newSeries]
|
||||
* If a single new series was added, a reference to this series.
|
||||
* @param {Highcharts.Point} [newPoint]
|
||||
* If a single point was added, a reference to this point.
|
||||
*
|
||||
* @return {string|null}
|
||||
* The announcement message to give to user.
|
||||
*/
|
||||
NewDataAnnouncer.prototype.buildAnnouncementMessage = function (dirtySeries, newSeries, newPoint) {
|
||||
var chart = this.chart, annOptions = chart.options.accessibility.announceNewData;
|
||||
// User supplied formatter?
|
||||
if (annOptions.announcementFormatter) {
|
||||
var formatterRes = annOptions.announcementFormatter(dirtySeries, newSeries, newPoint);
|
||||
if (formatterRes !== false) {
|
||||
return formatterRes.length ? formatterRes : null;
|
||||
}
|
||||
}
|
||||
// Default formatter - use lang options
|
||||
var multiple = H.charts && H.charts.length > 1 ?
|
||||
'Multiple' : 'Single', langKey = newSeries ? 'newSeriesAnnounce' + multiple :
|
||||
newPoint ? 'newPointAnnounce' + multiple : 'newDataAnnounce', chartTitle = getChartTitle(chart);
|
||||
return chart.langFormat('accessibility.announceNewData.' + langKey, {
|
||||
chartTitle: chartTitle,
|
||||
seriesDesc: newSeries ?
|
||||
defaultSeriesDescriptionFormatter(newSeries) :
|
||||
null,
|
||||
pointDesc: newPoint ?
|
||||
defaultPointDescriptionFormatter(newPoint) :
|
||||
null,
|
||||
point: newPoint,
|
||||
series: newSeries
|
||||
});
|
||||
};
|
||||
return NewDataAnnouncer;
|
||||
}());
|
||||
/* *
|
||||
*
|
||||
* Class Namespace
|
||||
*
|
||||
* */
|
||||
(function (NewDataAnnouncer) {
|
||||
/* *
|
||||
*
|
||||
* Declarations
|
||||
*
|
||||
* */
|
||||
/* *
|
||||
*
|
||||
* Static Properties
|
||||
*
|
||||
* */
|
||||
NewDataAnnouncer.composedClasses = [];
|
||||
/* *
|
||||
*
|
||||
* Static Functions
|
||||
*
|
||||
* */
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function compose(SeriesClass) {
|
||||
if (NewDataAnnouncer.composedClasses.indexOf(SeriesClass) === -1) {
|
||||
NewDataAnnouncer.composedClasses.push(SeriesClass);
|
||||
addEvent(SeriesClass, 'addPoint', seriesOnAddPoint);
|
||||
addEvent(SeriesClass, 'updatedData', seriesOnUpdatedData);
|
||||
}
|
||||
}
|
||||
NewDataAnnouncer.compose = compose;
|
||||
/**
|
||||
* On new point added, update dirty list.
|
||||
* @private
|
||||
* @param {Highcharts.Point} point
|
||||
*/
|
||||
function seriesOnAddPoint(e) {
|
||||
var chart = this.chart, newDataAnnouncer = this.newDataAnnouncer;
|
||||
if (newDataAnnouncer &&
|
||||
newDataAnnouncer.chart === chart &&
|
||||
chartHasAnnounceEnabled(chart)) {
|
||||
// Add it to newPoint storage unless we already have one
|
||||
newDataAnnouncer.dirty.newPoint = (defined(newDataAnnouncer.dirty.newPoint) ?
|
||||
void 0 :
|
||||
e.point);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* On new data in the series, make sure we add it to the dirty list.
|
||||
* @private
|
||||
* @param {Highcharts.Series} series
|
||||
*/
|
||||
function seriesOnUpdatedData() {
|
||||
var chart = this.chart, newDataAnnouncer = this.newDataAnnouncer;
|
||||
if (newDataAnnouncer &&
|
||||
newDataAnnouncer.chart === chart &&
|
||||
chartHasAnnounceEnabled(chart)) {
|
||||
newDataAnnouncer.dirty.hasDirty = true;
|
||||
newDataAnnouncer.dirty.allSeries[this.name + this.index] = this;
|
||||
}
|
||||
}
|
||||
})(NewDataAnnouncer || (NewDataAnnouncer = {}));
|
||||
/* *
|
||||
*
|
||||
* Default Export
|
||||
*
|
||||
* */
|
||||
export default NewDataAnnouncer;
|
||||
@@ -0,0 +1,149 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2009-2021 Øystein Moseng
|
||||
*
|
||||
* Accessibility component for series and points.
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
var __extends = (this && this.__extends) || (function () {
|
||||
var extendStatics = function (d, b) {
|
||||
extendStatics = Object.setPrototypeOf ||
|
||||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
||||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
||||
return extendStatics(d, b);
|
||||
};
|
||||
return function (d, b) {
|
||||
if (typeof b !== "function" && b !== null)
|
||||
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
||||
extendStatics(d, b);
|
||||
function __() { this.constructor = d; }
|
||||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
||||
};
|
||||
})();
|
||||
import AccessibilityComponent from '../../AccessibilityComponent.js';
|
||||
import ChartUtilities from '../../Utils/ChartUtilities.js';
|
||||
var hideSeriesFromAT = ChartUtilities.hideSeriesFromAT;
|
||||
import ForcedMarkers from './ForcedMarkers.js';
|
||||
import NewDataAnnouncer from './NewDataAnnouncer.js';
|
||||
import SeriesDescriber from './SeriesDescriber.js';
|
||||
var describeSeries = SeriesDescriber.describeSeries;
|
||||
import SeriesKeyboardNavigation from './SeriesKeyboardNavigation.js';
|
||||
import Tooltip from '../../../Core/Tooltip.js';
|
||||
/* *
|
||||
*
|
||||
* Class
|
||||
*
|
||||
* */
|
||||
/**
|
||||
* The SeriesComponent class
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.SeriesComponent
|
||||
*/
|
||||
var SeriesComponent = /** @class */ (function (_super) {
|
||||
__extends(SeriesComponent, _super);
|
||||
function SeriesComponent() {
|
||||
return _super !== null && _super.apply(this, arguments) || this;
|
||||
}
|
||||
/* *
|
||||
*
|
||||
* Static Functions
|
||||
*
|
||||
* */
|
||||
/* eslint-disable valid-jsdoc */
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
SeriesComponent.compose = function (ChartClass, PointClass, SeriesClass) {
|
||||
NewDataAnnouncer.compose(SeriesClass);
|
||||
ForcedMarkers.compose(SeriesClass);
|
||||
SeriesKeyboardNavigation.compose(ChartClass, PointClass, SeriesClass);
|
||||
};
|
||||
/* *
|
||||
*
|
||||
* Functions
|
||||
*
|
||||
* */
|
||||
/**
|
||||
* Init the component.
|
||||
*/
|
||||
SeriesComponent.prototype.init = function () {
|
||||
this.newDataAnnouncer = new NewDataAnnouncer(this.chart);
|
||||
this.newDataAnnouncer.init();
|
||||
this.keyboardNavigation = new SeriesKeyboardNavigation(this.chart, this.keyCodes);
|
||||
this.keyboardNavigation.init();
|
||||
this.hideTooltipFromATWhenShown();
|
||||
this.hideSeriesLabelsFromATWhenShown();
|
||||
};
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
SeriesComponent.prototype.hideTooltipFromATWhenShown = function () {
|
||||
var component = this;
|
||||
this.addEvent(Tooltip, 'refresh', function () {
|
||||
if (this.chart === component.chart &&
|
||||
this.label &&
|
||||
this.label.element) {
|
||||
this.label.element.setAttribute('aria-hidden', true);
|
||||
}
|
||||
});
|
||||
};
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
SeriesComponent.prototype.hideSeriesLabelsFromATWhenShown = function () {
|
||||
this.addEvent(this.chart, 'afterDrawSeriesLabels', function () {
|
||||
this.series.forEach(function (series) {
|
||||
if (series.labelBySeries) {
|
||||
series.labelBySeries.attr('aria-hidden', true);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
/**
|
||||
* Called on chart render. It is necessary to do this for render in case
|
||||
* markers change on zoom/pixel density.
|
||||
*/
|
||||
SeriesComponent.prototype.onChartRender = function () {
|
||||
var chart = this.chart;
|
||||
chart.series.forEach(function (series) {
|
||||
var shouldDescribeSeries = (series.options.accessibility &&
|
||||
series.options.accessibility.enabled) !== false &&
|
||||
series.visible;
|
||||
if (shouldDescribeSeries) {
|
||||
describeSeries(series);
|
||||
}
|
||||
else {
|
||||
hideSeriesFromAT(series);
|
||||
}
|
||||
});
|
||||
};
|
||||
/**
|
||||
* Get keyboard navigation handler for this component.
|
||||
* @private
|
||||
*/
|
||||
SeriesComponent.prototype.getKeyboardNavigation = function () {
|
||||
return this.keyboardNavigation.getKeyboardNavigationHandler();
|
||||
};
|
||||
/**
|
||||
* Remove traces
|
||||
* @private
|
||||
*/
|
||||
SeriesComponent.prototype.destroy = function () {
|
||||
this.newDataAnnouncer.destroy();
|
||||
this.keyboardNavigation.destroy();
|
||||
};
|
||||
return SeriesComponent;
|
||||
}(AccessibilityComponent));
|
||||
/* *
|
||||
*
|
||||
* Default Export
|
||||
*
|
||||
* */
|
||||
export default SeriesComponent;
|
||||
@@ -0,0 +1,407 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2009-2021 Øystein Moseng
|
||||
*
|
||||
* Place desriptions on a series and its points.
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import AnnotationsA11y from '../AnnotationsA11y.js';
|
||||
var getPointAnnotationTexts = AnnotationsA11y.getPointAnnotationTexts;
|
||||
import ChartUtilities from '../../Utils/ChartUtilities.js';
|
||||
var getAxisDescription = ChartUtilities.getAxisDescription, getSeriesFirstPointElement = ChartUtilities.getSeriesFirstPointElement, getSeriesA11yElement = ChartUtilities.getSeriesA11yElement, unhideChartElementFromAT = ChartUtilities.unhideChartElementFromAT;
|
||||
import F from '../../../Core/FormatUtilities.js';
|
||||
var format = F.format, numberFormat = F.numberFormat;
|
||||
import HTMLUtilities from '../../Utils/HTMLUtilities.js';
|
||||
var reverseChildNodes = HTMLUtilities.reverseChildNodes, stripHTMLTags = HTMLUtilities.stripHTMLTagsFromString;
|
||||
import U from '../../../Core/Utilities.js';
|
||||
var find = U.find, isNumber = U.isNumber, pick = U.pick, defined = U.defined;
|
||||
/* *
|
||||
*
|
||||
* Functions
|
||||
*
|
||||
* */
|
||||
/* eslint-disable valid-jsdoc */
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function findFirstPointWithGraphic(point) {
|
||||
var sourcePointIndex = point.index;
|
||||
if (!point.series || !point.series.data || !defined(sourcePointIndex)) {
|
||||
return null;
|
||||
}
|
||||
return find(point.series.data, function (p) {
|
||||
return !!(p &&
|
||||
typeof p.index !== 'undefined' &&
|
||||
p.index > sourcePointIndex &&
|
||||
p.graphic &&
|
||||
p.graphic.element);
|
||||
}) || null;
|
||||
}
|
||||
/**
|
||||
* Whether or not we should add a dummy point element in
|
||||
* order to describe a point that has no graphic.
|
||||
* @private
|
||||
*/
|
||||
function shouldAddDummyPoint(point) {
|
||||
// Note: Sunburst series use isNull for hidden points on drilldown.
|
||||
// Ignore these.
|
||||
var series = point.series, chart = series && series.chart, isSunburst = series && series.is('sunburst'), isNull = point.isNull, shouldDescribeNull = chart &&
|
||||
chart
|
||||
.options.accessibility.point.describeNull;
|
||||
return isNull && !isSunburst && shouldDescribeNull;
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function makeDummyElement(point, pos) {
|
||||
var renderer = point.series.chart.renderer, dummy = renderer.rect(pos.x, pos.y, 1, 1);
|
||||
dummy.attr({
|
||||
'class': 'highcharts-a11y-dummy-point',
|
||||
fill: 'none',
|
||||
opacity: 0,
|
||||
'fill-opacity': 0,
|
||||
'stroke-opacity': 0
|
||||
});
|
||||
return dummy;
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function addDummyPointElement(point) {
|
||||
var series = point.series, firstPointWithGraphic = findFirstPointWithGraphic(point), firstGraphic = firstPointWithGraphic && firstPointWithGraphic.graphic, parentGroup = firstGraphic ?
|
||||
firstGraphic.parentGroup :
|
||||
series.graph || series.group, dummyPos = firstPointWithGraphic ? {
|
||||
x: pick(point.plotX, firstPointWithGraphic.plotX, 0),
|
||||
y: pick(point.plotY, firstPointWithGraphic.plotY, 0)
|
||||
} : {
|
||||
x: pick(point.plotX, 0),
|
||||
y: pick(point.plotY, 0)
|
||||
}, dummyElement = makeDummyElement(point, dummyPos);
|
||||
if (parentGroup && parentGroup.element) {
|
||||
point.graphic = dummyElement;
|
||||
point.hasDummyGraphic = true;
|
||||
dummyElement.add(parentGroup);
|
||||
// Move to correct pos in DOM
|
||||
parentGroup.element.insertBefore(dummyElement.element, firstGraphic ? firstGraphic.element : null);
|
||||
return dummyElement.element;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function hasMorePointsThanDescriptionThreshold(series) {
|
||||
var chartA11yOptions = series.chart.options.accessibility, threshold = (chartA11yOptions.series.pointDescriptionEnabledThreshold);
|
||||
return !!(threshold !== false &&
|
||||
series.points &&
|
||||
series.points.length >= threshold);
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function shouldSetScreenReaderPropsOnPoints(series) {
|
||||
var seriesA11yOptions = series.options.accessibility || {};
|
||||
return !hasMorePointsThanDescriptionThreshold(series) &&
|
||||
!seriesA11yOptions.exposeAsGroupOnly;
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function shouldSetKeyboardNavPropsOnPoints(series) {
|
||||
var chartA11yOptions = series.chart.options.accessibility, seriesNavOptions = chartA11yOptions.keyboardNavigation.seriesNavigation;
|
||||
return !!(series.points && (series.points.length <
|
||||
seriesNavOptions.pointNavigationEnabledThreshold ||
|
||||
seriesNavOptions.pointNavigationEnabledThreshold === false));
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function shouldDescribeSeriesElement(series) {
|
||||
var chart = series.chart, chartOptions = chart.options.chart, chartHas3d = chartOptions.options3d && chartOptions.options3d.enabled, hasMultipleSeries = chart.series.length > 1, describeSingleSeriesOption = chart.options.accessibility.series.describeSingleSeries, exposeAsGroupOnlyOption = (series.options.accessibility || {}).exposeAsGroupOnly, noDescribe3D = chartHas3d && hasMultipleSeries;
|
||||
return !noDescribe3D && (hasMultipleSeries || describeSingleSeriesOption ||
|
||||
exposeAsGroupOnlyOption || hasMorePointsThanDescriptionThreshold(series));
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function pointNumberToString(point, value) {
|
||||
var series = point.series, chart = series.chart, a11yPointOptions = chart.options.accessibility.point || {}, seriesA11yPointOptions = series.options.accessibility &&
|
||||
series.options.accessibility.point || {}, tooltipOptions = series.tooltipOptions || {}, lang = chart.options.lang;
|
||||
if (isNumber(value)) {
|
||||
return numberFormat(value, seriesA11yPointOptions.valueDecimals ||
|
||||
a11yPointOptions.valueDecimals ||
|
||||
tooltipOptions.valueDecimals ||
|
||||
-1, lang.decimalPoint, lang.accessibility.thousandsSep || lang.thousandsSep);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function getSeriesDescriptionText(series) {
|
||||
var seriesA11yOptions = series.options.accessibility || {}, descOpt = seriesA11yOptions.description;
|
||||
return descOpt && series.chart.langFormat('accessibility.series.description', {
|
||||
description: descOpt,
|
||||
series: series
|
||||
}) || '';
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function getSeriesAxisDescriptionText(series, axisCollection) {
|
||||
var axis = series[axisCollection];
|
||||
return series.chart.langFormat('accessibility.series.' + axisCollection + 'Description', {
|
||||
name: getAxisDescription(axis),
|
||||
series: series
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Get accessible time description for a point on a datetime axis.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function getPointA11yTimeDescription(point) {
|
||||
var series = point.series, chart = series.chart, seriesA11yOptions = series.options.accessibility &&
|
||||
series.options.accessibility.point || {}, a11yOptions = chart.options.accessibility.point || {}, dateXAxis = series.xAxis && series.xAxis.dateTime;
|
||||
if (dateXAxis) {
|
||||
var tooltipDateFormat = dateXAxis.getXDateFormat(point.x || 0, chart.options.tooltip.dateTimeLabelFormats), dateFormat = seriesA11yOptions.dateFormatter &&
|
||||
seriesA11yOptions.dateFormatter(point) ||
|
||||
a11yOptions.dateFormatter && a11yOptions.dateFormatter(point) ||
|
||||
seriesA11yOptions.dateFormat ||
|
||||
a11yOptions.dateFormat ||
|
||||
tooltipDateFormat;
|
||||
return chart.time.dateFormat(dateFormat, point.x || 0, void 0);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function getPointXDescription(point) {
|
||||
var timeDesc = getPointA11yTimeDescription(point), xAxis = point.series.xAxis || {}, pointCategory = xAxis.categories && defined(point.category) &&
|
||||
('' + point.category).replace('<br/>', ' '), canUseId = defined(point.id) &&
|
||||
('' + point.id).indexOf('highcharts-') < 0, fallback = 'x, ' + point.x;
|
||||
return point.name || timeDesc || pointCategory ||
|
||||
(canUseId ? point.id : fallback);
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function getPointArrayMapValueDescription(point, prefix, suffix) {
|
||||
var pre = prefix || '', suf = suffix || '', keyToValStr = function (key) {
|
||||
var num = pointNumberToString(point, pick(point[key], point.options[key]));
|
||||
return key + ': ' + pre + num + suf;
|
||||
}, pointArrayMap = point.series.pointArrayMap;
|
||||
return pointArrayMap.reduce(function (desc, key) {
|
||||
return desc + (desc.length ? ', ' : '') + keyToValStr(key);
|
||||
}, '');
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function getPointValue(point) {
|
||||
var series = point.series, a11yPointOpts = series.chart.options.accessibility.point || {}, seriesA11yPointOpts = series.chart.options.accessibility &&
|
||||
series.chart.options.accessibility.point || {}, tooltipOptions = series.tooltipOptions || {}, valuePrefix = seriesA11yPointOpts.valuePrefix ||
|
||||
a11yPointOpts.valuePrefix ||
|
||||
tooltipOptions.valuePrefix ||
|
||||
'', valueSuffix = seriesA11yPointOpts.valueSuffix ||
|
||||
a11yPointOpts.valueSuffix ||
|
||||
tooltipOptions.valueSuffix ||
|
||||
'', fallbackKey = (typeof point.value !==
|
||||
'undefined' ?
|
||||
'value' : 'y'), fallbackDesc = pointNumberToString(point, point[fallbackKey]);
|
||||
if (point.isNull) {
|
||||
return series.chart.langFormat('accessibility.series.nullPointValue', {
|
||||
point: point
|
||||
});
|
||||
}
|
||||
if (series.pointArrayMap) {
|
||||
return getPointArrayMapValueDescription(point, valuePrefix, valueSuffix);
|
||||
}
|
||||
return valuePrefix + fallbackDesc + valueSuffix;
|
||||
}
|
||||
/**
|
||||
* Return the description for the annotation(s) connected to a point, or
|
||||
* empty string if none.
|
||||
*
|
||||
* @private
|
||||
* @param {Highcharts.Point} point
|
||||
* The data point to get the annotation info from.
|
||||
* @return {string}
|
||||
* Annotation description
|
||||
*/
|
||||
function getPointAnnotationDescription(point) {
|
||||
var chart = point.series.chart;
|
||||
var langKey = 'accessibility.series.pointAnnotationsDescription';
|
||||
var annotations = getPointAnnotationTexts(point);
|
||||
var context = { point: point, annotations: annotations };
|
||||
return annotations.length ? chart.langFormat(langKey, context) : '';
|
||||
}
|
||||
/**
|
||||
* Return string with information about point.
|
||||
* @private
|
||||
*/
|
||||
function getPointValueDescription(point) {
|
||||
var series = point.series, chart = series.chart, seriesA11yOptions = series.options.accessibility, seriesValueDescFormat = seriesA11yOptions && seriesA11yOptions.point &&
|
||||
seriesA11yOptions.point.valueDescriptionFormat, pointValueDescriptionFormat = seriesValueDescFormat ||
|
||||
chart.options.accessibility.point.valueDescriptionFormat, showXDescription = pick(series.xAxis &&
|
||||
series.xAxis.options.accessibility &&
|
||||
series.xAxis.options.accessibility.enabled, !chart.angular), xDesc = showXDescription ? getPointXDescription(point) : '', context = {
|
||||
point: point,
|
||||
index: defined(point.index) ? (point.index + 1) : '',
|
||||
xDescription: xDesc,
|
||||
value: getPointValue(point),
|
||||
separator: showXDescription ? ', ' : ''
|
||||
};
|
||||
return format(pointValueDescriptionFormat, context, chart);
|
||||
}
|
||||
/**
|
||||
* Return string with information about point.
|
||||
* @private
|
||||
*/
|
||||
function defaultPointDescriptionFormatter(point) {
|
||||
var series = point.series, shouldExposeSeriesName = series.chart.series.length > 1 ||
|
||||
series.options.name, valText = getPointValueDescription(point), description = point.options && point.options.accessibility &&
|
||||
point.options.accessibility.description, userDescText = description ? ' ' + description : '', seriesNameText = shouldExposeSeriesName ? ' ' + series.name + '.' : '', annotationsDesc = getPointAnnotationDescription(point), pointAnnotationsText = annotationsDesc ? ' ' + annotationsDesc : '';
|
||||
point.accessibility = point.accessibility || {};
|
||||
point.accessibility.valueDescription = valText;
|
||||
return valText + userDescText + seriesNameText + pointAnnotationsText;
|
||||
}
|
||||
/**
|
||||
* Set a11y props on a point element
|
||||
* @private
|
||||
* @param {Highcharts.Point} point
|
||||
* @param {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement} pointElement
|
||||
*/
|
||||
function setPointScreenReaderAttribs(point, pointElement) {
|
||||
var series = point.series, a11yPointOptions = series.chart.options.accessibility.point || {}, seriesPointA11yOptions = series.options.accessibility &&
|
||||
series.options.accessibility.point || {}, label = stripHTMLTags(seriesPointA11yOptions.descriptionFormatter &&
|
||||
seriesPointA11yOptions.descriptionFormatter(point) ||
|
||||
a11yPointOptions.descriptionFormatter &&
|
||||
a11yPointOptions.descriptionFormatter(point) ||
|
||||
defaultPointDescriptionFormatter(point));
|
||||
pointElement.setAttribute('role', 'img');
|
||||
pointElement.setAttribute('aria-label', label);
|
||||
}
|
||||
/**
|
||||
* Add accessible info to individual point elements of a series
|
||||
* @private
|
||||
* @param {Highcharts.Series} series
|
||||
*/
|
||||
function describePointsInSeries(series) {
|
||||
var setScreenReaderProps = shouldSetScreenReaderPropsOnPoints(series), setKeyboardProps = shouldSetKeyboardNavPropsOnPoints(series), shouldDescribeNullPoints = series.chart.options.accessibility
|
||||
.point.describeNull;
|
||||
if (setScreenReaderProps || setKeyboardProps) {
|
||||
series.points.forEach(function (point) {
|
||||
var pointEl = point.graphic && point.graphic.element ||
|
||||
shouldAddDummyPoint(point) && addDummyPointElement(point), pointA11yDisabled = (point.options &&
|
||||
point.options.accessibility &&
|
||||
point.options.accessibility.enabled === false);
|
||||
if (pointEl) {
|
||||
if (point.isNull && !shouldDescribeNullPoints) {
|
||||
pointEl.setAttribute('aria-hidden', true);
|
||||
return;
|
||||
}
|
||||
// We always set tabindex, as long as we are setting props.
|
||||
// When setting tabindex, also remove default outline to
|
||||
// avoid ugly border on click.
|
||||
pointEl.setAttribute('tabindex', '-1');
|
||||
if (!series.chart.styledMode) {
|
||||
pointEl.style.outline = 'none';
|
||||
}
|
||||
if (setScreenReaderProps && !pointA11yDisabled) {
|
||||
setPointScreenReaderAttribs(point, pointEl);
|
||||
}
|
||||
else {
|
||||
pointEl.setAttribute('aria-hidden', true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Return string with information about series.
|
||||
* @private
|
||||
*/
|
||||
function defaultSeriesDescriptionFormatter(series) {
|
||||
var chart = series.chart, chartTypes = chart.types || [], description = getSeriesDescriptionText(series), shouldDescribeAxis = function (coll) {
|
||||
return chart[coll] && chart[coll].length > 1 && series[coll];
|
||||
}, seriesNumber = series.index + 1, xAxisInfo = getSeriesAxisDescriptionText(series, 'xAxis'), yAxisInfo = getSeriesAxisDescriptionText(series, 'yAxis'), summaryContext = {
|
||||
seriesNumber: seriesNumber,
|
||||
series: series,
|
||||
chart: chart
|
||||
}, combinationSuffix = chartTypes.length > 1 ? 'Combination' : '', summary = chart.langFormat('accessibility.series.summary.' + series.type + combinationSuffix, summaryContext) || chart.langFormat('accessibility.series.summary.default' + combinationSuffix, summaryContext), axisDescription = (shouldDescribeAxis('yAxis') ? ' ' + yAxisInfo + '.' : '') + (shouldDescribeAxis('xAxis') ? ' ' + xAxisInfo + '.' : ''), formatStr = chart.options.accessibility.series.descriptionFormat || '';
|
||||
return format(formatStr, {
|
||||
seriesDescription: summary,
|
||||
authorDescription: (description ? ' ' + description : ''),
|
||||
axisDescription: axisDescription,
|
||||
series: series,
|
||||
chart: chart,
|
||||
seriesNumber: seriesNumber
|
||||
}, void 0);
|
||||
}
|
||||
/**
|
||||
* Set a11y props on a series element
|
||||
* @private
|
||||
* @param {Highcharts.Series} series
|
||||
* @param {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement} seriesElement
|
||||
*/
|
||||
function describeSeriesElement(series, seriesElement) {
|
||||
var seriesA11yOptions = series.options.accessibility || {}, a11yOptions = series.chart.options.accessibility, landmarkVerbosity = a11yOptions.landmarkVerbosity;
|
||||
// Handle role attribute
|
||||
if (seriesA11yOptions.exposeAsGroupOnly) {
|
||||
seriesElement.setAttribute('role', 'img');
|
||||
}
|
||||
else if (landmarkVerbosity === 'all') {
|
||||
seriesElement.setAttribute('role', 'region');
|
||||
}
|
||||
else {
|
||||
seriesElement.setAttribute('role', 'group');
|
||||
}
|
||||
seriesElement.setAttribute('tabindex', '-1');
|
||||
if (!series.chart.styledMode) {
|
||||
// Don't show browser outline on click, despite tabindex
|
||||
seriesElement.style.outline = 'none';
|
||||
}
|
||||
seriesElement.setAttribute('aria-label', stripHTMLTags(a11yOptions.series.descriptionFormatter &&
|
||||
a11yOptions.series.descriptionFormatter(series) ||
|
||||
defaultSeriesDescriptionFormatter(series)));
|
||||
}
|
||||
/**
|
||||
* Put accessible info on series and points of a series.
|
||||
* @param {Highcharts.Series} series The series to add info on.
|
||||
*/
|
||||
function describeSeries(series) {
|
||||
var chart = series.chart, firstPointEl = getSeriesFirstPointElement(series), seriesEl = getSeriesA11yElement(series), is3d = chart.is3d && chart.is3d();
|
||||
if (seriesEl) {
|
||||
// For some series types the order of elements do not match the
|
||||
// order of points in series. In that case we have to reverse them
|
||||
// in order for AT to read them out in an understandable order.
|
||||
// Due to z-index issues we cannot do this for 3D charts.
|
||||
if (seriesEl.lastChild === firstPointEl && !is3d) {
|
||||
reverseChildNodes(seriesEl);
|
||||
}
|
||||
describePointsInSeries(series);
|
||||
unhideChartElementFromAT(chart, seriesEl);
|
||||
if (shouldDescribeSeriesElement(series)) {
|
||||
describeSeriesElement(series, seriesEl);
|
||||
}
|
||||
else {
|
||||
seriesEl.removeAttribute('aria-label');
|
||||
}
|
||||
}
|
||||
}
|
||||
/* *
|
||||
*
|
||||
* Default Export
|
||||
*
|
||||
* */
|
||||
var SeriesDescriber = {
|
||||
defaultPointDescriptionFormatter: defaultPointDescriptionFormatter,
|
||||
defaultSeriesDescriptionFormatter: defaultSeriesDescriptionFormatter,
|
||||
describeSeries: describeSeries
|
||||
};
|
||||
export default SeriesDescriber;
|
||||
@@ -0,0 +1,707 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2009-2021 Øystein Moseng
|
||||
*
|
||||
* Handle keyboard navigation for series.
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import Point from '../../../Core/Series/Point.js';
|
||||
import Series from '../../../Core/Series/Series.js';
|
||||
import SeriesRegistry from '../../../Core/Series/SeriesRegistry.js';
|
||||
var seriesTypes = SeriesRegistry.seriesTypes;
|
||||
import H from '../../../Core/Globals.js';
|
||||
var doc = H.doc;
|
||||
import U from '../../../Core/Utilities.js';
|
||||
var defined = U.defined, fireEvent = U.fireEvent;
|
||||
import KeyboardNavigationHandler from '../../KeyboardNavigationHandler.js';
|
||||
import EventProvider from '../../Utils/EventProvider.js';
|
||||
import ChartUtilities from '../../Utils/ChartUtilities.js';
|
||||
var getPointFromXY = ChartUtilities.getPointFromXY, getSeriesFromName = ChartUtilities.getSeriesFromName, scrollToPoint = ChartUtilities.scrollToPoint;
|
||||
/* *
|
||||
*
|
||||
* Functions
|
||||
*
|
||||
* */
|
||||
/* eslint-disable valid-jsdoc */
|
||||
/**
|
||||
* Get the index of a point in a series. This is needed when using e.g. data
|
||||
* grouping.
|
||||
*
|
||||
* @private
|
||||
* @function getPointIndex
|
||||
* @param {Highcharts.AccessibilityPoint} point
|
||||
* The point to find index of.
|
||||
* @return {number|undefined}
|
||||
* The index in the series.points array of the point.
|
||||
*/
|
||||
function getPointIndex(point) {
|
||||
var index = point.index, points = point.series.points;
|
||||
var i = points.length;
|
||||
if (points[index] !== point) {
|
||||
while (i--) {
|
||||
if (points[i] === point) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Determine if series navigation should be skipped
|
||||
* @private
|
||||
*/
|
||||
function isSkipSeries(series) {
|
||||
var a11yOptions = series.chart.options.accessibility, seriesNavOptions = a11yOptions.keyboardNavigation.seriesNavigation, seriesA11yOptions = series.options.accessibility || {}, seriesKbdNavOptions = seriesA11yOptions.keyboardNavigation;
|
||||
return seriesKbdNavOptions && seriesKbdNavOptions.enabled === false ||
|
||||
seriesA11yOptions.enabled === false ||
|
||||
series.options.enableMouseTracking === false || // #8440
|
||||
!series.visible ||
|
||||
// Skip all points in a series where pointNavigationEnabledThreshold is
|
||||
// reached
|
||||
(seriesNavOptions.pointNavigationEnabledThreshold &&
|
||||
seriesNavOptions.pointNavigationEnabledThreshold <=
|
||||
series.points.length);
|
||||
}
|
||||
/**
|
||||
* Determine if navigation for a point should be skipped
|
||||
* @private
|
||||
*/
|
||||
function isSkipPoint(point) {
|
||||
var a11yOptions = point.series.chart.options.accessibility;
|
||||
var pointA11yDisabled = (point.options.accessibility &&
|
||||
point.options.accessibility.enabled === false);
|
||||
return point.isNull &&
|
||||
a11yOptions.keyboardNavigation.seriesNavigation.skipNullPoints ||
|
||||
point.visible === false ||
|
||||
point.isInside === false ||
|
||||
pointA11yDisabled ||
|
||||
isSkipSeries(point.series);
|
||||
}
|
||||
/**
|
||||
* Get the first point that is not a skip point in this series.
|
||||
* @private
|
||||
*/
|
||||
function getFirstValidPointInSeries(series) {
|
||||
var points = series.points || [], len = points.length;
|
||||
for (var i = 0; i < len; ++i) {
|
||||
if (!isSkipPoint(points[i])) {
|
||||
return points[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Get the first point that is not a skip point in this chart.
|
||||
* @private
|
||||
*/
|
||||
function getFirstValidPointInChart(chart) {
|
||||
var series = chart.series || [], len = series.length;
|
||||
for (var i = 0; i < len; ++i) {
|
||||
if (!isSkipSeries(series[i])) {
|
||||
var point = getFirstValidPointInSeries(series[i]);
|
||||
if (point) {
|
||||
return point;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function highlightLastValidPointInChart(chart) {
|
||||
var numSeries = chart.series.length;
|
||||
var i = numSeries, res = false;
|
||||
while (i--) {
|
||||
chart.highlightedPoint = chart.series[i].points[chart.series[i].points.length - 1];
|
||||
// Highlight first valid point in the series will also
|
||||
// look backwards. It always starts from currently
|
||||
// highlighted point.
|
||||
res = chart.series[i].highlightNextValidPoint();
|
||||
if (res) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
/**
|
||||
* After drilling down/up, we need to set focus to the first point for
|
||||
* screen readers and keyboard nav.
|
||||
* @private
|
||||
*/
|
||||
function updateChartFocusAfterDrilling(chart) {
|
||||
var point = getFirstValidPointInChart(chart);
|
||||
if (point) {
|
||||
point.highlight(false); // Do not visually highlight
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Highlight the first point in chart that is not a skip point
|
||||
* @private
|
||||
*/
|
||||
function highlightFirstValidPointInChart(chart) {
|
||||
delete chart.highlightedPoint;
|
||||
var point = getFirstValidPointInChart(chart);
|
||||
return point ? point.highlight() : false;
|
||||
}
|
||||
/* *
|
||||
*
|
||||
* Class
|
||||
*
|
||||
* */
|
||||
/**
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.SeriesKeyboardNavigation
|
||||
*/
|
||||
var SeriesKeyboardNavigation = /** @class */ (function () {
|
||||
/* *
|
||||
*
|
||||
* Constructor
|
||||
*
|
||||
* */
|
||||
function SeriesKeyboardNavigation(chart, keyCodes) {
|
||||
this.keyCodes = keyCodes;
|
||||
this.chart = chart;
|
||||
}
|
||||
/* *
|
||||
*
|
||||
* Functions
|
||||
*
|
||||
* */
|
||||
/* eslint-disable valid-jsdoc */
|
||||
/**
|
||||
* Init the keyboard navigation
|
||||
*/
|
||||
SeriesKeyboardNavigation.prototype.init = function () {
|
||||
var keyboardNavigation = this, chart = this.chart, e = this.eventProvider = new EventProvider();
|
||||
e.addEvent(Series, 'destroy', function () {
|
||||
return keyboardNavigation.onSeriesDestroy(this);
|
||||
});
|
||||
e.addEvent(chart, 'afterApplyDrilldown', function () {
|
||||
updateChartFocusAfterDrilling(this);
|
||||
});
|
||||
e.addEvent(chart, 'drilldown', function (e) {
|
||||
var point = e.point, series = point.series;
|
||||
keyboardNavigation.lastDrilledDownPoint = {
|
||||
x: point.x,
|
||||
y: point.y,
|
||||
seriesName: series ? series.name : ''
|
||||
};
|
||||
});
|
||||
e.addEvent(chart, 'drillupall', function () {
|
||||
setTimeout(function () {
|
||||
keyboardNavigation.onDrillupAll();
|
||||
}, 10);
|
||||
});
|
||||
// Heatmaps et al. alter z-index in setState, causing elements
|
||||
// to lose focus
|
||||
e.addEvent(Point, 'afterSetState', function () {
|
||||
var point = this;
|
||||
var pointEl = point.graphic && point.graphic.element;
|
||||
var focusedElement = doc.activeElement;
|
||||
// VO brings focus with it to container, causing series nav to run.
|
||||
// If then navigating with virtual cursor, it is possible to leave
|
||||
// keyboard nav module state on the data points and still activate
|
||||
// proxy buttons.
|
||||
var focusedElClassName = (focusedElement && focusedElement.getAttribute('class'));
|
||||
var isProxyFocused = focusedElClassName &&
|
||||
focusedElClassName.indexOf('highcharts-a11y-proxy-button') > -1;
|
||||
if (chart.highlightedPoint === point &&
|
||||
focusedElement !== pointEl &&
|
||||
!isProxyFocused &&
|
||||
pointEl &&
|
||||
pointEl.focus) {
|
||||
pointEl.focus();
|
||||
}
|
||||
});
|
||||
};
|
||||
/**
|
||||
* After drillup we want to find the point that was drilled down to and
|
||||
* highlight it.
|
||||
* @private
|
||||
*/
|
||||
SeriesKeyboardNavigation.prototype.onDrillupAll = function () {
|
||||
var last = this.lastDrilledDownPoint, chart = this.chart, series = last && getSeriesFromName(chart, last.seriesName);
|
||||
var point;
|
||||
if (last && series && defined(last.x) && defined(last.y)) {
|
||||
point = getPointFromXY(series, last.x, last.y);
|
||||
}
|
||||
point = point || getFirstValidPointInChart(chart);
|
||||
// Container focus can be lost on drillup due to deleted elements.
|
||||
if (chart.container) {
|
||||
chart.container.focus();
|
||||
}
|
||||
if (point && point.highlight) {
|
||||
point.highlight(false); // Do not visually highlight
|
||||
}
|
||||
};
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
SeriesKeyboardNavigation.prototype.getKeyboardNavigationHandler = function () {
|
||||
var keyboardNavigation = this, keys = this.keyCodes, chart = this.chart, inverted = chart.inverted;
|
||||
return new KeyboardNavigationHandler(chart, {
|
||||
keyCodeMap: [
|
||||
[inverted ? [keys.up, keys.down] : [keys.left, keys.right],
|
||||
function (keyCode) {
|
||||
return keyboardNavigation.onKbdSideways(this, keyCode);
|
||||
}],
|
||||
[inverted ? [keys.left, keys.right] : [keys.up, keys.down],
|
||||
function (keyCode) {
|
||||
return keyboardNavigation.onKbdVertical(this, keyCode);
|
||||
}],
|
||||
[[keys.enter, keys.space],
|
||||
function (keyCode, event) {
|
||||
var point = chart.highlightedPoint;
|
||||
if (point) {
|
||||
event.point = point;
|
||||
fireEvent(point.series, 'click', event);
|
||||
point.firePointEvent('click');
|
||||
}
|
||||
return this.response.success;
|
||||
}],
|
||||
[[keys.home],
|
||||
function () {
|
||||
highlightFirstValidPointInChart(chart);
|
||||
return this.response.success;
|
||||
}],
|
||||
[[keys.end],
|
||||
function () {
|
||||
highlightLastValidPointInChart(chart);
|
||||
return this.response.success;
|
||||
}],
|
||||
[[keys.pageDown, keys.pageUp],
|
||||
function (keyCode) {
|
||||
chart.highlightAdjacentSeries(keyCode === keys.pageDown);
|
||||
return this.response.success;
|
||||
}]
|
||||
],
|
||||
init: function () {
|
||||
return keyboardNavigation.onHandlerInit(this);
|
||||
},
|
||||
validate: function () {
|
||||
return !!getFirstValidPointInChart(chart);
|
||||
},
|
||||
terminate: function () {
|
||||
return keyboardNavigation.onHandlerTerminate();
|
||||
}
|
||||
});
|
||||
};
|
||||
/**
|
||||
* @private
|
||||
* @param {Highcharts.KeyboardNavigationHandler} handler
|
||||
* @param {number} keyCode
|
||||
* @return {number}
|
||||
* response
|
||||
*/
|
||||
SeriesKeyboardNavigation.prototype.onKbdSideways = function (handler, keyCode) {
|
||||
var keys = this.keyCodes, isNext = keyCode === keys.right || keyCode === keys.down;
|
||||
return this.attemptHighlightAdjacentPoint(handler, isNext);
|
||||
};
|
||||
/**
|
||||
* When keyboard navigation inits.
|
||||
* @private
|
||||
* @param {Highcharts.KeyboardNavigationHandler} handler The handler object
|
||||
* @return {number}
|
||||
* response
|
||||
*/
|
||||
SeriesKeyboardNavigation.prototype.onHandlerInit = function (handler) {
|
||||
var chart = this.chart, kbdNavOptions = chart.options.accessibility.keyboardNavigation;
|
||||
if (kbdNavOptions.seriesNavigation.rememberPointFocus &&
|
||||
chart.highlightedPoint) {
|
||||
chart.highlightedPoint.highlight();
|
||||
}
|
||||
else {
|
||||
highlightFirstValidPointInChart(chart);
|
||||
}
|
||||
return handler.response.success;
|
||||
};
|
||||
/**
|
||||
* @private
|
||||
* @param {Highcharts.KeyboardNavigationHandler} handler
|
||||
* @param {number} keyCode
|
||||
* @return {number}
|
||||
* response
|
||||
*/
|
||||
SeriesKeyboardNavigation.prototype.onKbdVertical = function (handler, keyCode) {
|
||||
var chart = this.chart, keys = this.keyCodes, isNext = keyCode === keys.down || keyCode === keys.right, navOptions = chart.options.accessibility.keyboardNavigation
|
||||
.seriesNavigation;
|
||||
// Handle serialized mode, act like left/right
|
||||
if (navOptions.mode && navOptions.mode === 'serialize') {
|
||||
return this.attemptHighlightAdjacentPoint(handler, isNext);
|
||||
}
|
||||
// Normal mode, move between series
|
||||
var highlightMethod = (chart.highlightedPoint &&
|
||||
chart.highlightedPoint.series.keyboardMoveVertical) ?
|
||||
'highlightAdjacentPointVertical' :
|
||||
'highlightAdjacentSeries';
|
||||
chart[highlightMethod](isNext);
|
||||
return handler.response.success;
|
||||
};
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
SeriesKeyboardNavigation.prototype.onHandlerTerminate = function () {
|
||||
var chart = this.chart, kbdNavOptions = chart.options.accessibility.keyboardNavigation;
|
||||
if (chart.tooltip) {
|
||||
chart.tooltip.hide(0);
|
||||
}
|
||||
var hoverSeries = (chart.highlightedPoint && chart.highlightedPoint.series);
|
||||
if (hoverSeries && hoverSeries.onMouseOut) {
|
||||
hoverSeries.onMouseOut();
|
||||
}
|
||||
if (chart.highlightedPoint && chart.highlightedPoint.onMouseOut) {
|
||||
chart.highlightedPoint.onMouseOut();
|
||||
}
|
||||
if (!kbdNavOptions.seriesNavigation.rememberPointFocus) {
|
||||
delete chart.highlightedPoint;
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Function that attempts to highlight next/prev point. Handles wrap around.
|
||||
* @private
|
||||
*/
|
||||
SeriesKeyboardNavigation.prototype.attemptHighlightAdjacentPoint = function (handler, directionIsNext) {
|
||||
var chart = this.chart, wrapAround = chart.options.accessibility.keyboardNavigation
|
||||
.wrapAround, highlightSuccessful = chart.highlightAdjacentPoint(directionIsNext);
|
||||
if (!highlightSuccessful) {
|
||||
if (wrapAround && (directionIsNext ?
|
||||
highlightFirstValidPointInChart(chart) :
|
||||
highlightLastValidPointInChart(chart))) {
|
||||
return handler.response.success;
|
||||
}
|
||||
return handler.response[directionIsNext ? 'next' : 'prev'];
|
||||
}
|
||||
return handler.response.success;
|
||||
};
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
SeriesKeyboardNavigation.prototype.onSeriesDestroy = function (series) {
|
||||
var chart = this.chart, currentHighlightedPointDestroyed = chart.highlightedPoint &&
|
||||
chart.highlightedPoint.series === series;
|
||||
if (currentHighlightedPointDestroyed) {
|
||||
delete chart.highlightedPoint;
|
||||
if (chart.focusElement) {
|
||||
chart.focusElement.removeFocusBorder();
|
||||
}
|
||||
}
|
||||
};
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
SeriesKeyboardNavigation.prototype.destroy = function () {
|
||||
this.eventProvider.removeAddedEvents();
|
||||
};
|
||||
return SeriesKeyboardNavigation;
|
||||
}());
|
||||
/* *
|
||||
*
|
||||
* Class Namespace
|
||||
*
|
||||
* */
|
||||
(function (SeriesKeyboardNavigation) {
|
||||
/* *
|
||||
*
|
||||
* Declarations
|
||||
*
|
||||
* */
|
||||
/* *
|
||||
*
|
||||
* Constants
|
||||
*
|
||||
* */
|
||||
var composedClasses = [];
|
||||
/* *
|
||||
*
|
||||
* Functions
|
||||
*
|
||||
* */
|
||||
/**
|
||||
* Function to highlight next/previous point in chart.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.Chart#highlightAdjacentPoint
|
||||
*
|
||||
* @param {boolean} next
|
||||
* Flag for the direction.
|
||||
*
|
||||
* @return {Highcharts.Point|boolean}
|
||||
* Returns highlighted point on success, false on failure (no adjacent point
|
||||
* to highlight in chosen direction).
|
||||
*/
|
||||
function chartHighlightAdjacentPoint(next) {
|
||||
var chart = this, series = chart.series, curPoint = chart.highlightedPoint, curPointIndex = curPoint && getPointIndex(curPoint) || 0, curPoints = curPoint && curPoint.series.points || [], lastSeries = chart.series && chart.series[chart.series.length - 1], lastPoint = lastSeries &&
|
||||
lastSeries.points &&
|
||||
lastSeries.points[lastSeries.points.length - 1];
|
||||
var newSeries, newPoint;
|
||||
// If no points, return false
|
||||
if (!series[0] || !series[0].points) {
|
||||
return false;
|
||||
}
|
||||
if (!curPoint) {
|
||||
// No point is highlighted yet. Try first/last point depending on
|
||||
// move direction
|
||||
newPoint = next ? series[0].points[0] : lastPoint;
|
||||
}
|
||||
else {
|
||||
// We have a highlighted point. Grab next/prev point & series.
|
||||
newSeries = series[curPoint.series.index + (next ? 1 : -1)];
|
||||
newPoint = curPoints[curPointIndex + (next ? 1 : -1)];
|
||||
if (!newPoint && newSeries) {
|
||||
// Done with this series, try next one
|
||||
newPoint = newSeries.points[next ? 0 : newSeries.points.length - 1];
|
||||
}
|
||||
// If there is no adjacent point, we return false
|
||||
if (!newPoint) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Recursively skip points
|
||||
if (isSkipPoint(newPoint)) {
|
||||
// If we skip this whole series, move to the end of the series
|
||||
// before we recurse, just to optimize
|
||||
newSeries = newPoint.series;
|
||||
if (isSkipSeries(newSeries)) {
|
||||
chart.highlightedPoint = next ?
|
||||
newSeries.points[newSeries.points.length - 1] :
|
||||
newSeries.points[0];
|
||||
}
|
||||
else {
|
||||
// Otherwise, just move one point
|
||||
chart.highlightedPoint = newPoint;
|
||||
}
|
||||
// Retry
|
||||
return chart.highlightAdjacentPoint(next);
|
||||
}
|
||||
// There is an adjacent point, highlight it
|
||||
return newPoint.highlight();
|
||||
}
|
||||
/**
|
||||
* Highlight the closest point vertically.
|
||||
* @private
|
||||
*/
|
||||
function chartHighlightAdjacentPointVertical(down) {
|
||||
var curPoint = this.highlightedPoint;
|
||||
var minDistance = Infinity, bestPoint;
|
||||
if (!defined(curPoint.plotX) || !defined(curPoint.plotY)) {
|
||||
return false;
|
||||
}
|
||||
this.series.forEach(function (series) {
|
||||
if (isSkipSeries(series)) {
|
||||
return;
|
||||
}
|
||||
series.points.forEach(function (point) {
|
||||
if (!defined(point.plotY) || !defined(point.plotX) ||
|
||||
point === curPoint) {
|
||||
return;
|
||||
}
|
||||
var yDistance = point.plotY - curPoint.plotY;
|
||||
var width = Math.abs(point.plotX - curPoint.plotX), distance = Math.abs(yDistance) * Math.abs(yDistance) +
|
||||
width * width * 4; // Weigh horizontal distance highly
|
||||
// Reverse distance number if axis is reversed
|
||||
if (series.yAxis && series.yAxis.reversed) {
|
||||
yDistance *= -1;
|
||||
}
|
||||
if (yDistance <= 0 && down || yDistance >= 0 && !down ||
|
||||
distance < 5 || // Points in same spot => infinite loop
|
||||
isSkipPoint(point)) {
|
||||
return;
|
||||
}
|
||||
if (distance < minDistance) {
|
||||
minDistance = distance;
|
||||
bestPoint = point;
|
||||
}
|
||||
});
|
||||
});
|
||||
return bestPoint ? bestPoint.highlight() : false;
|
||||
}
|
||||
/**
|
||||
* Highlight next/previous series in chart. Returns false if no adjacent
|
||||
* series in the direction, otherwise returns new highlighted point.
|
||||
* @private
|
||||
*/
|
||||
function chartHighlightAdjacentSeries(down) {
|
||||
var chart = this, curPoint = chart.highlightedPoint, lastSeries = chart.series && chart.series[chart.series.length - 1], lastPoint = lastSeries && lastSeries.points &&
|
||||
lastSeries.points[lastSeries.points.length - 1];
|
||||
var newSeries, newPoint, adjacentNewPoint;
|
||||
// If no point is highlighted, highlight the first/last point
|
||||
if (!chart.highlightedPoint) {
|
||||
newSeries = down ? (chart.series && chart.series[0]) : lastSeries;
|
||||
newPoint = down ?
|
||||
(newSeries && newSeries.points && newSeries.points[0]) :
|
||||
lastPoint;
|
||||
return newPoint ? newPoint.highlight() : false;
|
||||
}
|
||||
newSeries = (chart.series[curPoint.series.index + (down ? -1 : 1)]);
|
||||
if (!newSeries) {
|
||||
return false;
|
||||
}
|
||||
// We have a new series in this direction, find the right point
|
||||
// Weigh xDistance as counting much higher than Y distance
|
||||
newPoint = getClosestPoint(curPoint, newSeries, 4);
|
||||
if (!newPoint) {
|
||||
return false;
|
||||
}
|
||||
// New series and point exists, but we might want to skip it
|
||||
if (isSkipSeries(newSeries)) {
|
||||
// Skip the series
|
||||
newPoint.highlight();
|
||||
// Try recurse
|
||||
adjacentNewPoint = chart.highlightAdjacentSeries(down);
|
||||
if (!adjacentNewPoint) {
|
||||
// Recurse failed
|
||||
curPoint.highlight();
|
||||
return false;
|
||||
}
|
||||
// Recurse succeeded
|
||||
return adjacentNewPoint;
|
||||
}
|
||||
// Highlight the new point or any first valid point back or forwards
|
||||
// from it
|
||||
newPoint.highlight();
|
||||
return newPoint.series.highlightNextValidPoint();
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function compose(ChartClass, PointClass, SeriesClass) {
|
||||
if (composedClasses.indexOf(ChartClass) === -1) {
|
||||
composedClasses.push(ChartClass);
|
||||
var chartProto = ChartClass.prototype;
|
||||
chartProto.highlightAdjacentPoint = chartHighlightAdjacentPoint;
|
||||
chartProto.highlightAdjacentPointVertical = (chartHighlightAdjacentPointVertical);
|
||||
chartProto.highlightAdjacentSeries = chartHighlightAdjacentSeries;
|
||||
}
|
||||
if (composedClasses.indexOf(PointClass) === -1) {
|
||||
composedClasses.push(PointClass);
|
||||
var pointProto = PointClass.prototype;
|
||||
pointProto.highlight = pointHighlight;
|
||||
}
|
||||
if (composedClasses.indexOf(SeriesClass) === -1) {
|
||||
composedClasses.push(SeriesClass);
|
||||
var seriesProto = SeriesClass.prototype;
|
||||
/**
|
||||
* Set for which series types it makes sense to move to the closest
|
||||
* point with up/down arrows, and which series types should just
|
||||
* move to next series.
|
||||
* @private
|
||||
*/
|
||||
seriesProto.keyboardMoveVertical = true;
|
||||
[
|
||||
'column',
|
||||
'gantt',
|
||||
'pie'
|
||||
].forEach(function (type) {
|
||||
if (seriesTypes[type]) {
|
||||
seriesTypes[type].prototype.keyboardMoveVertical = false;
|
||||
}
|
||||
});
|
||||
seriesProto.highlightNextValidPoint = (seriesHighlightNextValidPoint);
|
||||
}
|
||||
}
|
||||
SeriesKeyboardNavigation.compose = compose;
|
||||
/**
|
||||
* Get the point in a series that is closest (in pixel distance) to a
|
||||
* reference point. Optionally supply weight factors for x and y directions.
|
||||
* @private
|
||||
*/
|
||||
function getClosestPoint(point, series, xWeight, yWeight) {
|
||||
var minDistance = Infinity, dPoint, minIx, distance, i = series.points.length;
|
||||
var hasUndefinedPosition = function (point) { return (!(defined(point.plotX) && defined(point.plotY))); };
|
||||
if (hasUndefinedPosition(point)) {
|
||||
return;
|
||||
}
|
||||
while (i--) {
|
||||
dPoint = series.points[i];
|
||||
if (hasUndefinedPosition(dPoint)) {
|
||||
continue;
|
||||
}
|
||||
distance = (point.plotX - dPoint.plotX) *
|
||||
(point.plotX - dPoint.plotX) *
|
||||
(xWeight || 1) +
|
||||
(point.plotY - dPoint.plotY) *
|
||||
(point.plotY - dPoint.plotY) *
|
||||
(yWeight || 1);
|
||||
if (distance < minDistance) {
|
||||
minDistance = distance;
|
||||
minIx = i;
|
||||
}
|
||||
}
|
||||
return defined(minIx) ? series.points[minIx] : void 0;
|
||||
}
|
||||
/**
|
||||
* Highlights a point (show tooltip, display hover state, focus element).
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.Point#highlight
|
||||
*
|
||||
* @return {Highcharts.Point}
|
||||
* This highlighted point.
|
||||
*/
|
||||
function pointHighlight(highlightVisually) {
|
||||
if (highlightVisually === void 0) { highlightVisually = true; }
|
||||
var chart = this.series.chart;
|
||||
if (!this.isNull && highlightVisually) {
|
||||
this.onMouseOver(); // Show the hover marker and tooltip
|
||||
}
|
||||
else {
|
||||
if (chart.tooltip) {
|
||||
chart.tooltip.hide(0);
|
||||
}
|
||||
// Do not call blur on the element, as it messes up the focus of the
|
||||
// div element of the chart
|
||||
}
|
||||
scrollToPoint(this);
|
||||
// We focus only after calling onMouseOver because the state change can
|
||||
// change z-index and mess up the element.
|
||||
if (this.graphic) {
|
||||
chart.setFocusToElement(this.graphic);
|
||||
if (!highlightVisually && chart.focusElement) {
|
||||
chart.focusElement.removeFocusBorder();
|
||||
}
|
||||
}
|
||||
chart.highlightedPoint = this;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Highlight first valid point in a series. Returns the point if
|
||||
* successfully highlighted, otherwise false. If there is a highlighted
|
||||
* point in the series, use that as starting point.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.Series#highlightNextValidPoint
|
||||
*/
|
||||
function seriesHighlightNextValidPoint() {
|
||||
var curPoint = this.chart.highlightedPoint, start = (curPoint && curPoint.series) === this ?
|
||||
getPointIndex(curPoint) :
|
||||
0, points = this.points, len = points.length;
|
||||
if (points && len) {
|
||||
for (var i = start; i < len; ++i) {
|
||||
if (!isSkipPoint(points[i])) {
|
||||
return points[i].highlight();
|
||||
}
|
||||
}
|
||||
for (var j = start; j >= 0; --j) {
|
||||
if (!isSkipPoint(points[j])) {
|
||||
return points[j].highlight();
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
})(SeriesKeyboardNavigation || (SeriesKeyboardNavigation = {}));
|
||||
/* *
|
||||
*
|
||||
* Default Export
|
||||
*
|
||||
* */
|
||||
export default SeriesKeyboardNavigation;
|
||||
Reference in New Issue
Block a user