UNPKG

@openui5/sap.m

Version:

OpenUI5 UI Library sap.m

404 lines (347 loc) 13.8 kB
/*! * OpenUI5 * (c) Copyright 2026 SAP SE or an SAP affiliate company. * Licensed under the Apache License, Version 2.0 - see LICENSE.txt. */ // Provides control sap.m.ObjectAttribute. sap.ui.define([ './library', "sap/base/i18n/Localization", 'sap/ui/core/Control', 'sap/ui/core/library', 'sap/m/Text', 'sap/ui/core/Element', "sap/ui/core/Locale", 'sap/ui/events/KeyCodes', './ObjectAttributeRenderer', 'sap/base/Log', 'sap/ui/base/ManagedObjectObserver' ], function(library, Localization, Control, coreLibrary, Text, Element, Locale, KeyCodes, ObjectAttributeRenderer, Log, ManagedObjectObserver) { "use strict"; // shortcut for sap.ui.core.TextDirection var TextDirection = coreLibrary.TextDirection; // shortcut for sap.m.EmptyIndicatorMode var EmptyIndicatorMode = library.EmptyIndicatorMode; // shortcut for sap.ui.core.aria.HasPopup var AriaHasPopup = coreLibrary.aria.HasPopup; // shortcut for sap.m.ReactiveAreaMode var ReactiveAreaMode = library.ReactiveAreaMode; /** * Constructor for a new <code>ObjectAttribute</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>ObjectAttribute</code> control displays a text field that can be normal or active. * The <code>ObjectAttribute</code> fires a <code>press</code> event when the user chooses the active text. * * <b>Note:</b> If property <code>active</code> is set to <code>true</code>, only the value of the * <code>text</code> property is styled and acts as a link. In this case the <code>text</code> * property must also be set, as otherwise there will be no link displayed for the user. * @extends sap.ui.core.Control * @version 1.146.0 * * @constructor * @public * @since 1.12 * @alias sap.m.ObjectAttribute */ var ObjectAttribute = Control.extend("sap.m.ObjectAttribute", /** @lends sap.m.ObjectAttribute.prototype */ { metadata : { library : "sap.m", designtime: "sap/m/designtime/ObjectAttribute.designtime", properties : { /** * Defines the ObjectAttribute title. */ title : {type : "string", group : "Misc", defaultValue : null}, /** * Defines the ObjectAttribute text. */ text : {type : "string", group : "Misc", defaultValue : null}, /** * Indicates if the <code>ObjectAttribute</code> text is selectable for the user. * * <b>Note:</b> As of version 1.48, only the value of the <code>text</code> property becomes active (styled and acts like a link) as opposed to both the <code>title</code> and <code>text</code> in the previous versions. If you set this property to <code>true</code>, you have to also set the <code>text</code> property. * <b>Note:</b> When <code>active</code> property is set to <code>true</code>, and the text direction of the <code>title</code> or the <code>text</code> does not match the text direction of the application, the <code>textDirection</code> property should be set to ensure correct display. */ active : {type : "boolean", group : "Misc", defaultValue : null}, /** * Defines the size of the reactive area of the link:<ul> * <li><code>ReactiveAreaMode.Inline</code> - The link is displayed as part of a sentence.</li> * <li><code>ReactiveAreaMode.Overlay</code> - The link is displayed as an overlay on top of other interactive parts of the page.</li></ul> * * <b>Note:</b>It is designed to make links easier to activate and helps meet the WCAG 2.2 Target Size requirement. It is applicable only for the SAP Horizon themes. * <b>Note:</b>The Reactive area size is sufficiently large to help users avoid accidentally selecting (clicking or tapping) on unintented UI elements. * UI elements positioned over other parts of the page may need an invisible active touch area. * This will ensure that no elements beneath are activated accidentally when the user tries to interact with the overlay element. * * @since 1.133.0 */ reactiveAreaMode : {type : "sap.m.ReactiveAreaMode", group : "Appearance", defaultValue : ReactiveAreaMode.Inline}, /** * Determines the direction of the text. * Available options for the text direction are LTR (left-to-right), RTL (right-to-left), or Inherit. By default the control inherits the text direction from its parent control. */ textDirection : {type : "sap.ui.core.TextDirection", group : "Appearance", defaultValue : TextDirection.Inherit}, /** * Specifies the value of the <code>aria-haspopup</code> attribute * * If the value is <code>None</code>, the attribute will not be rendered. Otherwise it will be rendered with the selected value. * * NOTE: Use this property only when an <code>sap.m.ObjectAttribute</code> instance is active and related to a popover/popup. * The value needs to be equal to the main/root role of the popup - e.g. dialog, * menu or list (examples: if you have dialog -> dialog, if you have menu -> menu; if you have list -> list; if you have dialog containing a list -> dialog). * Do not use it, if you open a standard sap.m.Dialog, MessageBox or other type of modal dialogs. * * @since 1.97.0 */ ariaHasPopup : {type : "sap.ui.core.aria.HasPopup", group : "Accessibility", defaultValue : AriaHasPopup.None} }, aggregations : { /** * When the aggregation is set, it replaces the <code>text</code>, <code>active</code> and <code>textDirection</code> properties. This also ignores the press event. The provided control is displayed as an active link in case it is a sap.m.Link. * <b>Note:</b> It will only allow sap.m.Text and sap.m.Link controls. */ customContent : {type : "sap.ui.core.Control", multiple : false}, /** * Text control to display title and text property. */ _textControl : {type : "sap.ui.core.Control", multiple : false, visibility : "hidden"} }, events : { /** * Fires when the user clicks on active text. */ press : { parameters : { /** * DOM reference of the ObjectAttribute's text to be used for positioning. */ domRef : {type : "string"} } } }, dnd: { draggable: true, droppable: false } }, renderer: ObjectAttributeRenderer }); /** * Initializes member variables. * * @private */ ObjectAttribute.prototype.init = function() { this.setAggregation('_textControl', new Text()); }; ObjectAttribute.prototype.exit = function() { if (this._oCustomContentObserver) { this._oCustomContentObserver.disconnect(); this._oCustomContentObserver = null; } if (this._oCustomContentCloning) { this._oCustomContentCloning.destroy(); } }; ObjectAttribute.prototype.onBeforeRendering = function() { var oLink, sTitleId = this.getId() + "-title"; if (this._isClickable() && !this._isSimulatedLink()) { oLink = this.getCustomContent(); oLink.setAriaHasPopup(this.getAriaHasPopup()); if (!oLink.getAriaLabelledBy().includes(sTitleId)) { oLink.addAriaLabelledBy(sTitleId); } } }; /** * Delivers text control with updated title, text and maxLines properties. * * @private */ ObjectAttribute.prototype._getUpdatedTextControl = function() { var oAttrAggregation = this._oCustomContentCloning || this.getAggregation('_textControl'), sTitle = this.getTitle(), sText = this.getAggregation('customContent') ? this.getAggregation('customContent').getText() : this.getText(), sTextDir = this.getTextDirection(), oParent = this.getParent(), bPageRTL = Localization.getRTL(), iMaxLines, bWrap = true, oppositeDirectionMarker = '', sResult; this._bEmptyIndicatorMode = this._isEmptyIndicatorMode(); if (sTextDir === TextDirection.LTR && bPageRTL) { oppositeDirectionMarker = '\u200e'; } if (sTextDir === TextDirection.RTL && !bPageRTL) { oppositeDirectionMarker = '\u200f'; } sText = oppositeDirectionMarker + sText + oppositeDirectionMarker; if (sTitle) { sResult = sTitle; if (new Locale(Localization.getLanguageTag()).getLanguage().toLowerCase() === "fr") { sResult += " "; } sResult += ": " + sText; } else { sResult = sText; } if (this._bEmptyIndicatorMode) { //inner text control is used in order to display properly the empty indicator this.getAggregation('_textControl').setEmptyIndicatorMode(EmptyIndicatorMode.On); } oAttrAggregation.setText(sResult); oAttrAggregation.setTextDirection(sTextDir); //if attribute is used inside responsive ObjectHeader or in ObjectListItem - only 1 line if (oParent && oParent.isA("sap.m.ObjectListItem")) { bWrap = false; iMaxLines = ObjectAttributeRenderer.MAX_LINES.SINGLE_LINE; } this._setControlWrapping(oAttrAggregation, bWrap, iMaxLines); return oAttrAggregation; }; ObjectAttribute.prototype._isEmptyIndicatorMode = function () { var oCustomContent = this.getAggregation('customContent'); return oCustomContent && oCustomContent.getEmptyIndicatorMode() !== EmptyIndicatorMode.Off && !oCustomContent.getText(); }; /** * Sets the appropriate property to the customContent aggregation. * * @private */ ObjectAttribute.prototype._setControlWrapping = function(oAttrAggregation, bWrap, iMaxLines) { if (oAttrAggregation.isA("sap.m.Link")) { oAttrAggregation.setWrapping(bWrap); } if (oAttrAggregation.isA("sap.m.Text")) { oAttrAggregation.setMaxLines(iMaxLines); } }; /** * @private * @param {object} oEvent The fired event */ ObjectAttribute.prototype.ontap = function(oEvent) { var oTarget = oEvent.target; oTarget = oTarget.id ? oTarget : oTarget.parentElement; //event should only be fired if the click is on the text (acting like a link) if (this._isSimulatedLink() && (oTarget.id === this.getId() + "-text")) { this.firePress({ domRef : this.getDomRef() }); } }; /** * @private * @param {object} oEvent The fired event */ ObjectAttribute.prototype.onsapenter = function(oEvent) { if (this._isSimulatedLink()) { this.firePress({ domRef : this.getDomRef() }); // mark the event that it is handled by the control oEvent.setMarked(); } }; /** * @private * @param {object} oEvent The fired event */ ObjectAttribute.prototype.onsapspace = function(oEvent) { oEvent.preventDefault(); }; ObjectAttribute.prototype.onkeyup = function (oEvent) { if (oEvent.which === KeyCodes.SPACE) { this.onsapenter(oEvent); } }; /** * Checks if ObjectAttribute is empty. * * @private * @returns {boolean} true if ObjectAttribute's text is empty or only consists of whitespaces */ ObjectAttribute.prototype._isEmpty = function() { if (this.getAggregation('customContent') && !(this.getAggregation('customContent').isA("sap.m.Link") || this.getAggregation('customContent').isA("sap.m.Text"))) { Log.warning("Only sap.m.Link or sap.m.Text are allowed in \"sap.m.ObjectAttribute.customContent\" aggregation"); return true; } return !(this.getText().trim() || this.getTitle().trim()); }; /** * Called when the control is touched. * @param {object} oEvent The fired event * @private */ ObjectAttribute.prototype.ontouchstart = function(oEvent) { if (this._isSimulatedLink()) { // for control who need to know if they should handle events from the ObjectAttribute control oEvent.originalEvent._sapui_handledByControl = true; } }; /** * Defines to which DOM reference the Popup should be docked. * * @protected * @return {Element} The DOM reference that Popup should dock to */ ObjectAttribute.prototype.getPopupAnchorDomRef = function() { return this.getDomRef("text"); }; /** * @see sap.ui.core.Element.prototype.getFocusDomRef * @protected * @override * @returns {Element|null} Returns the DOM Element that should get the focus or <code>null</code> */ ObjectAttribute.prototype.getFocusDomRef = function() { var oDomRef = this.getDomRef(); if (oDomRef) { if (this._isSimulatedLink()) { return oDomRef.querySelector(".sapMObjectAttributeText"); } else if (this._isClickable()) { return this.getAggregation("customContent").getDomRef(); } } return Element.prototype.getFocusDomRef.apply(this, arguments); }; ObjectAttribute.prototype._isSimulatedLink = function () { return (this.getActive() && this.getText() !== "") && !this.getAggregation('customContent'); }; ObjectAttribute.prototype.setCustomContent = function(oCustomContent) { var oCurrentCustomContent = this.getCustomContent(); // clone the new aggregation, but first destroy the previous cloning if (this._oCustomContentCloning) { this._oCustomContentCloning.destroy(); } this._oCustomContentCloning = oCustomContent && oCustomContent.clone(); if (!this._oCustomContentObserver) { this._oCustomContentObserver = new ManagedObjectObserver(function() { this.invalidate(); }.bind(this)); } // do not listen for property changes on the old control if (oCurrentCustomContent) { this._oCustomContentObserver.unobserve(oCurrentCustomContent); } // listen on the new one oCustomContent && this._oCustomContentObserver.observe(oCustomContent, { properties: true }); return this.setAggregation('customContent', oCustomContent); }; /** * Returns whether the control can be clicked so in the renderer appropriate attributes can be set (for example tabindex). * @private */ ObjectAttribute.prototype._isClickable = function() { return this.getAggregation('customContent') ? this.getAggregation('customContent').isA('sap.m.Link') : this.getActive() && this.getText() !== ""; }; return ObjectAttribute; });