@openui5/sap.m
Version:
OpenUI5 UI Library sap.m
414 lines (349 loc) • 12.6 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([
'sap/ui/core/library',
'sap/ui/core/IconPool',
'sap/ui/core/ShortcutHintsMixin',
'sap/m/library',
'sap/ui/core/InvisibleText',
'sap/ui/core/AccessKeysEnablement'
],
function(coreLibrary, IconPool, ShortcutHintsMixin, library, InvisibleText, AccessKeysEnablement) {
"use strict";
// shortcut for sap.m.ButtonType
var ButtonType = library.ButtonType;
// shortcut for sap.m.ButtonAccessibilityType
var ButtonAccessibilityType = library.ButtonAccessibilityType;
// shortcut for sap.m.LinkAccessibleRole
var ButtonAccessibleRole = library.ButtonAccessibleRole;
// shortcut for sap.ui.core.TextDirection
var TextDirection = coreLibrary.TextDirection;
// shortcut for sap.m.BadgeState
var BadgeState = library.BadgeState;
// shortcut for sap.ui.core.aria.HasPopup
var AriaHasPopup = coreLibrary.aria.HasPopup;
/**
* Button renderer.
* @namespace
*/
var ButtonRenderer = {apiVersion: 2};
/**
* Renders the HTML for the given control, using the provided
* {@link sap.ui.core.RenderManager}.
*
* @param {sap.ui.core.RenderManager} oRm
* the RenderManager that can be used for writing to
* the Render-Output-Buffer
* @param {sap.m.Button} oButton
* the button to be rendered
*/
ButtonRenderer.render = function(oRm, oButton) {
// get control properties
var sButtonId = oButton.getId();
var sType = oButton.getType();
var bEnabled = oButton.getEnabled();
var sWidth = oButton.getWidth();
var sTooltip = oButton._getTooltip();
var sText = oButton._getText();
var sTextDir = oButton.getTextDirection();
var bRenderBDI = (sTextDir === TextDirection.Inherit);
// get icon from icon pool
var sBackURI = IconPool.getIconURI("nav-back");
var sMinWidth;
// start button tag
oRm.openStart("button", oButton);
oRm.class("sapMBtnBase");
oRm.attr("data-ui5-accesskey", oButton.getProperty("accesskey"));
// button container style class
if (!oButton._isUnstyled()) {
oRm.class("sapMBtn");
}
//ARIA attributes
var mAccProps = ButtonRenderer.generateAccProps(oButton);
//descendants (e.g. ToggleButton) callback
if (this.renderAccessibilityAttributes) {
this.renderAccessibilityAttributes(oRm, oButton, mAccProps);
}
oRm.accessibilityState(oButton, mAccProps);
// check if the button is disabled
if (!bEnabled) {
oRm.attr("disabled", "disabled");
if (!oButton._isUnstyled()) {
oRm.class("sapMBtnDisabled");
}
} else {
switch (sType) {
case ButtonType.Accept:
case ButtonType.Reject:
case ButtonType.Emphasized:
case ButtonType.Attention:
oRm.class("sapMBtnInverted");
break;
default: // No need to do anything for other button types
break;
}
}
// add tooltip if available
if (sTooltip && !ShortcutHintsMixin.isDOMIDRegistered(sButtonId)) {
oRm.attr("title", sTooltip);
}
// set user defined width
if (sWidth != "" || sWidth.toLowerCase() === "auto") {
oRm.style("width", sWidth);
//this is a workaround until we move all button property classes to the root element
//we need different min-width of the button in different cases
//we may also need it different in different themes, but not possible with this workaround
if (oButton._getAppliedIcon() && sText) {
sMinWidth = "4rem";
} else { //text only, icon only OR no text no icon
sMinWidth = "2.25rem";
}
oRm.style("min-width", sMinWidth);
}
renderTabIndex(oButton, oRm);
// close button tag
oRm.openEnd();
// start inner button tag
oRm.openStart("span", sButtonId + "-inner");
// button style class
if (!oButton._isUnstyled()) {
oRm.class("sapMBtnInner");
}
// check if button is hoverable
if (oButton._isHoverable()) {
oRm.class("sapMBtnHoverable");
}
// check if button is focusable (not disabled)
if (bEnabled) {
oRm.class("sapMFocusable");
}
if (!oButton._isUnstyled()) {
if (sText) {
oRm.class("sapMBtnText");
}
if (sType === ButtonType.Back || sType === ButtonType.Up) {
oRm.class("sapMBtnBack");
}
if (oButton._getAppliedIcon()) {
if (oButton.getIconFirst()) {
oRm.class("sapMBtnIconFirst");
} else {
oRm.class("sapMBtnIconLast");
}
}
}
//get render attributes of depended buttons (e.g. ToggleButton)
if (this.renderButtonAttributes) {
this.renderButtonAttributes(oRm, oButton);
}
// set button specific styles
if (!oButton._isUnstyled() && sType !== "") {
// set button specific styles
oRm.class("sapMBtn" + sType);
}
//apply on the inner level as well as not applying it will allow for focusing the button after a mouse click
renderTabIndex(oButton, oRm);
// close inner button tag
oRm.openEnd();
// set image for internal image control (back)
if (sType === ButtonType.Back || sType === ButtonType.Up) {
this.writeInternalIconPoolHtml(oRm, oButton, sBackURI);
}
// write icon
if (oButton.getIconFirst() && oButton._getAppliedIcon()) {
this.writeImgHtml(oRm, oButton);
}
// write button text
if (sText) {
this.writeButtonText(oRm, oButton, sTextDir, bRenderBDI);
}
// write icon
if (!oButton.getIconFirst() && oButton._getAppliedIcon()) {
this.writeImgHtml(oRm, oButton);
}
// end inner button tag
oRm.close("span");
// add tooltip if available
if (sTooltip) {
oRm.openStart("span", sButtonId + "-tooltip");
oRm.class("sapUiInvisibleText");
oRm.openEnd();
oRm.text(sTooltip);
oRm.close("span");
}
// end button tag
oRm.close("button");
};
/**
* HTML for image
*
* @param {sap.ui.core.RenderManager} oRm
* the RenderManager that can be used for writing to
* the Render-Output-Buffer
* @param {sap.m.Button} oButton
* the button to be rendered
* @private
*/
ButtonRenderer.writeImgHtml = function(oRm, oButton) {
var sType = oButton.getType(),
bHasExplicitIcon = oButton.getIcon(),
bIsBackType = (sType === ButtonType.Back) || (sType === ButtonType.Up);
// Avoid duplicated rendering of the default Back/Up icon. Due to legacy
// reasons, those types are different than the others and can have 2 icons
// at the same time - here we render the one that's explicitly provided.
//
// If such isn't provided, our button will get the default icon for its type.
// However, because Back/Up's default icon is already rendered, two identical
// icons will appear unless we escape this here.
if (!bHasExplicitIcon && bIsBackType) {
return;
}
oRm.renderControl(oButton._getImage(
oButton.getId() + "-img",
oButton._getAppliedIcon(),
oButton.getActiveIcon(),
oButton.getIconDensityAware()));
};
/**
* @param {sap.ui.core.RenderManager} oRm
* the RenderManager that can be used for writing to
* the Render-Output-Buffer
* @param {sap.m.Button} oButton
* the button to be rendered
* @param {sap.ui.core.URI} sURI
* URI of the icon to be written
* HTML for internal image (icon pool)
*/
ButtonRenderer.writeInternalIconPoolHtml = function(oRm, oButton, sURI) {
oRm.renderControl(oButton._getInternalIconBtn((oButton.getId() + "-iconBtn"), sURI));
};
ButtonRenderer.writeButtonText = function(oRm, oButton, sTextDir, bRenderBDI){
oRm.openStart("span", oButton.getId() + "-content");
oRm.class("sapMBtnContent");
// check if textDirection property is not set to default "Inherit" and add "dir" attribute
if (sTextDir !== TextDirection.Inherit) {
oRm.attr("dir", sTextDir.toLowerCase());
}
if (oButton.getProperty("highlightAccKeysRef")) {
oRm.class(AccessKeysEnablement.CSS_CLASS);
}
oRm.openEnd();
if (bRenderBDI) {
oRm.openStart("bdi", oButton.getId() + "-BDI-content");
oRm.openEnd();
}
oRm.text(oButton.getText());
if (bRenderBDI) {
oRm.close("bdi");
}
oRm.close("span");
};
/**
* Renders tabindex with value of "-1" if required by <code>_bExcludeFromTabChain</code> property.
* @param {sap.m.Button} oButton The sap.m.Button to be rendered
* @param {sap.ui.core.RenderManager} oRm The RenderManager that can be used for writing to the Render-Output-Buffer
*/
function renderTabIndex(oButton, oRm){
if (oButton._bExcludeFromTabChain) {
oRm.attr("tabindex", -1);
}
}
var mARIATextKeys = {
Accept: "BUTTON_ARIA_TYPE_ACCEPT",
Reject: "BUTTON_ARIA_TYPE_REJECT",
Attention: "BUTTON_ARIA_TYPE_ATTENTION",
Emphasized: "BUTTON_ARIA_TYPE_EMPHASIZED",
Critical: "BUTTON_ARIA_TYPE_CRITICAL",
Negative: "BUTTON_ARIA_TYPE_NEGATIVE",
Success: "BUTTON_ARIA_TYPE_SUCCESS"
};
ButtonRenderer.getButtonTypeAriaLabelId = function(sType) {
return InvisibleText.getStaticId("sap.m", mARIATextKeys[sType]);
};
ButtonRenderer.getBadgeTextId = function(oButton) {
return oButton._oBadgeData && oButton._oBadgeData.value !== "" && oButton._oBadgeData.state !== BadgeState.Disappear ? oButton._getBadgeInvisibleText().getId() : "";
};
ButtonRenderer.generateAccProps = function (oButton) {
var sText = oButton._getText(),
sHasPopupType = oButton.getAriaHasPopup(),
mAccProps,
sAccessibleRole = oButton.getAccessibleRole();
if (sText) {
mAccProps = ButtonRenderer.generateTextButtonAccProps(oButton);
} else {
mAccProps = ButtonRenderer.generateIconOnlyButtonAccProps(oButton);
}
// prevent rendering of aria-disabled attribute to avoid having
// both aria-disabled and disabled at the same time
mAccProps["disabled"] = null;
if (sAccessibleRole === ButtonAccessibleRole.Link) {
mAccProps["role"] = "link";
}
mAccProps["haspopup"] = (sHasPopupType === AriaHasPopup.None) ? null : sHasPopupType.toLowerCase();
return mAccProps;
};
ButtonRenderer.generateIconOnlyButtonAccProps = function (oButton) {
var sTypeId = ButtonRenderer.getButtonTypeAriaLabelId(oButton.getType()),
sBadgeTextId = this.getBadgeTextId(oButton),
sTooltip = oButton._getTooltip(),
sTooltipId = oButton.getId() + "-tooltip", // Icon-only buttons will always have a tooltip
sAccessibilityType = oButton._determineAccessibilityType(),
mAccProps = {},
sDescription;
switch (sAccessibilityType) {
case ButtonAccessibilityType.Default:
mAccProps["label"] = { value: sTooltip, append: true };
break;
case ButtonAccessibilityType.Described:
mAccProps["label"] = { value: sTooltip, append: true };
sDescription = (sTypeId + " " + sBadgeTextId).trim();
sDescription && (mAccProps["describedby"] = { value: sDescription, append: true });
break;
case ButtonAccessibilityType.Labelled:
mAccProps["describedby"] = { value: sTooltipId, append: true };
break;
case ButtonAccessibilityType.Combined:
mAccProps["describedby"] = { value: (sTooltipId + " " + sTypeId + " " + sBadgeTextId).trim(), append: true };
break;
default:
break;
}
return mAccProps;
};
ButtonRenderer.generateTextButtonAccProps = function (oButton) {
var sButtonId = oButton.getId(),
sTypeId = ButtonRenderer.getButtonTypeAriaLabelId(oButton.getType()),
sBadgeTextId = this.getBadgeTextId(oButton),
sTooltipId = oButton._getTooltip() ? sButtonId + "-tooltip" : "", // Don't assign if empty (to ease conditions in the switch)
sInnerTextId = sButtonId + "-content",
sAccessibilityType = oButton._determineAccessibilityType(),
bPlaceSelfReference = oButton._determineSelfReferencePresence(),
mAccProps = {},
sDescription;
switch (sAccessibilityType) {
case ButtonAccessibilityType.Default:
sTooltipId && (mAccProps["describedby"] = { value: sTooltipId, append: true });
break;
case ButtonAccessibilityType.Described:
sDescription = (sTooltipId + " " + sTypeId + " " + sBadgeTextId).trim();
sDescription && (mAccProps["describedby"] = { value: sDescription, append: true });
break;
case ButtonAccessibilityType.Labelled:
bPlaceSelfReference && (mAccProps["labelledby"] = { value: sInnerTextId, append: true });
sTooltipId && (mAccProps["describedby"] = { value: sTooltipId, append: true });
break;
case ButtonAccessibilityType.Combined:
sDescription = (sTooltipId + " " + sTypeId + " " + sBadgeTextId).trim();
sDescription && (mAccProps["describedby"] = { value: sDescription, append: true });
bPlaceSelfReference && (mAccProps["labelledby"] = { value: sInnerTextId, append: true });
break;
default:
break;
}
return mAccProps;
};
return ButtonRenderer;
}, /* bExport= */ true);