UNPKG

@openui5/sap.m

Version:

OpenUI5 UI Library sap.m

467 lines (399 loc) 14 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.ColumnListItem. sap.ui.define([ "sap/ui/core/Element", "sap/ui/core/library", "sap/ui/core/Lib", "sap/ui/dom/detectTextSelection", "./library", "./ListItemBase", "./ColumnListItemRenderer", "sap/ui/thirdparty/jquery", // jQuery custom selectors ":sapFocusable", ":sapTabbable" "sap/ui/dom/jquery/Selectors" ], function(Element, coreLibrary, Lib, detectTextSelection, library, ListItemBase, ColumnListItemRenderer) { "use strict"; // shortcut for sap.m.ListType var ListItemType = library.ListType; // shortcut for sap.ui.core.VerticalAlign var VerticalAlign = coreLibrary.VerticalAlign; /** * Constructor for a new ColumnListItem. * * @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 * <code>sap.m.ColumnListItem</code> can be used with the <code>cells</code> aggregation to create rows for the <code>sap.m.Table</code> control. * The <code>columns</code> aggregation of the <code>sap.m.Table</code> should match with the cells aggregation. * * <b>Note:</b> This control should only be used within the <code>sap.m.Table</code> control. * The inherited <code>counter</code> property of <code>sap.m.ListItemBase</code> is not supported. * * @extends sap.m.ListItemBase * @implements sap.m.ITableItem * * @author SAP SE * @version 1.146.0 * * @constructor * @public * @since 1.12 * @alias sap.m.ColumnListItem */ var ColumnListItem = ListItemBase.extend("sap.m.ColumnListItem", /** @lends sap.m.ColumnListItem.prototype */ { metadata : { interfaces : [ "sap.m.ITableItem" ], library : "sap.m", properties : { /** * Sets the vertical alignment of all the cells within the table row (including selection and navigation). * <b>Note:</b> <code>vAlign</code> property of <code>sap.m.Column</code> overrides the property for cell vertical alignment if both are set. * @since 1.20 */ vAlign : {type : "sap.ui.core.VerticalAlign", group : "Appearance", defaultValue : VerticalAlign.Inherit} }, defaultAggregation : "cells", aggregations : { /** * Every <code>control</code> inside the <code>cells</code> aggregation defines one cell of the row. * <b>Note:</b> The order of the <code>cells</code> aggregation must match the order of the <code>columns</code> aggregation of <code>sap.m.Table</code>. */ cells : {type : "sap.ui.core.Control", multiple : true, singularName : "cell", bindable : "bindable"} } }, renderer: ColumnListItemRenderer }); /** * TablePopin element that handles own events. * * @class * @alias sap.m.TablePopin */ var TablePopin = Element.extend("sap.m.TablePopin", { ontap: function(oEvent) { // prevent the tap event if selection is done within the popin control if (oEvent.isMarked() || detectTextSelection(this.getDomRef())) { return oEvent.stopImmediatePropagation(true); } }, ontouchend: function() { if (document.activeElement === this.getFocusDomRef()) { this.getParent().focus({ preventScroll: true }); } }, getFocusDomRef: function() { return this.getParent().getDomRef("subcont"); } }); // defines tag name ColumnListItem.prototype.TagName = "tr"; ColumnListItem.prototype.init = function() { ListItemBase.prototype.init.call(this); this._bNeedsTypeColumn = false; this._aClonedHeaders = []; }; ColumnListItem.prototype.onBeforeRendering = function() { ListItemBase.prototype.onBeforeRendering.call(this); this.aAriaOwns = []; if (this._oPopin && this._oDomRef) { this.$Popin().off(); } }; ColumnListItem.prototype.onAfterRendering = function() { if (this._oPopin) { this.$().attr("aria-owns", this.aAriaOwns.join(" ")); this.isActionable(true) && this.$Popin().on("mouseenter mouseleave", (oEvent) => { this.toggleStyleClass("sapMPopinHovered", oEvent.type == "mouseenter"); }); } ListItemBase.prototype.onAfterRendering.call(this); this._checkTypeColumn(); if (this.bOutput !== false && document.activeElement.id === this.getId()) { this.getTable()?._setFirstLastVisibleCells(document.activeElement); } }; ColumnListItem.prototype.exit = function() { ListItemBase.prototype.exit.call(this); this._checkTypeColumn(false); this._destroyClonedHeaders(); if (this._oPopin) { this._oPopin.destroy(true); this._oPopin = null; } }; // remove pop-in from DOM when setVisible false is called ColumnListItem.prototype.setVisible = function(bVisible) { ListItemBase.prototype.setVisible.call(this, bVisible); if (!bVisible && this.hasPopin()) { this.removePopin(); } return this; }; // returns responsible table control for the item ColumnListItem.prototype.getTable = function() { var oParent = this.getParent(); if (oParent && oParent.isA("sap.m.Table")) { return oParent; } }; /** * Returns the pop-in element. * * @protected * @since 1.30.9 */ ColumnListItem.prototype.getPopin = function() { if (!this._oPopin) { this._oPopin = new TablePopin({ id: this.getId() + "-sub" }).addDelegate({ // handle the events of pop-in ontouchstart: this.ontouchstart, ontouchmove: this.ontouchmove, ontap: this.ontap, ontouchend: this.ontouchend, ontouchcancel: this.ontouchcancel, onsapup: this.onsapup, onsapdown: this.onsapdown, oncontextmenu: this.oncontextmenu, onkeydown: this.onkeydown, onfocusin: this.onfocusin, onfocusout: this.onfocusout }, this).setParent(this, null, true); } return this._oPopin; }; /** * Returns pop-in DOMRef as a jQuery Object * * @protected * @since 1.26 */ ColumnListItem.prototype.$Popin = function() { return this.$("sub"); }; /** * Determines whether control has pop-in or not. * @protected */ ColumnListItem.prototype.hasPopin = function() { return this._oPopin; }; /** * Pemove pop-in from DOM * @protected */ ColumnListItem.prototype.removePopin = function() { this._oPopin && this.$Popin().remove(); }; /** * Returns the tabbable DOM elements as a jQuery collection * When popin is available this separated dom should also be included * * @param [bContentOnly] Whether only tabbables of data cells * @returns {jQuery} jQuery object * @protected * @since 1.26 */ ColumnListItem.prototype.getTabbables = function(bContentOnly) { const $Content = bContentOnly ? this.$().find(".sapMListTblCell") : this.$(); return $Content.add(this.$Popin()).find(":sapTabbable"); }; /** * Calculates and returns the bounding client rectangle * of the drop area taking the popin area into account. * @private */ ColumnListItem.prototype.getDropAreaRect = function() { var oPopin = null; var oDomRef = this.getDomRef(); var mDropRect = oDomRef.getBoundingClientRect().toJSON(); if (this._oPopin && (oPopin = this.getDomRef("sub"))) { var mPopinRect = oPopin.getBoundingClientRect(); mDropRect.bottom = mPopinRect.bottom; mDropRect.height += mPopinRect.height; } return mDropRect; }; ColumnListItem.prototype.getAccessibilityType = function(oBundle) { return oBundle.getText("ACC_CTR_TYPE_ROW"); }; ColumnListItem.prototype.getContentAnnouncementOfCell = function(oColumn) { return getAnnouncementForColumn(oColumn, this.getCells(), false); }; function getAnnouncementForColumn(oColumn, aCells, bIncludeHeader) { const oCell = aCells[oColumn.getInitialOrder()]; let sOutput = ListItemBase.getAccessibilityText(oCell, true); if (bIncludeHeader) { const bPopinFocused = document.activeElement.classList.contains("sapMListTblSubCnt"); const sColumnDescription = oColumn.getAccessibilityDescription(!bPopinFocused); sOutput = sColumnDescription + " " + sOutput; } else if (oCell.$().parent().find(":sapTabbable").length > 0) { sOutput = Lib.getResourceBundleFor("sap.m").getText("TABLE_CELL_INCLUDES", [sOutput]); } return sOutput; } ColumnListItem.prototype.getContentAnnouncementOfPopin = function() { const aCells = this.getCells(); const aOutput = this.getTable()._getVisiblePopin().map(function(oColumn) { return getAnnouncementForColumn(oColumn, aCells, true); }); let sOutput = aOutput.filter(Boolean).join(" . ").trim(); if (this.$Popin().find(":sapTabbable").length > 0) { sOutput = Lib.getResourceBundleFor("sap.m").getText("TABLE_CELL_INCLUDES", [sOutput]); } return sOutput; }; ColumnListItem.prototype.getContentAnnouncementOfRowAction = function() { // Only if the item is inactive, to announce empty row action cell if (this.getEffectiveType() === ListItemType.Inactive) { return ListItemBase.getAccessibilityText(null, true); } }; ColumnListItem.prototype.getContentAnnouncement = function() { const oTable = this.getTable(); if (!oTable) { return; } const aCells = this.getCells(); const aOutput = oTable.getRenderedColumns().map(function(oColumn) { return getAnnouncementForColumn(oColumn, aCells, true); }); return aOutput.filter(Boolean).join(" . ").trim(); }; ColumnListItem.prototype.getGroupAnnouncement = function() { return this.$().prevAll(".sapMGHLI:first").text(); }; // update the aria-selected for the cells ColumnListItem.prototype.updateSelectedDOM = function(bSelected, $This) { ListItemBase.prototype.updateSelectedDOM.apply(this, arguments); $This.find(".sapMTblCellFocusable").attr("aria-selected", bSelected); if (this.hasPopin()) { this.$("subcont").attr("aria-selected", bSelected); } }; ColumnListItem.prototype.onfocusin = function(oEvent) { if (oEvent.isMarked()) { return; } if (oEvent.srcControl === this) { this.$().children(".sapMListTblCellDup").find(":sapTabbable").attr("tabindex", -1); } const oTable = this.getTable(); const oTarget = oEvent.target; let sInvisibleText; if (oTarget.classList.contains("sapMListTblCell")) { const oColumn = Element.getElementById(oTarget.getAttribute("data-sap-ui-column")); sInvisibleText = this.getContentAnnouncementOfCell(oColumn); } else if (oTarget.classList.contains("sapMListTblSubCnt")) { sInvisibleText = this.getContentAnnouncementOfPopin(); } else if (oTarget.classList.contains("sapMListTblNavCol")) { sInvisibleText = this.getContentAnnouncementOfRowAction(); } else if (oTarget.classList.contains("sapMListTblActionsCol")) { sInvisibleText = this._getCustomActionsAnnouncement(true); } if (sInvisibleText) { oTable.updateInvisibleText(sInvisibleText); oEvent.setMarked("contentAnnouncementGenerated"); } ListItemBase.prototype.onfocusin.apply(this, arguments); }; ColumnListItem.prototype.onfocusout = function(oEvent) { if (oEvent.isMarked()) { return; } const oTarget = oEvent.target; if (oTarget.matches(".sapMListTblCell") || oTarget.matches(".sapMListTblSubCnt")) { this.getTable().removeInvisibleTextAssociation(oTarget); } ListItemBase.prototype.onfocusout.apply(this, arguments); }; ColumnListItem.prototype.onsapenter = ColumnListItem.prototype.onsapspace = function(oEvent) { if (oEvent.isMarked()) { return; } var sTargetId = oEvent.target.id; var sEventHandler = "on" + oEvent.type; if (sTargetId == this.getId() + "-ModeCell") { oEvent.target = this.getDomRef(); sEventHandler = this.getMode() == "Delete" ? "onsapdelete" : "onsapspace"; } else if (sTargetId == this.getId() + "-TypeCell") { oEvent.target = this.getDomRef(); if (this.getEffectiveType() == "Navigation") { sEventHandler = "onsapenter"; } else { oEvent.code = "KeyE"; oEvent.ctrlKey = true; sEventHandler = "onkeydown"; } } ListItemBase.prototype[sEventHandler].call(this, oEvent); }; ColumnListItem.prototype.setType = function(sType) { ListItemBase.prototype.setType.call(this, sType); this._checkTypeColumn(); return this; }; ColumnListItem.prototype.setParent = function() { ListItemBase.prototype.setParent.apply(this, arguments); this._checkTypeColumn(); return this; }; // informs the table when item's type column requirement is changed ColumnListItem.prototype._checkTypeColumn = function(bNeedsTypeColumn) { if (!this.getParent()) { return; } if (bNeedsTypeColumn == undefined) { bNeedsTypeColumn = this._needsTypeColumn(); } if (this._bNeedsTypeColumn != bNeedsTypeColumn) { this._bNeedsTypeColumn = bNeedsTypeColumn; this.informList("TypeColumnChange", bNeedsTypeColumn); } }; // determines whether type column for this item is necessary or not ColumnListItem.prototype._needsTypeColumn = function() { if (!this.getVisible()) { return false; } const sType = this.getEffectiveType(); return sType === ListItemType.Navigation ? true : sType.startsWith(ListItemType.Detail) && this._getMaxActionsCount() === -1; }; // Adds cloned header to the local collection ColumnListItem.prototype._addClonedHeader = function(oHeader) { return this._aClonedHeaders.push(oHeader); }; // Destroys cloned headers that are generated for popin ColumnListItem.prototype._destroyClonedHeaders = function() { if (this._aClonedHeaders.length) { this._aClonedHeaders.forEach(function(oClone) { oClone.destroy("KeepDom"); }); this._aClonedHeaders = []; } }; // active feedback for pop-in ColumnListItem.prototype._activeHandlingInheritor = function() { this._toggleActiveClass(true); }; // inactive feedback for pop-in ColumnListItem.prototype._inactiveHandlingInheritor = function() { this._toggleActiveClass(false); }; // toggles the active class of the pop-in. ColumnListItem.prototype._toggleActiveClass = function(bSwitch) { if (this.hasPopin()) { this.$Popin().toggleClass("sapMLIBActive", bSwitch); } }; return ColumnListItem; });