UNPKG

@openui5/sap.m

Version:

OpenUI5 UI Library sap.m

777 lines (676 loc) 25.1 kB
/* * UI development toolkit for HTML5 (OpenUI5) * (c) Copyright 2009-2022 SAP SE or an SAP affiliate company. * Licensed under the Apache License, Version 2.0 - see LICENSE.txt. */ // Provides TablePersoDialog sap.ui.define([ './Button', './Dialog', './InputListItem', './List', './Toolbar', 'sap/ui/base/ManagedObject', 'sap/m/library', 'sap/ui/Device', 'sap/ui/model/Sorter', 'sap/ui/model/Filter', 'sap/ui/model/FilterOperator', 'sap/ui/model/json/JSONModel', 'sap/m/CheckBox', 'sap/m/SearchField', 'sap/m/ScrollContainer', "sap/ui/thirdparty/jquery" ], function( Button, Dialog, InputListItem, List, Toolbar, ManagedObject, library, Device, Sorter, Filter, FilterOperator, JSONModel, CheckBox, SearchField, ScrollContainer, jQuery ) { "use strict"; // shortcut for sap.m.ToolbarDesign var ToolbarDesign = library.ToolbarDesign; // shortcut for sap.m.ListMode var ListMode = library.ListMode; /** * The TablePersoDialog can be used to display and allow modification of personalization settings relating to a Table. It displays the columns of the table that it refers to by using * <ul><li>The result of calling sap.m.TablePersoProvider's 'getCaption' callback if it is implemented and delivers a non-null value for a column</li> * <li>the column header control's 'text' property if no caption property is available</li> * <li>the column header control's 'title' property if neither 'text' nor 'caption' property are available</li> * <li>the column id is displayed as last fallback, if none of the above is at hand. In that case, a warning is logged. </li></ul> * * @param {string} * [sId] optional id for the new control; generated automatically if * no non-empty id is given Note: this can be omitted, no matter * whether <code>mSettings</code> will be given or not! * @param {object} * [mSettings] optional map/JSON-object with initial settings for the * new component instance * @public * * @class Table Personalization Dialog * @extends sap.ui.base.ManagedObject * @author SAP * @version 1.60.39 * @alias sap.m.TablePersoDialog */ var TablePersoDialog = ManagedObject.extend("sap.m.TablePersoDialog", /** @lends sap.m.TablePersoDialog */ { constructor : function(sId, mSettings) { ManagedObject.apply(this, arguments); }, metadata : { properties: { "contentWidth": {type: "sap.ui.core.CSSSize"}, "contentHeight": {type: "sap.ui.core.CSSSize", since: "1.22"}, "persoMap": {type: "object"}, "columnInfoCallback": {type: "object", since: "1.22"}, "initialColumnState": {type: "object", since: "1.22"}, "hasGrouping": {type: "boolean", since: "1.22"}, "showSelectAll": {type: "boolean", since: "1.22"}, "showResetAll": {type: "boolean", since: "1.22"} }, aggregations: { /** * Refers to the service for reading and writing the personalization. * @deprecated Since version 1.30.1 * This aggregate is no longer used. It collided with the TablePersoController's * persoService reference */ "persoService": { type: "Object", multiple: false, deprecated: true } }, associations: { /** * The table which shall be personalized. */ "persoDialogFor": "sap.m.Table" }, events: { confirm: {}, cancel: {} }, library: "sap.m" } }); /** * Initializes the TablePersoDialog instance after creation. * * @protected */ TablePersoDialog.prototype.init = function() { var that = this, iLiveChangeTimer = 0; // Resource bundle, for texts this._oRb = sap.ui.getCore().getLibraryResourceBundle("sap.m"); // To store the column settings this._oP13nModel = new JSONModel(); // Make sure that model can contain more than the 100 entries // it may contain by default. // SUGGESTED IMPROVEMENT: use number of table columns instead this._oP13nModel.setSizeLimit(Number.MAX_VALUE); // Makes sure that 'selectAll' check box and check boxes // in the list are in sync: if selectAll is checked or unchecked, // all list checkboxes must be marked or unmarked, accordingly. this._fnUpdateCheckBoxes = jQuery.proxy(function(oEvent) { var bSelected = oEvent.getParameter('selected'), oData = this._oP13nModel.getData(); if (oEvent.getSource().getId() === this._getSelectAllCheckboxId()) { // 'Select All' checkbox has been changed oData.aColumns.forEach(function(oColumn) { oColumn.visible = bSelected; }); } else { // One of the list checkboxes has been modified // Update the state of the 'Select All' checkbox var bSelectAll = !oData.aColumns.some(function(oColumn) { return !oColumn.visible; }); oData.oHeader.visible = bSelectAll; } // Call setData to trigger update of bound controls this._oP13nModel.setData(oData); }, this); // SUGGESTED IMPROVEMENT: checkbox should be selected if space bar is pressed // on focused list item. Maybe this behavior could be part of the next // suggestion // SUGGESTED IMPROVEMENT: this function swaps check box and label // for each list item, whenever the table is re.rendered or the // list is updated. Better solution: create a list item control // for this case. // Template for list inside the dialog - 1 item per column this._oColumnItemTemplate = new InputListItem(this.getId() + "-li", { label: "{Personalization>text}", content: new CheckBox(this.getId() + "-cb", { selected: "{Personalization>visible}", select: this._fnUpdateCheckBoxes }) }).addStyleClass("sapMPersoDialogLI"); // Button definition for sorting of the table content(up/down) this._oButtonUp = new Button(this.getId() + "-buttonUp", { icon: "sap-icon://arrow-top", enabled: false, tooltip: that._oRb.getText('PERSODIALOG_UP'), press: function() { that._moveItem(-1); } }); this._oButtonDown = new Button(this.getId() + "-buttonDown",{ icon: "sap-icon://arrow-bottom", enabled: false, tooltip: that._oRb.getText('PERSODIALOG_DOWN'), press: function() { that._moveItem(1); } }); this._fnHandleResize = function() { // Check if dialog is rendered if (that._oDialog) { var $dialogCont = that._oDialog.$("cont"); var $scroll = that._oDialog.$("scroll"); if ($dialogCont.children().length > 0) { var iContentHeight = $dialogCont.children()[0].clientHeight; var iPaddingHeight = $scroll[0].clientHeight - iContentHeight; // Take the header border into account otherwise the scroll container's // height is 2px bigger and causes the selectAllToolbar to scroll as well var iHeaderHeight = that.getShowSelectAll() ? that._oSelectAllToolbar.$().outerHeight() : 0; that._oScrollContainer.setHeight((iContentHeight - iHeaderHeight - iPaddingHeight) + 'px'); } } }; this._fnUpdateArrowButtons = function(bUpdateFocus) { // Initialisation of the enabled property var bButtonDownEnabled = true, bButtonUpEnabled = true, sValue = that._oSearchField.getValue(), iItemCount = that._oList.getItems().length; if (!!sValue || that._oList.getSelectedItems().length === 0) { // Disable buttons if search filters the list or if list is empty bButtonUpEnabled = false; bButtonDownEnabled = false; } else { // Data available (1 or more items) if (that._oList.getItems()[0].getSelected()) { // First item selected: disable button "arrow top" and focus button "arrow bottom" bButtonUpEnabled = false; if (bUpdateFocus && that._oButtonDown.getDomRef()) { that._oButtonDown.getDomRef().focus(); } } if (that._oList.getItems()[iItemCount - 1].getSelected()) { // Last item selected: disable button "arrow bottom" and focus button "arrow top" bButtonDownEnabled = false; if (bUpdateFocus && that._oButtonUp.getDomRef()) { that._oButtonUp.getDomRef().focus(); } } } that._oButtonUp.setEnabled(bButtonUpEnabled); that._oButtonDown.setEnabled(bButtonDownEnabled); }; // SUGGESTED IMPROVEMENT: this function swaps check box and label // for each list item, whenever the table is re.rendered or the // list is updated. Better solution: create a list item control // for this case. this._fnListUpdateFinished = function() { // Find all checkboxes in the list var aItems = that._oList.$().find('.sapMCb'), iItemsLength = aItems.length; // 'forEach' does not work for (var i = 0; i < iItemsLength; i++) { var $checkBox = jQuery(aItems[i]).parent(), aSiblings = $checkBox.siblings(), $label = aSiblings.length == 1 ? jQuery(aSiblings[0]) : null; if ($label) { $checkBox = $checkBox.detach(); $checkBox[0].className = 'sapMLIBSelectM'; $checkBox.insertBefore($label); } } // that._sLastSelectedItemId is used to have an initial selection when the dialog // is opened for the first time and after 'resetAll' has been called if (that._sLastSelectedItemId) { var fnItemMatches = function (oListItem) { var bResult = (oListItem.getBindingContext('Personalization') && oListItem.getBindingContext('Personalization').getProperty('id') === that._sLastSelectedItemId); if (bResult) { that._oList.setSelectedItem(oListItem); } return bResult; }; // Use 'some' to make sure it only traverses the array of listItems // as far as needed that._oList.getItems().some(fnItemMatches); // Clear last selected item so it does not get used again that._sLastSelectedItemId = null; // Make sure that arrow buttons are updated if (that._fnUpdateArrowButtons) { that._fnUpdateArrowButtons.call(this); } } }; this._fnAfterDialogOpen = function () { // Make sure that arrow buttons are updated when dialog is opened that._fnUpdateArrowButtons.call(that); }; this._fnAfterScrollContainerRendering = function () { // Scroll container gets focused in Firefox that._oScrollContainer.$().attr('tabindex', '-1'); }; this._oList = new List(this.getId() + "-colList",{ includeItemInSelection: true, noDataText: this._oRb.getText('PERSODIALOG_NO_DATA'), mode: ListMode.SingleSelectMaster, selectionChange: function(){ this._fnUpdateArrowButtons.call(this); }.bind(this), updateFinished: this._fnListUpdateFinished }); this._oList.addDelegate({onAfterRendering : this._fnListUpdateFinished}); this._oSearchField = new SearchField(this.getId() + "-searchField", { width: "100%", liveChange: function (oEvent) { var sValue = oEvent.getSource().getValue(), iDelay = (sValue ? 300 : 0); // No delay if value is empty // Execute search after user stops typing for 300ms clearTimeout(iLiveChangeTimer); if (iDelay) { iLiveChangeTimer = setTimeout(function () { that._executeSearch(); }, iDelay); } else { that._executeSearch(); } }, // Execute the standard search search: function () { that._executeSearch(); } }); this._oScrollContainer = new ScrollContainer({ horizontal: false, vertical: true, content:[this._oList], width:'100%' }); this._oScrollContainer.addDelegate({onAfterRendering : this._fnAfterScrollContainerRendering}); this._resetAllButton = new Button(this.getId() + "-buttonUndo", { icon: "sap-icon://undo", tooltip: this._oRb.getText('PERSODIALOG_UNDO'), press : function () { this._resetAll(); }.bind(this) }).addStyleClass("sapMPersoDialogResetBtn"); this._oSelectAllCheckbox = new CheckBox(this._getSelectAllCheckboxId(), { selected: "{Personalization>/oHeader/visible}", select: this._fnUpdateCheckBoxes, text: "{Personalization>/oHeader/text}" }).addStyleClass("sapMPersoDialogSelectAllCb"); // SUGGESTED IMPROVEMENT: adjust alignment of selectAll checkbox in compact mode this._oSelectAllToolbar = new Toolbar(this.getId() + "-toolbarSelAll", { // makes sure that toolbar itself is not clickable and removed from tab chain active: false, design : ToolbarDesign.Transparent, content: [this._oSelectAllCheckbox, this._resetAllButton] }).addStyleClass("sapMPersoDialogFixedBar"); this._oDialog = new Dialog(this.getId() + "-Dialog", { title : this._oRb.getText("PERSODIALOG_COLUMNS_TITLE"), stretch: Device.system.phone, horizontalScrolling: false, verticalScrolling: false, initialFocus: (Device.system.desktop ? this._oList : null), content : [ this._oSelectAllToolbar, this._oScrollContainer], subHeader : new Toolbar(this.getId() + "-toolbar", { //makes sure that toolbar itself is not clickable and removed from tab chain active : false, content: [ this._oButtonUp, this._oButtonDown, this._oSearchField ] }), leftButton : new Button(this.getId() + "-buttonOk", { text : this._oRb.getText("PERSODIALOG_OK"), press : function () { that._oDialog.close(); that._oSearchField.setValue(""); that._oSelectAllToolbar.setVisible(true); Device.resize.detachHandler(that._fnHandleResize); that.fireConfirm(); } }), rightButton : new Button(this.getId() + "-buttonCancel", { text: this._oRb.getText("PERSODIALOG_CANCEL"), press: function () { that._oDialog.close(); that._oSearchField.setValue(""); that._oSelectAllToolbar.setVisible(true); Device.resize.detachHandler(that._fnHandleResize); that.fireCancel(); } }), afterOpen: this._fnAfterDialogOpen }).addStyleClass("sapMPersoDialog"); }; /** * Returns the personalizations made. Currently supports * a 'columns' property which holds an array of settings, * one element per column in the associated table. The element * contains column-specific information as follows: id: column id; * order: new order; text: the column's header text that was displayed * in the dialog; visible: visibility (true or false). * * @return {object} the personalization data * @public */ TablePersoDialog.prototype.retrievePersonalizations = function () { return this._oP13nModel.getData(); }; /** * Sets the content of the dialog, being list items representing * the associated table's column settings, and opens the dialog * @public */ TablePersoDialog.prototype.open = function () { var aSorter = null; if (this.getHasGrouping()) { aSorter = [new Sorter('group', false, true)]; } // Get the associated Table's column info and set it into the Personalization model this._readCurrentSettingsFromTable(); // SUGGESTED IMPROVEMENT: Move the following code block into // 'init' method. Seems like it is not necessary to call setModel // and 'bindAggregation' over and over angain, when the dialog is // opened. this._oDialog.setModel(this._oP13nModel, "Personalization"); this._oList.bindAggregation("items", { path: "Personalization>/aColumns", sorter: aSorter, template: this._oColumnItemTemplate }); // SUGGESTED IMPROVEMENT: until here if (!this._oList.getSelectedItem()) { // Make sure initial selection is set var aItems = this._oList.getItems(); if (this.getHasGrouping()) { aItems = aItems.filter(function (oItem){ return oItem.getMetadata().getName() != "sap.m.GroupHeaderListItem"; }); } if (aItems.length > 0) { this._sLastSelectedItemId = aItems[0].getBindingContext('Personalization').getProperty('id'); } } // Update 'Move' button's state this._fnUpdateArrowButtons.call(this); // Now show the dialog this._oDialog.open(); // SUGGESTED IMPROVEMENT: this delegate should rather be attached to // 'onAfterOpen' since the dialog may not be opened yet by the time // it is executed. // _fnHandleResize is called to make sure that 'selectallToolBar' does not show // scrollbar this._fnHandleResize.call(this); Device.resize.attachHandler(this._fnHandleResize); }; TablePersoDialog.prototype.setContentHeight = function(sHeight) { this.setProperty("contentHeight", sHeight, true); this._oDialog.setContentHeight(sHeight); return this; }; TablePersoDialog.prototype.setContentWidth = function(sWidth) { this.setProperty("contentWidth", sWidth, true); this._oDialog.setContentWidth(sWidth); return this; }; /** * Destroys the control * @private */ TablePersoDialog.prototype.exit = function () { this._oRb = null; this._oP13nModel = null; if (this._oColumnItemTemplate) { this._oColumnItemTemplate.destroy(); this._oColumnItemTemplate = null; } if (this._oSelectAllToolbar) { this._oSelectAllToolbar.destroy(); this._oSelectAllToolbar = null; } if (this._oList) { this._oList.destroy(); this._oList = null; } if (this._oSearchField) { this._oSearchField.destroy(); this._oSearchField = null; } if (this._oScrollContainer) { this._oScrollContainer.destroy(); this._oScrollContainer = null; } if (this._oDialog) { this._oDialog.destroy(); this._oDialog = null; } if (this._oButtonDown) { this._oButtonDown.destroy(); this._oButtonDown = null; } if (this._oButtonUp) { this._oButtonUp.destroy(); this._oButtonUp = null; } }; /* =========================================================== */ /* begin: internal methods */ /* =========================================================== */ /** * Turn column visibility and order back to initial state (state before table * was personalized) * @private */ TablePersoDialog.prototype._resetAll = function () { if (this.getInitialColumnState()) { // Deep copy of Initial Data, otherwise initial data will be changed // and can only be used once to restore the initial state var aInitialStateCopy = jQuery.extend(true, [], this.getInitialColumnState()), that = this; // CSN 0120031469 0000184938 2014 // Remember last selected row, so it can be selected again after // reset all is done var oLastSelectedItem = this._oList.getSelectedItem(); this._sLastSelectedItemId = oLastSelectedItem && oLastSelectedItem.getBindingContext('Personalization') && oLastSelectedItem.getBindingContext('Personalization').getProperty('id'); // CSN 0120061532 0001380609 2014 // Make sure that captions are not replaced by column id's. This my be the case if // initalStateCopy has been created too early if (this._mColumnCaptions) { aInitialStateCopy.forEach( function(oColumn) { oColumn.text = that._mColumnCaptions[oColumn.id]; }); } this._oP13nModel.getData().aColumns = aInitialStateCopy; this._oP13nModel.getData().oHeader.visible = !this.getInitialColumnState().some(function(oColumn) { return !oColumn.visible; }); this._oP13nModel.updateBindings(); //Make sure that list is rerendered so that _fnListUpdateFinished is called //and list items are rendered correctly sap.ui.getCore().applyChanges(); } }; /** * Moves an item up or down, swapping it with the neighbour. * Does this in the bound model. * @private * @param {int} iDirection the move direction (-1 up, 1 down) */ TablePersoDialog.prototype._moveItem = function (iDirection) { // Abort if nothing selected var oSelectedItem = this._oList.getSelectedItem(); if (!oSelectedItem) { return; } // The items themselves var oData = this._oP13nModel.getData(); // Get array index of selected item var item = oSelectedItem.getBindingContext("Personalization").getPath().split("/").pop() * 1; // Get array index of item to swap with var swap = item + iDirection; // Abort if out of bounds if ( swap < 0 || swap >= oData.aColumns.length ) { return; } // Do the swap var temp = oData.aColumns[swap]; oData.aColumns[swap] = oData.aColumns[item]; // Make sure the order member is adapted as well! oData.aColumns[swap].order = swap; oData.aColumns[item] = temp; // Make sure the order member is adapted as well! oData.aColumns[item].order = item; // Remove selection before binding this._oList.removeSelections(true); // Call setData to trigger update of bound controls this._oP13nModel.updateBindings(); // Switch the selected item var oSwapItem = this._oList.getItems()[swap]; this._oList.setSelectedItem(oSwapItem, true); // Scroll to selected item // Make sure that item is selected so 'oSwapItem.$()' // is not empty sap.ui.getCore().applyChanges(); // swapItem need to be rendered, otherwise we can not // perfrom the necessary calculations if (oSwapItem.getDomRef()) { var iElementOffset = oSwapItem.$().position().top, // This is the minimal height that should be visible from the selected element // 18 means 18px which corresponds to 3em iMinHeight = 18, iViewPortHeight = this._oScrollContainer.$().height(), iViewPortStart = this._oScrollContainer.$().offset().top - this._oList.$().offset().top, iViewPortEnd = iViewPortStart + iViewPortHeight; if (iElementOffset < iViewPortStart ) { // Selected element is above visible viewport // scroll up so at least 'iMinHeight' is visible of the moved element this._oScrollContainer.scrollTo(0, Math.max(0, iViewPortStart - iViewPortHeight + iMinHeight)); } else if (iElementOffset + iMinHeight > iViewPortEnd) { // Selected element is below visible viewport // scroll down to the vertical position of the moved element this._oScrollContainer.scrollTo(0, iElementOffset); } // Otherwise, element is within the scroll container's viewport, so no action is necessary } this._fnUpdateArrowButtons.call(this, true); }; /** * Reads current column settings from the table and stores in the model * @private */ TablePersoDialog.prototype._readCurrentSettingsFromTable = function() { var oTable = sap.ui.getCore().byId(this.getPersoDialogFor()), that = this, aCurrentColumns = this.getColumnInfoCallback().call(this, oTable, this.getPersoMap()); this._oP13nModel.setData({ aColumns : aCurrentColumns, oHeader : { text : this._oRb.getText("PERSODIALOG_SELECT_ALL"), visible : !aCurrentColumns.some(function(oColumn) { return !oColumn.visible; }), id: this._getSelectAllCheckboxId() } }); // Remember column captions, needed for 'Reset All' // This is a workaround to fix an issue with unavailable column texts // after executing 'resetAll' (see 'resetAll' and CSN 0120061532 0001380609 2014) this._mColumnCaptions = {}; aCurrentColumns.forEach( function(oColumn) { that._mColumnCaptions[oColumn.id] = oColumn.text; }); }; /** * Filters the columns list with the given value * @return {string} the select all checkbox id. * @private */ TablePersoDialog.prototype._getSelectAllCheckboxId = function () { return this.getId() + '_SelectAll'; }; /** * Filters the columns list with the given value * @return {sap.m.TablePersoDialog} the tablePersoDialog instance. * @private */ TablePersoDialog.prototype._executeSearch = function () { var sValue = this._oSearchField.getValue(), oFilter = new Filter("text", FilterOperator.Contains, sValue), oBinding = this._oList.getBinding("items"); this._oSelectAllToolbar.setVisible(!sValue && this.getShowSelectAll()); oBinding.filter([oFilter]); this._fnUpdateArrowButtons.call(this); return this; }; /** * Setter to turn on/ switch off TablePersoDialog's grouping mode. * @param {boolean} bHasGrouping groping mode on or off. * @return {sap.m.TablePersoDialog} the TablePersoDialog instance. * @public */ TablePersoDialog.prototype.setHasGrouping = function (bHasGrouping) { this.setProperty("hasGrouping", bHasGrouping, true); var oBar = this._oDialog.getSubHeader(); if (!bHasGrouping) { if (oBar.getContent().length === 1) { // Only search field is displayed, add up- and down // buttons oBar.insertContent(this._oButtonDown, 0); oBar.insertContent(this._oButtonUp, 0); } } else { oBar.removeContent(this._oButtonUp); oBar.removeContent(this._oButtonDown); } return this; }; /** * Setter to show/hide TablePersoDialog's 'selectAll' checkbox. * @param {boolean} bShowSelectAll selectAll checkbox visible or not. * @return {sap.m.TablePersoDialog} the TablePersoDialog instance. * @public */ TablePersoDialog.prototype.setShowSelectAll = function (bShowSelectAll) { this.setProperty("showSelectAll", bShowSelectAll, true); this._oSelectAllToolbar.setVisible(bShowSelectAll); // Need to recalculate content height now this._fnHandleResize.call(this); return this; }; /** * Setter to show/hide TablePersoDialog's 'Undo Personalization' button. * @param {boolean} bShowResetAll 'undo Personalization' button visible or not. * @return {sap.m.TablePersoDialog} the TablePersoDialog instance. * @public */ TablePersoDialog.prototype.setShowResetAll = function (bShowResetAll) { this.setProperty("showResetAll", bShowResetAll, true); this._resetAllButton.setVisible(bShowResetAll); return this; }; return TablePersoDialog; });