@openui5/sap.m
Version:
OpenUI5 UI Library sap.m
833 lines (699 loc) • 29.1 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/base/i18n/Localization",
'sap/m/p13n/util/diff',
'sap/ui/base/Object',
'sap/base/util/merge',
'sap/base/util/deepEqual',
'sap/m/p13n/SelectionPanel',
'sap/m/p13n/modules/xConfigAPI',
"sap/ui/core/Locale",
"sap/ui/core/Lib",
'sap/ui/core/mvc/View'
], (
Localization,
diff,
BaseObject,
merge,
deepEqual,
SelectionPanel,
xConfigAPI,
Locale,
Library,
View
) => {
"use strict";
/**
* Personalization <code>SelectionState</code> object type. This object describes the state processed by this controller when accessing it through the {@link sap.m.p13n.Engine Engine}.
*
* @public
* @typedef {object} sap.m.p13n.SelectionState
* @property {string} key The key for the group state
* @property {boolean} [visible] Defines whether the item is selected (if a selection state is provided, it's selected automatically)
* @property {int} [index] Describes the index of the selection item
*
*/
/**
* Constructor for a new <code>SelectionController</code>.
*
* @param {object} mSettings Initial settings for the new controller
* @param {sap.ui.core.Control} mSettings.control The control instance that is personalized by this controller
* @param {function(sap.ui.core.Element):string} [mSettings.getKeyForItem] By default the SelectionController tries to identify the existing item through the
* key by checking if there is an existing item with this id. This behaviour can be overruled by implementing this method which will
* provide the according item of the <code>targetAggregation</code> to return the according key associated to this item.
* @param {string} mSettings.targetAggregation The name of the aggregation that is now managed by this controller
* @param {string} [mSettings.persistenceIdentifier] If multiple <code>SelectionController</code> controls exist for a personalization use case, the <code>persistenceIdentifier</code> property must be added to uniquely identify a <code>SelectionController</code> control
* @param {sap.m.p13n.MetadataHelper} [mSettings.helper] The <code>{@link sap.m.p13n.MetadataHelper MetadataHelper}</code> to provide metadata-specific information. It may be used to define more granular information for the selection of items.
*
* @class
* The <code>SelectionController</code> entity serves as a base class to create control-specific personalization implementations.
*
* @extends sap.ui.base.Object
*
* @author SAP SE
* @version 1.146.0
*
* @public
* @alias sap.m.p13n.SelectionController
*/
const SelectionController = BaseObject.extend("sap.m.p13n.SelectionController", {
constructor: function(mSettings) {
BaseObject.call(this);
this._oAdaptationControl = mSettings.control;
this._sPersistenceIdentifier = mSettings.persistenceIdentifier ? mSettings.persistenceIdentifier : null;
this._oMetadataHelper = mSettings.helper ? mSettings.helper : null;
/**
* The 'stableKeys' can be provided in the constructor to exclude the keys from the p13n UI, while still respecting them in the
* delta logic. For instance by ignoring the first column of a TreeTable in the p13n dialog while still not removing this column
* on closing the dialog.
*/
this._aStableKeys = mSettings.stableKeys || [];
if (!this._oAdaptationControl) {
throw new Error("Always provide atleast a 'control' configuration when creating a new p13n controller!");
}
this._sTargetAggregation = mSettings.targetAggregation;
this._fSelector = mSettings.getKeyForItem;
this._oP13nData = null;
this._bLiveMode = false;
this._bResetEnabled = false;
this._bReorderingEnabled = mSettings.hasOwnProperty("enableReorder") ? mSettings.enableReorder : true;
}
});
SelectionController.prototype.DIFF_INSERT_TYPE = "insert";
SelectionController.prototype.DIFF_DELETE_TYPE = "delete";
/**
* Gets defined <code>persistenceIdentifier</code> of the controller.
* @returns {string|null} String if <code>_sPersistenceIdentifier</code> is defined, otherwise null.
*/
SelectionController.prototype.getPersistenceIdentifier = function() {
return this._sPersistenceIdentifier;
};
/**
* Gets defined {@link sap.m.p13n.MetadataHelper MetadataHelper} of the controller.
* @returns {sap.m.p13n.MetadataHelper|null} Instance of {@link sap.m.p13n.MetadataHelper} if defined, otherwise null.
*/
SelectionController.prototype.getMetadataHelper = function() {
return this._oMetadataHelper;
};
/**
* The control that is being personalized via this controller.
*
* @returns {sap.ui.mdc.Control} The control which is being adapted.
*/
SelectionController.prototype.getAdaptationControl = function() {
return this._oAdaptationControl;
};
SelectionController.prototype.getTargetAggregation = function() {
return this._sTargetAggregation;
};
/**
* Defines the available ChangeTypes (should be in sync with 'getDelta').
*
* @returns {object} A map of legal change types.
*/
SelectionController.prototype.getChangeOperations = () => {
return {
add: "addItem",
remove: "removeItem",
move: "moveItem"
};
};
/**
* Defines which control(s) are considered for the reset.
*
* @returns {sap.ui.core.Control | sap.ui.core.Control[]}
**/
SelectionController.prototype.getSelectorForReset = function() {
return this._oAdaptationControl;
};
SelectionController.prototype.sanityCheck = (oState) => {
return oState;
};
/**
* Formats an external state to the appropriate internal state containg only the relevant information for the controller.
*
* @param {object} oExternalState external state to format
* @returns {object} formatted internal state
*/
SelectionController.prototype.formatToInternalState = (oExternalState) => {
return oExternalState;
};
/**
* Enhances the p13n popup. Can be used to add additional content to the popup.
*
* @param {sap.m.Dialog} oPopup The popup that is used to host the personalization UI
*/
SelectionController.prototype.enhancePopup = (oPopup) => {};
/**
* The actual UI used for personalization.
*
* @param {sap.ui.mdc.util.PropertyHelper} oPropertyHelper The property helper instance
* @returns {sap.ui.core.Control|string|Promise} The control which is going to be used in the p13n container.
*/
SelectionController.prototype.initAdaptationUI = function(oPropertyHelper) {
const oAdaptationData = this.mixInfoAndState(oPropertyHelper);
this._oPanel = this.createUI(oAdaptationData);
return Promise.resolve(this._oPanel);
};
SelectionController.prototype.createUI = function(oAdaptationData) {
const oSelectionPanel = new SelectionPanel({
showHeader: true,
enableCount: true
});
oSelectionPanel.setEnableReorder(this._bReorderingEnabled);
return oSelectionPanel.setP13nData(oAdaptationData.items);
};
const getViewForControl = (oControl) => {
if (oControl instanceof View) {
return oControl;
}
if (oControl && typeof oControl.getParent === "function") {
oControl = oControl.getParent();
return getViewForControl(oControl);
}
};
SelectionController.prototype._calcPresentState = function() {
const aState = [],
aAggregationItems = this.getAdaptationControl().getAggregation(this.getTargetAggregation()) || [];
const oView = getViewForControl(this.getAdaptationControl());
aAggregationItems.forEach((oItem, iIndex) => {
const sKey = oItem.getVisible() ? oItem.data("p13nKey") : null;
const sId = oView ? oView.getLocalId(oItem.getId()) : oItem.getId();
const vRelevant = sKey || (this._fSelector ? this._fSelector(oItem) : oItem.getVisible());
if (vRelevant) {
aState.push({
key: typeof vRelevant === "boolean" ? sId : vRelevant
});
}
});
return aState;
};
SelectionController.prototype.getCurrentState = function() {
const aState = this._calcPresentState(); //The state of the control without xConfig
const oXConfig = xConfigAPI.readConfig(this.getAdaptationControl()) || {};
const oItemXConfig = oXConfig.hasOwnProperty("aggregations") ? oXConfig.aggregations[this._sTargetAggregation] : {};
const aItemXConfig = [];
if (oItemXConfig) {
Object.entries(oItemXConfig).forEach(([sKey, oConfig]) => {
aItemXConfig.push({key: sKey, position: oConfig.position, visible: oConfig.visible});
});
aItemXConfig.sort((a, b) => a.position - b.position);
}
aItemXConfig.sort((a,b) => a.position - b.position);
aItemXConfig.forEach(({key}) => {
const aStateKeys = aState.map((o) => {
return o.key;
});
let iCurrentIndex = aStateKeys.indexOf(key);
const iNewIndex = oItemXConfig[key].position;
const bVisible = oItemXConfig[key].visible !== false;
const bReordered = iNewIndex !== undefined;
if (bVisible && iCurrentIndex === -1) {
aState.push({
key: key
});
}
if (bVisible && bReordered && aState.length > 0) {
const oItem = aState.splice(iCurrentIndex, 1)[0];
aState.splice(iNewIndex, 0, oItem);
iCurrentIndex = iNewIndex;
}
if (oItemXConfig[key].visible === false && iCurrentIndex > -1) {
aState.splice(iCurrentIndex, 1);
}
});
return aState;
};
SelectionController.prototype.getStateKey = () => {
return "items";
};
SelectionController.prototype.getDelta = function(mPropertyBag) {
const sPresenceAttribute = this._getPresenceAttribute(mPropertyBag.externalAppliance);
const fnFilterUnselected = (oItem) => {
return oItem.hasOwnProperty(sPresenceAttribute) && oItem[sPresenceAttribute] === false ? false : true;
};
const aNewStatePrepared = mPropertyBag.applyAbsolute ?
mPropertyBag.changedState.filter(fnFilterUnselected) :
this._getFilledArray(mPropertyBag.existingState, mPropertyBag.changedState, sPresenceAttribute).filter(fnFilterUnselected);
this._aStableKeys.forEach((sKey, iIndex) => {
const mExistingState = this.arrayToMap(this.getCurrentState());
const mNewState = this.arrayToMap(aNewStatePrepared);
const iStableIndex = mExistingState[sKey] || (iIndex - 1);
if (!mNewState.hasOwnProperty(sKey)) {
aNewStatePrepared.splice(iStableIndex, 0, mExistingState[sKey]);
}
});
mPropertyBag.changedState = aNewStatePrepared;
//Example: Dialog Ok --> don't trigger unnecessary flex change processing
if (deepEqual(mPropertyBag.existingState, aNewStatePrepared)) {
return [];
} else {
return this.getArrayDeltaChanges(mPropertyBag);
}
};
/**
* Generates a set of changes based on the given arrays for a specified control
*
* @param {object} mDeltaInfo Map containing the necessary information to calculate the diff as change objects
* @param {array} mDeltaInfo.existingState An array describing the control state before a adaptation
* @param {array} mDeltaInfo.changedState An array describing the control state after a certain adaptation
* @param {object} mDeltaInfo.control Control instance which is being used to generate the changes
* @param {object} mDeltaInfo.changeOperations Map containing the changeOperations for the given Control instance
* @param {string} mDeltaInfo.changeOperations.add Name of the control specific 'add' changehandler
* @param {string} mDeltaInfo.changeOperations.remove Name of the control specific 'remove' changehandler
* @param {string} [mDeltaInfo.changeOperations.move] Name of the control specific 'move' changehandler
* @param {string} [mDeltaInfo.generator] Name of the change generator (E.g. the namespace of the UI creating the change object)
*
* @returns {array} Array containing the delta based created changes
*/
SelectionController.prototype.getArrayDeltaChanges = function(mDeltaInfo) {
const aExistingArray = mDeltaInfo.existingState;
const aChangedArray = mDeltaInfo.changedState;
const oControl = mDeltaInfo.control;
const sInsertOperation = mDeltaInfo.changeOperations.add;
const sRemoveOperation = mDeltaInfo.changeOperations.remove;
const sMoveOperation = mDeltaInfo.changeOperations.move;
const aDeltaAttributes = mDeltaInfo.deltaAttributes || [];
const mDeleteInsert = this._calculateDeleteInserts(aExistingArray, aChangedArray, aDeltaAttributes);
let aChanges = this._createAddRemoveChanges(mDeleteInsert.deletes, oControl, sRemoveOperation, aDeltaAttributes);
if (sMoveOperation) {
const aExistingArrayWithoutDeletes = this._removeItems(aExistingArray, mDeleteInsert.deletes);
const aChangedArrayWithoutInserts = this._removeItems(aChangedArray, mDeleteInsert.inserts);
const aMoveChanges = this._createMoveChanges(aExistingArrayWithoutDeletes, aChangedArrayWithoutInserts, oControl, sMoveOperation, aDeltaAttributes);
aChanges = aChanges.concat(aMoveChanges);
}
const aInsertChanges = this._createAddRemoveChanges(mDeleteInsert.inserts, oControl, sInsertOperation, aDeltaAttributes);
aChanges = aChanges.concat(aInsertChanges);
return aChanges;
};
SelectionController.prototype._createMoveChanges = function(aExistingItems, aChangedItems, oControl, sOperation, aDeltaAttributes) {
const aChanges = [];
if (aExistingItems.length !== aChangedItems.length) {
return aChanges;
}
const fnSymbol = (o) => {
let sDiff = "";
aDeltaAttributes.forEach((sAttribute) => {
sDiff = sDiff + o[sAttribute];
});
return sDiff;
};
const aDiff = diff(aExistingItems, aChangedItems, {
symbol: fnSymbol,
includeReference: true
});
const aEnhancedDiff = aDiff.map((oDiffEntry) => {
oDiffEntry.affectedKey = oDiffEntry.affectedReference[oDiffEntry.affectedIndex].key;
return oDiffEntry;
});
for (let i = 0; i < aEnhancedDiff.length; i++) {
if (aEnhancedDiff[i].type === this.DIFF_INSERT_TYPE) {
let nIndex = aEnhancedDiff[i].index;
const nIndexDelta = this._getIndexDelta(aEnhancedDiff, i);
nIndex += nIndexDelta;
if (nIndex >= aChangedItems.length) {
nIndex = aChangedItems.length;
}
aChanges.push(this._createMoveChange(aEnhancedDiff[i].affectedKey, nIndex, sOperation, oControl));
}
}
return aChanges;
};
SelectionController.prototype._getIndexDelta = function(aDiff, iCurrentIndex) {
const oCurrentDiffEntry = aDiff[iCurrentIndex];
const aPreviousDiffs = aDiff.slice(0, iCurrentIndex);
const aPosteriorDiffs = aDiff.slice(iCurrentIndex);
// get all major inserts and minor deletes independent of affectedKey
const aMajorInserts = aPosteriorDiffs.filter((oDiffEntry) => oDiffEntry.type === this.DIFF_INSERT_TYPE);
const aMinorDeletes = aPreviousDiffs.filter((oDiffEntry) => oDiffEntry.type == this.DIFF_DELETE_TYPE);
const aRelevantMinorDeletes = aMinorDeletes.filter((oDiffEntry) => {
// don't take "me" into account
if (oDiffEntry.affectedKey === oCurrentDiffEntry.affectedKey) {
return false;
}
// check if there are major inserts with same affectedKey
const oMajorInsert = aMajorInserts.find((oMajorInsert) => oMajorInsert.affectedKey === oDiffEntry.affectedKey);
// don't take into account
// * if delete and insert condense each other (MajorInsert => don't condense each other)
// * if the delete index is not strict smaller than the current index
if (oMajorInsert && oDiffEntry.index < oCurrentDiffEntry.index) {
return true;
}
return false;
});
return aRelevantMinorDeletes.length;
};
SelectionController.prototype._createAddRemoveChanges = function(aItems, oControl, sOperation, aDeltaAttributes) {
const aChanges = [];
for (let i = 0; i < aItems.length; i++) {
aChanges.push(this._createAddRemoveChange(oControl, sOperation, this._getChangeContent(aItems[i], aDeltaAttributes)));
}
return aChanges;
};
SelectionController.prototype._removeItems = function(aTarget, aItems) {
let sKey;
const aResultingTarget = [];
for (let i = 0; i < aTarget.length; i++) {
sKey = aTarget[i].key || aTarget[i].name;
if (this._indexOfByKeyName(aItems, sKey) === -1) {
aResultingTarget.push(aTarget[i]);
}
}
return aResultingTarget;
};
SelectionController.prototype._indexOfByKeyName = (aArray, sKey) => {
let nIndex = -1;
aArray.some((oItem, nIdx) => {
if ((oItem.key === sKey) || (oItem.name === sKey)) {
nIndex = nIdx;
}
return (nIndex != -1);
});
return nIndex;
};
SelectionController.prototype._calculateDeleteInserts = function(aSource, aTarget, aDeltaAttributes) {
let i, sKey, oItem, nIdx;
const mDeleteInserts = {
deletes: [],
inserts: []
};
for (i = 0; i < aSource.length; i++) {
sKey = aSource[i].key || aSource[i].name;
nIdx = this._indexOfByKeyName(aTarget, sKey);
if (nIdx === -1) {
oItem = merge({}, aSource[i]);
mDeleteInserts.deletes.push(oItem);
} else if (aDeltaAttributes.length) {
if (this._verifyDeltaAttributes(aSource[i], aTarget[nIdx], aDeltaAttributes)) {
mDeleteInserts.deletes.push(aSource[i]);
oItem = merge({}, aTarget[nIdx]);
oItem.index = nIdx;
mDeleteInserts.inserts.push(oItem);
}
}
}
for (i = 0; i < aTarget.length; i++) {
sKey = aTarget[i].key || aTarget[i].name;
if (this._indexOfByKeyName(aSource, sKey) === -1) {
oItem = merge({}, aTarget[i]);
oItem.index = i;
mDeleteInserts.inserts.push(oItem);
}
}
return mDeleteInserts;
};
SelectionController.prototype._verifyDeltaAttributes = (oSource, oTarget, aDeltaAttributes) => {
let bReturn = false;
aDeltaAttributes.some((sAttr) => {
if (!oSource.hasOwnProperty(sAttr) && oTarget.hasOwnProperty(sAttr) ||
oSource.hasOwnProperty(sAttr) && !oTarget.hasOwnProperty(sAttr) ||
(oSource[sAttr] != oTarget[sAttr])) {
bReturn = true;
}
return bReturn;
});
return bReturn;
};
/**
* Method which reduces a propertyinfo map to changecontent relevant attributes.
* <b>Note:</b> This method determines the attributes stored in the changeContent.
*
* @param {object} oProperty Object containing all values prior to change creation
* @param {array} aDeltaAttributes Array containing all attributes that are necessary for the delta calculation
*
* @returns {object} Object containing reduced content
*/
SelectionController.prototype._getChangeContent = (oProperty, aDeltaAttributes) => {
const oChangeContent = {};
// Index
if (oProperty.hasOwnProperty("index") && oProperty.index >= 0) {
oChangeContent.index = oProperty.index;
}
aDeltaAttributes.forEach((sAttribute) => {
if (oProperty.hasOwnProperty(sAttribute)) {
oChangeContent[sAttribute] = oProperty[sAttribute];
}
});
return oChangeContent;
};
SelectionController.prototype._createAddRemoveChange = function(oControl, sOperation, oContent) {
const oChangeContent = oContent;
if (sOperation.indexOf("set") !== 0 && sOperation.indexOf("reset") !== 0) {
oChangeContent.value = (sOperation == this.getChangeOperations()["add"]);
}
if (this.getTargetAggregation()) {
oChangeContent.targetAggregation = this.getTargetAggregation();
}
if (this._sPersistenceIdentifier) {
oChangeContent.persistenceIdentifier = this._sPersistenceIdentifier;
}
const oAddRemoveChange = {
selectorElement: oControl,
changeSpecificData: {
changeType: sOperation,
content: oChangeContent
}
};
return oAddRemoveChange;
};
SelectionController.prototype._createMoveChange = function(sPropertykey, iNewIndex, sMoveOperation, oControl) {
const oContent = {
key: sPropertykey,
targetAggregation: this.getTargetAggregation(),
index: iNewIndex
};
if (this._sPersistenceIdentifier) {
oContent.persistenceIdentifier = this._sPersistenceIdentifier;
}
const oMoveChange = {
selectorElement: oControl,
changeSpecificData: {
changeType: sMoveOperation,
content: oContent
}
};
return oMoveChange;
};
SelectionController.prototype._getPresenceAttribute = (bexternalAppliance) => {
return "visible";
};
/**
* Allows calculations prior to applying a set of changes.
* This can be used to mix additional changes to auto-created changes.
*
* @returns {Promise} A Promise that should resolve with an array of additional changes.
*/
SelectionController.prototype.getBeforeApply = () => {
return Promise.resolve();
};
/**
* Initialized the inner model for the Personalization.
*
* @param {sap.ui.mdc.util.PropertyHelper} oPropertyHelper The propertyhelper that should be utilized for property determination.
* @returns {object} The personalization model data
*/
SelectionController.prototype.mixInfoAndState = function(oPropertyHelper) {
const aItemState = this.getCurrentState();
const mItemState = this.arrayToMap(aItemState);
const oP13nData = this.prepareAdaptationData(oPropertyHelper, (mItem, oProperty) => {
const oExisting = mItemState[oProperty.name || oProperty.key];
mItem.visible = !!oExisting;
mItem.position = oExisting ? oExisting.position : -1;
mItem.isRedundant = oExisting?.isRedundant ?? false;
return !(oProperty.visible === false || (this._aStableKeys.indexOf(oProperty.name || oProperty.key) > -1));
});
this.sortP13nData({
visible: "visible",
position: "position"
}, oP13nData.items);
oP13nData.items.forEach((oItem) => {
delete oItem.position;
});
return oP13nData;
};
/**
* @returns {object} The personalization data.
*
*/
SelectionController.prototype.getP13nData = function() {
return this._oPanel ? this._oPanel.getP13nData() : this._oAdaptationModel && this._oAdaptationModel.getProperty("/items");
};
SelectionController.prototype.model2State = false;
/**
* Can be used to trigger update after UI interactions such as "Ok" and "Reset"
*
* @param {sap.ui.mdc.util.PropertyHelper} oPropertyHelper The property helper instance
*/
SelectionController.prototype.update = function(oPropertyHelper) {
if (this._oPanel) {
if (!this._oPanel.isDestroyed()) {
const oAdaptationData = this.mixInfoAndState(oPropertyHelper);
this._oPanel.setP13nData(oAdaptationData.items);
}
} else if (this._oAdaptationModel) {
//'setData' causes unnecessary rerendering in some cases
const oP13nData = this.mixInfoAndState(oPropertyHelper);
this._oAdaptationModel.setProperty("/items", oP13nData.items);
this._oAdaptationModel.setProperty("/itemsGrouped", oP13nData.itemsGrouped);
}
};
SelectionController.prototype._getFilledArray = function(aPreviousItems, aNewItems, sRemoveProperty) {
const aNewItemsPrepared = merge([], aPreviousItems);
const aNewItemState = merge([], aNewItems);
aNewItemState.forEach((oItem) => {
const mExistingItems = this.arrayToMap(aNewItemsPrepared);
const oExistingItem = mExistingItems[oItem.key];
if (!oItem.hasOwnProperty(sRemoveProperty) || oItem[sRemoveProperty]) {
let iNewPosition = oItem.position;
if (oExistingItem) { //move if it exists
// do not reorder it in case it exists and no position is provided
iNewPosition = iNewPosition > -1 ? iNewPosition : oExistingItem.position;
const iOldPosition = oExistingItem.position;
aNewItemsPrepared.splice(iNewPosition, 0, aNewItemsPrepared.splice(iOldPosition, 1)[0]);
} else { //add if it does not exist the item will be inserted at the end
iNewPosition = iNewPosition > -1 ? iNewPosition : aNewItemsPrepared.length;
aNewItemsPrepared.splice(iNewPosition, 0, oItem);
}
aNewItemsPrepared[iNewPosition] = oItem; //overwrite existing item with new item (for correct values such as 'descending')
} else if (oExistingItem) { //check if exists before delete
aNewItemsPrepared[oExistingItem.position][sRemoveProperty] = false;
}
});
return aNewItemsPrepared;
};
SelectionController.prototype.getPropertySetterChanges = function(mDeltaInfo) {
const oControl = mDeltaInfo.control;
const aExistingState = mDeltaInfo.existingState;
const aChangedState = mDeltaInfo.changedState;
const sOperation = mDeltaInfo.operation;
const sSetAttribute = mDeltaInfo.deltaAttribute;
const aSetterChanges = [];
aChangedState.forEach((oItem) => {
//check if the provided state item holds the value to check for
if (oItem.hasOwnProperty(sSetAttribute)) {
const oExistingItem = aExistingState.find((oExisting) => {
const sIdentifier = oExisting.hasOwnProperty("key") ? "key" : "name";
return oExisting[sIdentifier] == oItem[sIdentifier];
});
//compare to identify delta (only create a change if really necessary)
const vOldValue = oExistingItem && oExistingItem.hasOwnProperty(sSetAttribute) && oExistingItem[sSetAttribute];
const vNewValue = oItem[sSetAttribute];
const bValueChanged = vOldValue !== vNewValue;
if (bValueChanged) {
aSetterChanges.push(this._createAddRemoveChange(oControl, sOperation, {
[oItem.hasOwnProperty("key") ? "key" : "name"]: oItem.key || oItem.name,
targetAggregation: this.getTargetAggregation(),
value: oItem[sSetAttribute]
}));
}
}
});
return aSetterChanges;
};
SelectionController.prototype.changesToState = function(aChanges) {
const aState = [];
aChanges.forEach((oChange) => {
const oStateDiffContent = merge({}, oChange.changeSpecificData.content);
const iIndex = oStateDiffContent.index;
delete oStateDiffContent.index;
//set the position in case its explicitly provided and different to the current position
if (iIndex !== undefined) {
oStateDiffContent.position = iIndex;
}
//set the presence attribute to false in case of an explicit remove
if (oChange.changeSpecificData.changeType === this.getChangeOperations()["remove"]) {
oStateDiffContent[this._getPresenceAttribute()] = false;
}
aState.push(oStateDiffContent);
});
return aState;
};
SelectionController.prototype.prepareAdaptationData = function(oPropertyHelper, fnEnhace, bGroupData) {
const aItems = [];
const mItemsGrouped = bGroupData ? {} : null;
const bEnhance = fnEnhace instanceof Function;
const oControllerHelper = this.getMetadataHelper();
const oHelper = oControllerHelper ? oControllerHelper : oPropertyHelper;
const aColumnsWithTextArrangement = (oHelper.getRedundantProperties?.() ?? []).map((p) => p.key);
oHelper.getProperties().forEach((oProperty) => {
const mItem = {};
mItem.key = oProperty.name || oProperty.key;
mItem.name = oProperty.name || oProperty.key;
if (bEnhance) {
const bIsValid = fnEnhace(mItem, oProperty);
if (!bIsValid) {
return;
}
}
mItem.label = oProperty.label || mItem.key;
mItem.tooltip = oProperty.tooltip;
mItem.isRedundant = aColumnsWithTextArrangement.includes(mItem.key);
if (mItemsGrouped) {
mItem.group = oProperty.group ? oProperty.group : "BASIC";
mItem.groupLabel = oProperty.groupLabel;
mItemsGrouped[mItem.group] = mItemsGrouped[mItem.group] ? mItemsGrouped[mItem.group] : [];
mItemsGrouped[mItem.group].push(mItem);
}
aItems.push(mItem);
});
const oAdaptationData = {
items: aItems
};
if (mItemsGrouped) {
oAdaptationData.itemsGrouped = this._buildGroupStructure(mItemsGrouped);
}
return oAdaptationData;
};
SelectionController.prototype._buildGroupStructure = function(mItemsGrouped) {
const aGroupedItems = [];
Object.keys(mItemsGrouped).forEach((sGroupKey) => {
this.sortP13nData("generic", mItemsGrouped[sGroupKey]);
aGroupedItems.push({
group: sGroupKey,
groupLabel: mItemsGrouped[sGroupKey][0].groupLabel || Library.getResourceBundleFor("sap.m").getText("p13n.BASIC_DEFAULT_GROUP"), //Grouplabel might not be necessarily be propagated to every item
groupVisible: true,
items: mItemsGrouped[sGroupKey]
});
});
return aGroupedItems;
};
SelectionController.prototype.sortP13nData = (oSorting, aItems) => {
const mP13nTypeSorting = oSorting;
const sPositionAttribute = mP13nTypeSorting.position;
const sSelectedAttribute = mP13nTypeSorting.visible;
const sLocale = new Locale(Localization.getLanguageTag()).toString();
const oCollator = window.Intl.Collator(sLocale, {});
// group selected / unselected --> sort alphabetically in each group
aItems.sort((mField1, mField2) => {
if (mField1[sSelectedAttribute] && mField2[sSelectedAttribute]) {
return (mField1[sPositionAttribute] || 0) - (mField2[sPositionAttribute] || 0);
} else if (mField1[sSelectedAttribute]) {
return -1;
} else if (mField2[sSelectedAttribute]) {
return 1;
} else if (!mField1[sSelectedAttribute] && !mField2[sSelectedAttribute]) {
return oCollator.compare(mField1.label, mField2.label);
}
});
};
SelectionController.prototype.arrayToMap = (aArray) => {
return aArray.reduce((mMap, oProp, iIndex) => {
mMap[oProp.key] = oProp;
mMap[oProp.key].position = iIndex;
return mMap;
}, {});
};
SelectionController.prototype.destroy = function() {
BaseObject.prototype.destroy.apply(this, arguments);
this._oAdaptationControl = null;
this._bLiveMode = null;
this._oPanel = null;
this._bResetEnabled = null;
this._oAdaptationModel = null;
};
return SelectionController;
});