tupali/librerias/gantt/code/es-modules/modules/accessibility/components/MenuComponent.js
2020-05-23 15:45:54 -05:00

357 lines
12 KiB
JavaScript

/* *
*
* (c) 2009-2020 Øystein Moseng
*
* Accessibility component for exporting menu.
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
'use strict';
import H from '../../../parts/Globals.js';
import U from '../../../parts/Utilities.js';
var extend = U.extend;
import AccessibilityComponent from '../AccessibilityComponent.js';
import KeyboardNavigationHandler from '../KeyboardNavigationHandler.js';
import ChartUtilities from '../utils/chartUtilities.js';
var unhideChartElementFromAT = ChartUtilities.unhideChartElementFromAT;
import HTMLUtilities from '../utils/htmlUtilities.js';
var removeElement = HTMLUtilities.removeElement, getFakeMouseEvent = HTMLUtilities.getFakeMouseEvent;
/* eslint-disable no-invalid-this, valid-jsdoc */
/**
* Get the wrapped export button element of a chart.
*
* @private
* @param {Highcharts.Chart} chart
* @returns {Highcharts.SVGElement}
*/
function getExportMenuButtonElement(chart) {
return chart.exportSVGElements && chart.exportSVGElements[0];
}
/**
* Show the export menu and focus the first item (if exists).
*
* @private
* @function Highcharts.Chart#showExportMenu
*/
H.Chart.prototype.showExportMenu = function () {
var exportButton = getExportMenuButtonElement(this);
if (exportButton) {
var el = exportButton.element;
if (el.onclick) {
el.onclick(getFakeMouseEvent('click'));
}
}
};
/**
* @private
* @function Highcharts.Chart#hideExportMenu
*/
H.Chart.prototype.hideExportMenu = function () {
var chart = this, exportList = chart.exportDivElements;
if (exportList && chart.exportContextMenu) {
// Reset hover states etc.
exportList.forEach(function (el) {
if (el.className === 'highcharts-menu-item' && el.onmouseout) {
el.onmouseout(getFakeMouseEvent('mouseout'));
}
});
chart.highlightedExportItemIx = 0;
// Hide the menu div
chart.exportContextMenu.hideMenu();
// Make sure the chart has focus and can capture keyboard events
chart.container.focus();
}
};
/**
* Highlight export menu item by index.
*
* @private
* @function Highcharts.Chart#highlightExportItem
*
* @param {number} ix
*
* @return {boolean}
*/
H.Chart.prototype.highlightExportItem = function (ix) {
var listItem = this.exportDivElements && this.exportDivElements[ix], curHighlighted = this.exportDivElements &&
this.exportDivElements[this.highlightedExportItemIx], hasSVGFocusSupport;
if (listItem &&
listItem.tagName === 'LI' &&
!(listItem.children && listItem.children.length)) {
// Test if we have focus support for SVG elements
hasSVGFocusSupport = !!(this.renderTo.getElementsByTagName('g')[0] || {}).focus;
// Only focus if we can set focus back to the elements after
// destroying the menu (#7422)
if (listItem.focus && hasSVGFocusSupport) {
listItem.focus();
}
if (curHighlighted && curHighlighted.onmouseout) {
curHighlighted.onmouseout(getFakeMouseEvent('mouseout'));
}
if (listItem.onmouseover) {
listItem.onmouseover(getFakeMouseEvent('mouseover'));
}
this.highlightedExportItemIx = ix;
return true;
}
return false;
};
/**
* Try to highlight the last valid export menu item.
*
* @private
* @function Highcharts.Chart#highlightLastExportItem
* @return {boolean}
*/
H.Chart.prototype.highlightLastExportItem = function () {
var chart = this, i;
if (chart.exportDivElements) {
i = chart.exportDivElements.length;
while (i--) {
if (chart.highlightExportItem(i)) {
return true;
}
}
}
return false;
};
/**
* @private
* @param {Highcharts.Chart} chart
*/
function exportingShouldHaveA11y(chart) {
var exportingOpts = chart.options.exporting, exportButton = getExportMenuButtonElement(chart);
return !!(exportingOpts &&
exportingOpts.enabled !== false &&
exportingOpts.accessibility &&
exportingOpts.accessibility.enabled &&
exportButton &&
exportButton.element);
}
/**
* The MenuComponent class
*
* @private
* @class
* @name Highcharts.MenuComponent
*/
var MenuComponent = function () { };
MenuComponent.prototype = new AccessibilityComponent();
extend(MenuComponent.prototype, /** @lends Highcharts.MenuComponent */ {
/**
* Init the component
*/
init: function () {
var chart = this.chart, component = this;
this.addEvent(chart, 'exportMenuShown', function () {
component.onMenuShown();
});
this.addEvent(chart, 'exportMenuHidden', function () {
component.onMenuHidden();
});
},
/**
* @private
*/
onMenuHidden: function () {
var menu = this.chart.exportContextMenu;
if (menu) {
menu.setAttribute('aria-hidden', 'true');
}
this.isExportMenuShown = false;
this.setExportButtonExpandedState('false');
},
/**
* @private
*/
onMenuShown: function () {
var chart = this.chart, menu = chart.exportContextMenu;
if (menu) {
this.addAccessibleContextMenuAttribs();
unhideChartElementFromAT(chart, menu);
}
this.isExportMenuShown = true;
this.setExportButtonExpandedState('true');
},
/**
* @private
* @param {string} stateStr
*/
setExportButtonExpandedState: function (stateStr) {
var button = this.exportButtonProxy;
if (button) {
button.setAttribute('aria-expanded', stateStr);
}
},
/**
* Called on each render of the chart. We need to update positioning of the
* proxy overlay.
*/
onChartRender: function () {
var chart = this.chart, a11yOptions = chart.options.accessibility;
// Always start with a clean slate
removeElement(this.exportProxyGroup);
// Set screen reader properties on export menu
if (exportingShouldHaveA11y(chart)) {
// Proxy button and group
this.exportProxyGroup = this.addProxyGroup(
// Wrap in a region div if verbosity is high
a11yOptions.landmarkVerbosity === 'all' ? {
'aria-label': chart.langFormat('accessibility.exporting.exportRegionLabel', { chart: chart }),
'role': 'region'
} : {});
var button = getExportMenuButtonElement(this.chart);
this.exportButtonProxy = this.createProxyButton(button, this.exportProxyGroup, {
'aria-label': chart.langFormat('accessibility.exporting.menuButtonLabel', { chart: chart }),
'aria-expanded': 'false'
});
}
},
/**
* @private
*/
addAccessibleContextMenuAttribs: function () {
var chart = this.chart, exportList = chart.exportDivElements;
if (exportList && exportList.length) {
// Set tabindex on the menu items to allow focusing by script
// Set role to give screen readers a chance to pick up the contents
exportList.forEach(function (item) {
if (item.tagName === 'LI' &&
!(item.children && item.children.length)) {
item.setAttribute('tabindex', -1);
}
else {
item.setAttribute('aria-hidden', 'true');
}
});
// Set accessibility properties on parent div
var parentDiv = exportList[0].parentNode;
parentDiv.removeAttribute('aria-hidden');
parentDiv.setAttribute('aria-label', chart.langFormat('accessibility.exporting.chartMenuLabel', { chart: chart }));
}
},
/**
* Get keyboard navigation handler for this component.
* @return {Highcharts.KeyboardNavigationHandler}
*/
getKeyboardNavigation: function () {
var keys = this.keyCodes, chart = this.chart, component = this;
return new KeyboardNavigationHandler(chart, {
keyCodeMap: [
// Arrow prev handler
[
[keys.left, keys.up],
function () {
return component.onKbdPrevious(this);
}
],
// Arrow next handler
[
[keys.right, keys.down],
function () {
return component.onKbdNext(this);
}
],
// Click handler
[
[keys.enter, keys.space],
function () {
return component.onKbdClick(this);
}
],
// ESC handler
[
[keys.esc],
function () {
return this.response.prev;
}
]
],
// Only run exporting navigation if exporting support exists and is
// enabled on chart
validate: function () {
return chart.exportChart &&
chart.options.exporting.enabled !== false &&
chart.options.exporting.accessibility.enabled !==
false;
},
// Focus export menu button
init: function () {
var exportBtn = component.exportButtonProxy, exportGroup = chart.exportingGroup;
if (exportGroup && exportBtn) {
chart.setFocusToElement(exportGroup, exportBtn);
}
},
// Hide the menu
terminate: function () {
chart.hideExportMenu();
}
});
},
/**
* @private
* @param {Highcharts.KeyboardNavigationHandler} keyboardNavigationHandler
* @return {number}
* Response code
*/
onKbdPrevious: function (keyboardNavigationHandler) {
var chart = this.chart, a11yOptions = chart.options.accessibility, response = keyboardNavigationHandler.response, i = chart.highlightedExportItemIx || 0;
// Try to highlight prev item in list. Highlighting e.g.
// separators will fail.
while (i--) {
if (chart.highlightExportItem(i)) {
return response.success;
}
}
// We failed, so wrap around or move to prev module
if (a11yOptions.keyboardNavigation.wrapAround) {
chart.highlightLastExportItem();
return response.success;
}
return response.prev;
},
/**
* @private
* @param {Highcharts.KeyboardNavigationHandler} keyboardNavigationHandler
* @return {number}
* Response code
*/
onKbdNext: function (keyboardNavigationHandler) {
var chart = this.chart, a11yOptions = chart.options.accessibility, response = keyboardNavigationHandler.response, i = (chart.highlightedExportItemIx || 0) + 1;
// Try to highlight next item in list. Highlighting e.g.
// separators will fail.
for (; i < chart.exportDivElements.length; ++i) {
if (chart.highlightExportItem(i)) {
return response.success;
}
}
// We failed, so wrap around or move to next module
if (a11yOptions.keyboardNavigation.wrapAround) {
chart.highlightExportItem(0);
return response.success;
}
return response.next;
},
/**
* @private
* @param {Highcharts.KeyboardNavigationHandler} keyboardNavigationHandler
* @return {number}
* Response code
*/
onKbdClick: function (keyboardNavigationHandler) {
var chart = this.chart, curHighlightedItem = chart.exportDivElements[chart.highlightedExportItemIx], exportButtonElement = getExportMenuButtonElement(chart).element;
if (this.isExportMenuShown) {
this.fakeClickEvent(curHighlightedItem);
}
else {
this.fakeClickEvent(exportButtonElement);
chart.highlightExportItem(0);
}
return keyboardNavigationHandler.response.success;
}
});
export default MenuComponent;