@openui5/sap.tnt
Version:
OpenUI5 UI Library sap.tnt
594 lines (506 loc) • 16.2 kB
JavaScript
/*!
* OpenUI5
* (c) Copyright 2026 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
sap.ui.define([
"./library",
"sap/ui/core/Item",
"sap/ui/core/Icon",
"sap/ui/core/Lib",
"sap/ui/events/KeyCodes",
"sap/base/i18n/Localization"
], function (library, Item, Icon, Lib, KeyCodes, Localization) {
"use strict";
const EXPAND_ICON_SRC = "sap-icon://navigation-right-arrow";
const COLLAPSE_ICON_SRC = "sap-icon://navigation-down-arrow";
const EXTERNAL_LINK_ICON = "sap-icon://arrow-right";
/**
* Constructor for a new <code>NavigationListItemBase</code>.
*
* @param {string} [sId] ID for the new control, generated automatically if no ID is given
* @param {object} [mSettings] Initial settings for the new control
*
* @class
* The <code>NavigationListItemBase</code> class represents a base class for the items that are accepted by the <code>NavigationList</code> control.
* @extends sap.ui.core.Item
*
* @author SAP SE
* @version 1.146.0
*
* @constructor
* @public
* @abstract
* @since 1.121
* @alias sap.tnt.NavigationListItemBase
*/
const NavigationListItemBase = Item.extend("sap.tnt.NavigationListItemBase", /** @lends sap.tnt.NavigationListItemBase.prototype */ {
metadata: {
library: "sap.tnt",
properties: {
/**
* Specifies if the item is expanded.
* @since 1.121
*/
expanded: { type: "boolean", group: "Misc", defaultValue: true },
/**
* Specifies if the item is allowed to be expanded or collapsed by the user.
*/
hasExpander: { type: "boolean", group: "Misc", defaultValue: true },
/**
* Specifies if the item should be shown.
*
* @since 1.121
*/
visible: { type: "boolean", group: "Appearance", defaultValue: true }
},
aggregations: {
/**
* The icon for the expanded items.
* @since 1.121
*/
_expandIconControl: { type: "sap.ui.core.Icon", multiple: false, visibility: "hidden" },
/**
* The icon for external links.
* @since 1.121
*/
_externalLinkIconControl: { type: "sap.ui.core.Icon", multiple: false, visibility: "hidden" }
},
events: {
/**
* Fired when an item is pressed.
* @since 1.133
*/
press: {
allowPreventDefault: true,
parameters: {
/**
* The pressed item.
*/
item: { type: "sap.ui.core.Item" },
/**
* Indicates whether the CTRL key was pressed when the link was selected.
* @since 1.137
*/
ctrlKey: { type: "boolean" },
/**
* Indicates whether the Shift key was pressed when the link was selected.
* @since 1.137
*/
shiftKey: { type: "boolean" },
/**
* Indicates whether the Alt key was pressed when the link was selected.
* @since 1.137
*/
altKey: { type: "boolean" },
/**
* Indicates whether the "meta" key was pressed when the link was selected.
*
* On Macintosh keyboards, this is the command key (⌘).
* On Windows keyboards, this is the windows key (⊞).
*
* @since 1.137
*/
metaKey: { type: "boolean" }
}
}
}
}
});
NavigationListItemBase.prototype.init = function () {
this._resourceBundleCore = Lib.getResourceBundleFor("sap.ui.core");
this._resourceBundleTnt = Lib.getResourceBundleFor("sap.tnt");
};
NavigationListItemBase.prototype.exit = function () {
this._resourceBundleCore = null;
this._resourceBundleTnt = null;
};
/**
* Gets the tree level of this item.
*
* @private
* @returns {number} level
*/
NavigationListItemBase.prototype.getLevel = function () {
const iBaseLevel = 0;
const oParent = this.getParent();
if (oParent && oParent.isA("sap.tnt.NavigationListItem")) {
return oParent.getLevel() + 1;
}
return iBaseLevel;
};
/**
* Gets the <code>NavigationList</code> control, which holds this item.
*
* @private
* @returns {sap.tnt.NavigationList} control instance
*/
NavigationListItemBase.prototype.getNavigationList = function () {
let oParent = this.getParent();
while (oParent && !oParent.isA("sap.tnt.NavigationList")) {
oParent = oParent.getParent();
}
return oParent;
};
/**
* Returns false if any of the parents up the chain of items has its property enabled set to false.
* Otherwise returns true.
*
* @private
* @returns {boolean} enabled
*/
NavigationListItemBase.prototype.getAllParentsEnabled = function () {
let oParent = this.getParent();
while (oParent && !oParent.isA("sap.tnt.NavigationList")) {
if (!oParent.getEnabled()) {
return false;
}
oParent = oParent.getParent();
}
return true;
};
/**
* Returns true if the parent <code>NavigationList</code> control is expanded.
*
* @private
* @returns {boolean} expanded
*/
NavigationListItemBase.prototype._isListExpanded = function () {
const oNavList = this.getNavigationList();
if (!oNavList) {
return false;
}
return oNavList.getExpanded() || oNavList.hasStyleClass("sapTntNLPopup");
};
/**
* If the item doesn't have a key, the function returns the ID of the <code>NavigationListItem</code>,
* so the <code>NavigationList</code> can remember the selected item.
*
* @private
* @returns {string} key
*/
NavigationListItemBase.prototype._getUniqueKey = function () {
const sKey = this.getKey();
return sKey ? sKey : this.getId();
};
/**
* Gets DOM references of the navigation items.
* @abstract
*/
NavigationListItemBase.prototype._getFocusDomRefs = function () { };
/**
* Returns the <code>sap.ui.core.Icon</code> control used to display the expand/collapse icon.
*
* @returns {sap.ui.core.Icon} Icon control instance
* @private
*/
NavigationListItemBase.prototype._getExpandIconControl = function () {
let oIcon = this.getAggregation("_expandIconControl");
if (!oIcon) {
const bExpanded = this.getExpanded();
oIcon = new Icon({
src: bExpanded ? COLLAPSE_ICON_SRC : EXPAND_ICON_SRC,
visible: !!this.getItems()?.length && this.getHasExpander(),
useIconTooltip: false,
tooltip: this._getExpandIconTooltip(!bExpanded)
}).addStyleClass(`sapTntNLIExpandIcon ${this._getExpandIconStyleClass()}`);
this.setAggregation("_expandIconControl", oIcon, true);
}
return oIcon;
};
/**
* Returns the <code>sap.ui.core.Icon</code> control used to display the external link icon.
*
* @returns {sap.ui.core.Icon} Icon control instance
* @private
*/
NavigationListItemBase.prototype._getExternalIcon = function () {
var oIcon = this.getAggregation("_externalLinkIconControl");
if (!oIcon) {
oIcon = new Icon({
src: EXTERNAL_LINK_ICON
}).addStyleClass(`sapTntNLIExternalLinkIcon`);
this.setAggregation("_externalLinkIconControl", oIcon);
}
return oIcon;
};
/**
* Returns a custom style class for the _expandIconControl aggregation.
*
* @private
* @returns {string} custom style class
*/
NavigationListItemBase.prototype._getExpandIconStyleClass = function () {
return "";
};
/**
* Gets the expand/collapse icon tooltip.
*
* @param {boolean} bExpanded whether the <code>NavigationListItemBase</code> is expanded
* @private
* @returns {string} sTooltip tooltip
*/
NavigationListItemBase.prototype._getExpandIconTooltip = function (bExpanded) {
if (!this.getEnabled() || !this.getAllParentsEnabled()) {
return "";
}
const sKey = bExpanded ? "Icon.expand" : "Icon.collapse";
return this._resourceBundleCore.getText(sKey);
};
/**
* Returns the DOM Element that should get the focus.
*
* @return {Element} Returns the DOM Element that should get the focus
* @protected
*/
NavigationListItemBase.prototype.getFocusDomRef = function () {
const oFocusRef = this.getDomRef()?.querySelector("[tabindex]");
if (!oFocusRef) {
return null;
}
return oFocusRef;
};
/**
* Handles key down event.
*
* @param {sap.ui.base.Event} oEvent keydown event
* @private
*/
NavigationListItemBase.prototype.onkeydown = function (oEvent) {
const bRtl = Localization.getRTL();
if (oEvent.key ? oEvent.key === " " : oEvent.keyCode === KeyCodes.SPACE) {
oEvent.preventDefault();
}
if (this._isInsidePopover()) {
if ((oEvent.which == KeyCodes.ARROW_LEFT && !bRtl) || (oEvent.which == KeyCodes.ARROW_RIGHT && bRtl)) {
this.getNavigationList().oParent.close();
// prevent ItemNavigation to move the focus to the next/previous item
oEvent.stopPropagation();
}
if ((oEvent.which == KeyCodes.ARROW_RIGHT && !bRtl) || (oEvent.which == KeyCodes.ARROW_LEFT && bRtl)) {
// prevent ItemNavigation to move the focus to the next/previous item
oEvent.stopPropagation();
}
return;
}
if (!this._isListExpanded()) {
if ((oEvent.which == KeyCodes.ARROW_RIGHT && !bRtl) || (oEvent.which == KeyCodes.ARROW_LEFT && bRtl)) {
if (this.getItems().length > 0 ) {
this.ontap(oEvent);
}
// prevent ItemNavigation to move the focus to the next/previous item
oEvent.stopPropagation();
}
if ((oEvent.which == KeyCodes.ARROW_LEFT && !bRtl) || (oEvent.which == KeyCodes.ARROW_RIGHT && bRtl)) {
// prevent ItemNavigation to move the focus to the next/previous item
oEvent.stopPropagation();
}
return;
}
if (oEvent.isMarked("subItem")) {
return;
}
if (oEvent.srcControl.getLevel() === 1) {
oEvent.setMarked("subItem");
}
if (this.getLevel() !== 0) {
if (oEvent.which == KeyCodes.ARROW_RIGHT && !bRtl || oEvent.which == KeyCodes.ARROW_LEFT && bRtl ||
oEvent.which == KeyCodes.ARROW_LEFT && !bRtl || oEvent.which == KeyCodes.ARROW_RIGHT && bRtl) {
// prevent ItemNavigation to move the focus to the next/previous item
oEvent.stopPropagation();
}
return;
}
// KeyCodes.MINUS is not returning 189
if ((oEvent.shiftKey && oEvent.which == 189) ||
oEvent.which == KeyCodes.NUMPAD_MINUS ||
(oEvent.which == KeyCodes.ARROW_RIGHT && bRtl) ||
(oEvent.which == KeyCodes.ARROW_LEFT && !bRtl)) {
this.collapse();
oEvent.preventDefault();
// prevent ItemNavigation to move the focus to the next/previous item
oEvent.stopPropagation();
} else if (oEvent.which == KeyCodes.NUMPAD_PLUS ||
(oEvent.shiftKey && oEvent.which == KeyCodes.PLUS) ||
oEvent.which == KeyCodes.ARROW_LEFT && bRtl ||
oEvent.which == KeyCodes.ARROW_RIGHT && !bRtl) {
this.expand();
oEvent.preventDefault();
// prevent ItemNavigation to move the focus to the next/previous item
oEvent.stopPropagation();
}
};
/**
* Handles ontap event.
*
* @param {sap.ui.base.Event} oEvent tap event
* @private
* @returns {boolean} whether the event was handled
*/
NavigationListItemBase.prototype.ontap = function (oEvent) {
if (this.getEnabled() && !(oEvent.srcControl.isA("sap.ui.core.Icon")) && !this._isOverflow && !(!this.getNavigationList().getExpanded() && this.getItems().length)) {
if (!this._firePress(oEvent, this)) {
return true;
}
oEvent.stopPropagation();
}
if (oEvent.isMarked("subItem")) {
return true;
}
oEvent.setMarked("subItem");
if (!this.getEnabled() || !this.getAllParentsEnabled()) {
oEvent.stopPropagation();
return true;
}
if (this._handleExpanderClick(oEvent)) {
return true;
}
return false;
};
/**
* Handle the mouseover event
* @param {jQuery.Event} oEvent The event object for the mouseover event in the callout.
* @private
*/
NavigationListItemBase.prototype.onmouseover = function (oEvent) {
const oTarget = oEvent.target,
oSrcControl = oEvent.srcControl;
if (!oTarget || (oSrcControl && !oSrcControl.isA("sap.tnt.NavigationListItemBase"))) {
return;
}
oEvent.stopPropagation();
const oTooltipElement = this._getTooltipElement();
if (!oTooltipElement) {
return;
}
const oTextElement = oTooltipElement.querySelector(".sapMText") || oTooltipElement.querySelector(".sapTntNLGroupText");
if (oTextElement.offsetWidth >= oTextElement.scrollWidth) {
return;
}
let sText = oSrcControl?.getText();
const sTooltip = oSrcControl.getTooltip_AsString();
if (sTooltip) {
sText += " - " + sTooltip;
}
oTooltipElement.setAttribute("title", sText);
};
NavigationListItemBase.prototype._getTooltipElement = function () {
return this.getDomRef().querySelector("[role='treeitem']");
};
/**
* Handle the onmouseout event
* @param {jQuery.Event} oEvent The event object for the onmouseout event in the callout.
* @private
*/
NavigationListItemBase.prototype.onmouseout = function (oEvent) {
const oTarget = oEvent.target,
oSrcControl = oEvent.srcControl;
if (!oTarget || oSrcControl && !oSrcControl.isA("sap.tnt.NavigationListItemBase")) {
return;
}
oEvent.stopPropagation();
const oTooltipElement = this._getTooltipElement();
const sTooltip = oSrcControl?.getTooltip_AsString();
if (oTooltipElement) {
if (sTooltip) {
oTooltipElement.setAttribute("title", sTooltip);
} else {
oTooltipElement.removeAttribute("title");
}
}
};
/**
* Fires a press event on an item.
* @param {sap.ui.base.Event} oEvent press event
* @param {sap.tnt.NavigationListMenuItem|sap.tnt.NavigationListItem} oItem The item that triggered the event
* @returns {boolean} whether the event was successfully fired
* @private
*/
NavigationListItemBase.prototype._firePress = function(oEvent, oItem) {
const oNavList = this.getNavigationList();
const oParams = oEvent.getParameters ? oEvent.getParameters() : {
item: oItem,
ctrlKey: !!oEvent.ctrlKey,
shiftKey: !!oEvent.shiftKey,
altKey: !!oEvent.altKey,
metaKey: !!oEvent.metaKey
};
if (!this.getEnabled()) {
return false;
}
if (!this.firePress(oParams)) {
oEvent.preventDefault();
return false;
}
if (!oNavList?.fireItemPress(oParams)) {
oEvent.preventDefault();
return false;
}
return true;
};
/**
* Handles the logic for expanding/collapsing the child items.
*
* @param {sap.ui.base.Event} oEvent tap event
* @private
* @returns {boolean} whether the event was handled
*/
NavigationListItemBase.prototype._handleExpanderClick = function (oEvent) {
const sClickTargetClassName = this._getExpanderActivationTarget(),
oClickedRef = oEvent.target.closest(sClickTargetClassName);
if (!this._isListExpanded() || this.getLevel() !== 0 || !oClickedRef) {
return false;
}
if ((oEvent.key ? oEvent.key === "Enter" : oEvent.keyCode === KeyCodes.ENTER)
|| (oEvent.key ? oEvent.key === " " : oEvent.keyCode === KeyCodes.SPACE)) {
return false;
}
if (this.getExpanded()) {
this.collapse();
} else {
this.expand();
}
return true;
};
/**
* Returns the root CSS selector of the element that will handle the expanding/collapsing of the child items.
*
* @private
* @returns {string} CSS selector of the target
*/
NavigationListItemBase.prototype._getExpanderActivationTarget = function () {
if (!this.getSelectable() && !(this.getHref() && this.getTarget() === "_blank")) {
return ".sapTntNLIFirstLevel";
}
return ".sapTntNLIExpandIcon";
};
/**
* Expands the child items (works only on first-level items).
* @returns {boolean} whether the items will be expanded
*/
NavigationListItemBase.prototype.expand = function () {
if (!this.getEnabled() || this.getExpanded() || !this.getHasExpander() || this.getItems().length == 0 || this.getLevel() !== 0) {
return false;
}
this.setProperty("expanded", true);
this._animateExpand = true;
return true;
};
/**
* Collapses the child items (works only on first-level items).
*
* @returns {boolean} whether the items will be collapsed
*/
NavigationListItemBase.prototype.collapse = function () {
if (!this.getEnabled() || !this.getExpanded() || !this.getHasExpander() || this.getItems().length == 0 || this.getLevel() !== 0) {
return false;
}
this.setProperty("expanded", false);
this._animateCollapse = true;
return true;
};
NavigationListItemBase.prototype._isInsidePopover = function () {
return !!this.getNavigationList()?.hasStyleClass("sapTntNLPopup");
};
return NavigationListItemBase;
});