@openui5/sap.ui.core
Version:
OpenUI5 Core Library sap.ui.core
255 lines (221 loc) • 9.11 kB
JavaScript
/*!
* OpenUI5
* (c) Copyright 2009-2021 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
/*
* IMPORTANT: This is a private module, its API must not be used and is subject to change.
* Code other than the OpenUI5 libraries must not introduce dependencies to this module.
*/
sap.ui.define([
'sap/ui/thirdparty/jquery',
'sap/ui/dom/jquery/Selectors'
], function(jQuery/*, sapTabbable */) {
"use strict";
/**
* Central handler for F6 key event. Based on the current target and the given event the next element in the F6 chain is focused.
*
* This handler might be also called manually. In this case the central handler is deactivated for the given event.
*
* If the event is not a keydown event, it does not represent the F6 key, the default behavior is prevented,
* the handling is explicitly skipped (<code>oSettings.skip</code>) or the target (<code>oSettings.target</code>) is not contained
* in the used scopes (<code>oSettings.scope</code>), the event is skipped.
*
* @namespace
* @since 1.58
* @alias module:sap/ui/events/F6Navigation
* @private
* @ui5-restricted sap.ui.core, sap.m, sap.uxap
*/
var F6Navigation = {};
/**
* CustomData attribute name for fast navigation groups (in DOM additional prefix "data-" is needed)
*
* @type string
* @const
* @private
* @ui5-restricted sap.ui.core, sap.m, sap.uxap
*/
F6Navigation.fastNavigationKey = "sap-ui-fastnavgroup";
/**
* Handles the F6 key event.
*
* @private
* @ui5-restricted sap.ui.core, sap.m, sap.uxap
* @param {jQuery.Event} oEvent a <code>keydown</code> event object.
* @param {object} [oSettings] further options in case the handler is called manually.
* @param {boolean} [oSettings.skip=false] whether the event should be ignored by the central handler (see above)
* @param {Element} [oSettings.target=document.activeElement] the DOMNode which should be used as starting point to find the next DOMNode in the F6 chain.
* @param {Element[]} [oSettings.scope=[document]] the DOMNodes(s) which are used for the F6 chain search
*/
F6Navigation.handleF6GroupNavigation = function(oEvent, oSettings) {
// Returns the nearest parent DomRef of the given DomRef with attribute data-sap-ui-customfastnavgroup="true".
function findClosestCustomGroup(oRef) {
var $Group = jQuery(oRef).closest('[data-sap-ui-customfastnavgroup="true"]');
return $Group[0];
}
// Returns the nearest parent DomRef of the given DomRef with attribute data-sap-ui-fastnavgroup="true" or
// (if available) the nearest parent with attribute data-sap-ui-customfastnavgroup="true".
function findClosestGroup(oRef) {
var oGroup = findClosestCustomGroup(oRef);
if (oGroup) {
return oGroup;
}
var $Group = jQuery(oRef).closest('[data-' + F6Navigation.fastNavigationKey + '="true"]');
return $Group[0];
}
// Returns a jQuery object which contains all next/previous (bNext) tabbable DOM elements of the given starting point (oRef) within the given scopes (DOMRefs)
function findTabbables(oRef, aScopes, bNext) {
var $Ref = jQuery(oRef),
$All, $Tabbables;
if (bNext) {
$All = jQuery.merge($Ref.find("*"), jQuery.merge($Ref.nextAll(), $Ref.parents().nextAll()));
$Tabbables = $All.find(':sapTabbable').addBack(':sapTabbable');
} else {
$All = jQuery.merge($Ref.prevAll(), $Ref.parents().prevAll());
$Tabbables = jQuery.merge($Ref.parents(':sapTabbable'), $All.find(':sapTabbable').addBack(':sapTabbable'));
}
var $Tabbables = jQuery.uniqueSort($Tabbables);
return $Tabbables.filter(function() {
return isContained(aScopes, this);
});
}
// Filters all elements in the given jQuery object which are in the static UIArea and which are not in the given scopes.
function filterStaticAreaContent($Refs, aScopes) {
var oStaticArea = window.document.getElementById("sap-ui-static");
if (!oStaticArea) {
return $Refs;
}
var aScopesInStaticArea = [];
for (var i = 0; i < aScopes.length; i++) {
if (jQuery.contains(oStaticArea, aScopes[i])) {
aScopesInStaticArea.push(aScopes[i]);
}
}
return $Refs.filter(function() {
if (aScopesInStaticArea.length && isContained(aScopesInStaticArea, this)) {
return true;
}
return !jQuery.contains(oStaticArea, this);
});
}
// Checks whether the given DomRef is contained or equals (in) one of the given container
function isContained(aContainers, oRef) {
for (var i = 0; i < aContainers.length; i++) {
if (aContainers[i] === oRef || jQuery.contains(aContainers[i], oRef)) {
return true;
}
}
return false;
}
//see navigate() (bForward = false)
function findFirstTabbableOfPreviousGroup($FirstTabbableInScope, $Tabbables, oSouceGroup, bFindPreviousGroup) {
var oGroup, $Target;
for (var i = $Tabbables.length - 1; i >= 0; i--) {
oGroup = findClosestGroup($Tabbables[i]);
if (oGroup != oSouceGroup) {
if (bFindPreviousGroup) {
//First find last tabbable of previous group and remember this new group (named "X" in the following comments)
oSouceGroup = oGroup;
bFindPreviousGroup = false;
} else {
//Then starting from group X and try to find again the last tabbable of previous group (named "Y")
//-> Jump one tabbable back to get the first tabbable of X
$Target = jQuery($Tabbables[i + 1]);
break;
}
}
}
if (!$Target && !bFindPreviousGroup) {
//Group X found but not group Y -> X is the first group -> Focus the first tabbable scope (e.g. page) element
$Target = $FirstTabbableInScope;
}
return $Target;
}
// Finds the next/previous (bForward) element in the F6 chain starting from the given source element within the given scopes and focus it
function navigate(oSource, aScopes, bForward) {
if (!aScopes || aScopes.length == 0) {
aScopes = [document];
}
if (!isContained(aScopes, oSource)) {
return;
}
var oSouceGroup = findClosestGroup(oSource),
$AllTabbables = filterStaticAreaContent(jQuery(aScopes).find(':sapTabbable').addBack(':sapTabbable'), aScopes),
$FirstTabbableInScope = $AllTabbables.first(),
$Tabbables = filterStaticAreaContent(findTabbables(oSource, aScopes, bForward), aScopes),
oGroup, $Target;
if (bForward) {
//Find the first next tabbable within another group
for (var i = 0; i < $Tabbables.length; i++) {
oGroup = findClosestGroup($Tabbables[i]);
if (oGroup != oSouceGroup) {
$Target = jQuery($Tabbables[i]);
break;
}
}
//If not found, end of scope (e.g. page) is reached -> Focus the first tabbable scope (e.g. page) element
if (!$Target || !$Target.length) {
$Target = $FirstTabbableInScope;
}
} else {
$Target = findFirstTabbableOfPreviousGroup($FirstTabbableInScope, $Tabbables, oSouceGroup, true);
if (!$Target || !$Target.length) {
//No other group found before -> find first element of last group in the scope (e.g. page)
if ($AllTabbables.length == 1) {
//Only one tabbable element -> use it
$Target = jQuery($AllTabbables[0]);
} else if ($AllTabbables.length > 1) {
oSouceGroup = findClosestGroup($AllTabbables.eq(-1));
oGroup = findClosestGroup($AllTabbables.eq(-2));
if (oSouceGroup != oGroup) {
//Last tabbable scope (e.g. page) element and the previous tabbable scope (e.g. page) element have different groups -> last tabbable scope (e.g. page) element is first tabbable element of its group
$Target = $AllTabbables.eq(-1);
} else {
//Take last tabbable scope (e.g. page) element as reference and start search for first tabbable of the same group
$Target = findFirstTabbableOfPreviousGroup($FirstTabbableInScope, $AllTabbables, oSouceGroup, false);
}
}
}
}
if ($Target && $Target.length) {
var oTarget = $Target[0],
oEvent = null,
oCustomGroup = findClosestCustomGroup(oTarget);
if (oCustomGroup && oCustomGroup.id) {
var oControl = sap.ui.getCore().byId(oCustomGroup.id);
if (oControl) {
oEvent = jQuery.Event("BeforeFastNavigationFocus");
oEvent.target = oTarget;
oEvent.source = oSource;
oEvent.forward = bForward;
oControl._handleEvent(oEvent);
}
}
if (!oEvent || !oEvent.isDefaultPrevented()) {
oTarget.focus();
}
}
}
if (oEvent.type != "keydown" ||
oEvent.key != 'F6' ||
oEvent.isMarked("sapui5_handledF6GroupNavigation") ||
oEvent.isMarked() ||
oEvent.isDefaultPrevented()) {
return;
}
oEvent.setMark("sapui5_handledF6GroupNavigation");
oEvent.setMarked();
oEvent.preventDefault();
if (oSettings && oSettings.skip) {
return;
}
var oTarget = oSettings && oSettings.target ? oSettings.target : document.activeElement,
aScopes = null;
if (oSettings && oSettings.scope) {
aScopes = Array.isArray(oSettings.scope) ? oSettings.scope : [oSettings.scope];
}
navigate(oTarget, aScopes, !oEvent.shiftKey);
};
return F6Navigation;
});