VisDnDColumnToSlotProvider.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. 'use strict';
  2. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  3. /**
  4. * Licensed Materials - Property of IBM
  5. * IBM Business Analytics (C) Copyright IBM Corp. 2020
  6. * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
  7. */
  8. /**
  9. * @class VisDnDColumnToSlotProvider
  10. * @hideconstructor
  11. *
  12. * @classdesc Handles drag and drop of metadata to slot to create a visualization.
  13. */
  14. define(['underscore', '../../../../../lib/@waca/dashboard-common/dist/core/APIFactory', '../../../../../dataSources/utils/ShapingUIUtils', '../../VisMapColumnsToSlot', 'dashboard-analytics/apiHelpers/SlotAPIHelper', 'dashboard-analytics/widgets/livewidget/nls/StringResources'], function (_, APIFactory, ShapingUIUtils, VisMapColumnsToSlot, SlotAPIHelper, StringResources) {
  15. var MULTI_MEASURES_SERIES = '_multiMeasuresSeries';
  16. return function () {
  17. function VisDnDColumnToSlotProvider() {
  18. var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  19. _classCallCheck(this, VisDnDColumnToSlotProvider);
  20. this.visDnDUtils = options.visDnDUtils;
  21. this.dashboardAPI = options.dashboardAPI;
  22. this.datasources = options.datasources;
  23. this.datasourcesMoser = options.datasourcesMoser;
  24. this.content = options.content;
  25. this.visualization = options.visualization;
  26. this.transaction = options.transaction;
  27. this.visMapColumnsToSlot = new VisMapColumnsToSlot({
  28. content: this.content,
  29. visualization: this.visualization,
  30. transaction: this.transaction
  31. });
  32. }
  33. VisDnDColumnToSlotProvider.prototype.destroy = function destroy() {
  34. this.dashboardAPI = null;
  35. this.content = null;
  36. this.visDnDUtils = null;
  37. this.datasources = null;
  38. this.datasourcesMoser = null;
  39. this.visualization = null;
  40. this.transaction = null;
  41. };
  42. /**
  43. * @param {Object} source - the DndSource defined by whichever source starts the drag
  44. * @param {Object} target - the DnDTarget defined by the drop target which includes target specific information.
  45. * @param {String} target.el - the drop node.
  46. * @param {String} target.type - the type of the target (eg. slot.item)
  47. * @param {String} target.info - a target-specific function which includes information about that target.
  48. * @return {boolean} true if this provider supports the dndSource and target.
  49. */
  50. VisDnDColumnToSlotProvider.prototype.supports = function supports(source, target) {
  51. return (target.type === 'slot.item' || target.info.type === 'slot.item') && source.type === 'A11YMetadataColumns' || (source.type === 'MODEL_ITEM' || source.type === 'GRID_HEADER_ITEM') && source.data.columns || this._isMetadataColumnArray(source);
  52. };
  53. VisDnDColumnToSlotProvider.prototype._getA11YData = function _getA11YData(sourceId) {
  54. var module = sourceId && this.datasourcesMoser.getModule(sourceId);
  55. var copiedTreeItems = ShapingUIUtils.getCopiedTreeItems(module, this.dashboardAPI);
  56. return copiedTreeItems && copiedTreeItems.data && copiedTreeItems.data;
  57. };
  58. VisDnDColumnToSlotProvider.prototype._clearA11YData = function _clearA11YData() {
  59. ShapingUIUtils.clearCopiedTreeItems();
  60. this.dashboardAPI.triggerDashboardEvent('dataSourceGrid:clearSourceSelected');
  61. };
  62. VisDnDColumnToSlotProvider.prototype._isMetadataColumnArray = function _isMetadataColumnArray(sourceData) {
  63. return sourceData.type === 'metadataColumns' && Array.isArray(sourceData.data);
  64. };
  65. //The ColumnToSlot provider accepts 3 forms of input
  66. //1) For a11y, a hint exists in the source that indicates we need to fetch it from shaping.
  67. //2) Columns as from drag&drop data (which we receive from an actual drag&drop)
  68. //3) metadataColumns as a simple array of metadataColumnAPI's (which we receive from type-in)
  69. VisDnDColumnToSlotProvider.prototype._getNormalizedMetadataColumnArray = function _getNormalizedMetadataColumnArray(source) {
  70. if (source.type === 'A11YMetadataColumns') {
  71. var a11YData = this._getA11YData(this.dashboardAPI.getActiveDataSourceId());
  72. return a11YData && a11YData.columns;
  73. } else if (source.data && source.data.columns) {
  74. return source.data.columns;
  75. } else if (this._isMetadataColumnArray(source)) {
  76. return source.data.map(function (column) {
  77. return { metadataColumn: column };
  78. });
  79. }
  80. return null; //Its can't be normalized into valid input.
  81. };
  82. VisDnDColumnToSlotProvider.prototype.accepts = function accepts(source, target) {
  83. if (source.data && source.data.utils && source.data.utils.isValid()) {
  84. return this._acceptsMetadataColumns(source, target);
  85. }
  86. return false;
  87. };
  88. VisDnDColumnToSlotProvider.prototype.onDrop = function onDrop(source, target) {
  89. var droppedColumns = this._getNormalizedMetadataColumnArray(source);
  90. if (!this.visDnDUtils.validateSupportsOLAP(droppedColumns.map(function (c) {
  91. return c.metadataColumn;
  92. }))) {
  93. this.dashboardAPI.getFeature('Notification').setMessage(StringResources.get('visualizationDoesNotSupportOLAP'));
  94. return false;
  95. }
  96. var addAfter = target.info.addAfter;
  97. var position = addAfter ? target.info.indexInSlot + 1 : target.info.indexInSlot;
  98. var transactionToken = this.transaction.startTransaction();
  99. var options = {
  100. bReplace: addAfter || position < 0 ? false : true,
  101. position: position,
  102. canSetLocalFilters: this._isMetadataColumnArray(source) ? false : true, //TODO: Not sure about this. Looks like its for member DnD.
  103. activeDataSourceId: this.dashboardAPI.getActiveDataSourceId()
  104. };
  105. this.visMapColumnsToSlot.mapColumns(target.info.slot.getId(), droppedColumns, options, transactionToken);
  106. if (source.type === 'A11YMetadataColumns') {
  107. this._clearA11YData();
  108. }
  109. this.transaction.endTransaction(transactionToken);
  110. };
  111. //Can this slotDataItem accept these items from the metadata tree or shaping grid? (aka metadataColumns)
  112. VisDnDColumnToSlotProvider.prototype._acceptsMetadataColumns = function _acceptsMetadataColumns(source, target) {
  113. var sourceData = source.data;
  114. var metadataColumns = sourceData;
  115. var metadataColumnsWithMembers = {};
  116. if (sourceData && sourceData.columns) {
  117. metadataColumns = _.map(sourceData.columns, function (column) {
  118. return column.metadataColumn;
  119. });
  120. metadataColumnsWithMembers = sourceData.utils && sourceData.utils.getColumnsWithMembers();
  121. }
  122. if (!metadataColumns.length) {
  123. return false;
  124. }
  125. //You CAN'T drop metadataColumns if any columns don't match the sourceId of this widget.
  126. var currentSourceId = this.getDatasourceId();
  127. var isDifferentSourceColumn = function isDifferentSourceColumn(metadataColumn) {
  128. /* needed to handle both metadataColumnApi and metadataColumn */
  129. var columnSourceId = metadataColumn.getSourceId ? metadataColumn.getSourceId() : metadataColumn.sourceId;
  130. return currentSourceId && currentSourceId !== columnSourceId;
  131. };
  132. if (metadataColumns.some(isDifferentSourceColumn)) {
  133. return false;
  134. }
  135. //You CAN'T drop metadatacolumns that have certain taxonomy characteristics
  136. if (!this._itemsSupported(metadataColumns, target.info.slot)) {
  137. return false;
  138. }
  139. // //You CAN'T drop on top of the placeholder dataItem 'Measure Group (n)' from the metadata tree. You CAN drop after it.
  140. if (!target.info.addAfter && this.isMeasureGroupItem(target.info.slot, target.info.indexInSlot)) {
  141. return false;
  142. }
  143. //You CAN'T drop a filter
  144. if (metadataColumns.some(function (metadataColumn) {
  145. return metadataColumn.getObjectType() === 'Filter';
  146. })) {
  147. return false;
  148. }
  149. //Check what hierarchies (if any) the dropped columns belong to.
  150. //Don't allow drop if any are in a hierarchy that is already being used in the visualization.
  151. var oHierarchyMap = {};
  152. if (ShapingUIUtils.isInvalidDragObject(metadataColumns, oHierarchyMap, metadataColumnsWithMembers, false /* metadataColumnsExist */) || this._isRestrictedForMembersOfSameHierarchy(metadataColumns, metadataColumnsWithMembers, target.info.slot.getId()) || ShapingUIUtils.isInvalidDragObject(this._getUsedMetadataColumns(), oHierarchyMap, metadataColumnsWithMembers, true /* metadataColumnsExist */)) {
  153. return false;
  154. }
  155. //You CAN'T drop more items than the slot can allow.
  156. var countItems = this._getCountItemsInDropData(metadataColumnsWithMembers, target.info.slot);
  157. return this._acceptsCommon(source, target, countItems, target.info.addAfter);
  158. };
  159. VisDnDColumnToSlotProvider.prototype.getDatasourceId = function getDatasourceId() {
  160. var dataSource = this.visualization.getDataSource();
  161. if (dataSource) {
  162. return dataSource.getId();
  163. }
  164. return null;
  165. };
  166. VisDnDColumnToSlotProvider.prototype.isMeasureGroupItem = function isMeasureGroupItem(slot, indexInSlot) {
  167. if (SlotAPIHelper.isMultiMeasuresSeriesSlot(slot)) {
  168. var thisDataItem = slot.getDataItemList()[indexInSlot];
  169. return thisDataItem && thisDataItem.getColumnId() === '_multiMeasuresSeries';
  170. }
  171. return false;
  172. };
  173. // Restrict to drop members of same hierarchy or namedSet in different slots
  174. VisDnDColumnToSlotProvider.prototype._isRestrictedForMembersOfSameHierarchy = function _isRestrictedForMembersOfSameHierarchy(metadataColumns, metadataColumnsWithMembers, slotId) {
  175. if (_.isEmpty(metadataColumnsWithMembers)) {
  176. return false;
  177. }
  178. // Get items from dropped columns that are hierarchy or namedSet
  179. var droppedColumnsWithMembers = [];
  180. metadataColumns.forEach(function (column) {
  181. if (column.isHierarchy() || column.isNamedSet()) {
  182. droppedColumnsWithMembers.push(column.getId());
  183. }
  184. });
  185. if (!droppedColumnsWithMembers.length) {
  186. return false;
  187. }
  188. // Get all items from other slots that are hierarchy or namedSet
  189. var itemsFromOtherSlots = this._getColumnsFromHierarchyOrNamedSetOfOtherSlots(slotId);
  190. if (!itemsFromOtherSlots.length) {
  191. return false;
  192. }
  193. if (!_.intersection(droppedColumnsWithMembers, itemsFromOtherSlots).length) {
  194. return false;
  195. }
  196. return true;
  197. };
  198. VisDnDColumnToSlotProvider.prototype._getColumnsFromHierarchyOrNamedSetOfOtherSlots = function _getColumnsFromHierarchyOrNamedSetOfOtherSlots(targetSlotId) {
  199. var itemsFromOtherSlots = [];
  200. this.visualization.getSlots().getSlotList().forEach(function (slot) {
  201. if (slot.getDefinition().getId() !== targetSlotId) {
  202. slot.getDataItemList().forEach(function (dataItem) {
  203. var column = dataItem.getMetadataColumn && dataItem.getMetadataColumn();
  204. if (column && (column.isHierarchy() || column.isNamedSet())) {
  205. itemsFromOtherSlots.push(column.getId());
  206. }
  207. });
  208. }
  209. });
  210. return itemsFromOtherSlots;
  211. };
  212. /**
  213. * return a list of metadataColumns all the projected dataItems except MultiMeasure item.
  214. */
  215. VisDnDColumnToSlotProvider.prototype._getUsedMetadataColumns = function _getUsedMetadataColumns() {
  216. var dataItemList = this.visualization.getSlots().getDataItemList();
  217. return dataItemList.filter(function (dataItem) {
  218. return dataItem.getColumnId() !== MULTI_MEASURES_SERIES;
  219. }).map(function (dataItem) {
  220. return dataItem.getMetadataColumn();
  221. });
  222. };
  223. VisDnDColumnToSlotProvider.prototype._getMappedItemsOfTargetSlot = function _getMappedItemsOfTargetSlot(slot) {
  224. var mappedItems = [];
  225. slot.getDataItemList().forEach(function (dataItem) {
  226. if (dataItem.getMetadataColumn) {
  227. mappedItems.push(dataItem.getMetadataColumn().getId());
  228. }
  229. });
  230. return mappedItems;
  231. };
  232. VisDnDColumnToSlotProvider.prototype._itemsSupported = function _itemsSupported(metadataColumns, slot) {
  233. //convert to new metadataColumn api
  234. var sourceId = metadataColumns[0].getSourceId();
  235. var dataSource = sourceId && this.datasources.getDataSource(sourceId);
  236. if (dataSource) {
  237. var newMetadataColumnAPIs = metadataColumns.map(function (metadataColumn) {
  238. return dataSource.getMetadataColumn(metadataColumn.getId());
  239. });
  240. return slot.supportsColumns(newMetadataColumnAPIs);
  241. }
  242. return false;
  243. };
  244. // We count only columns that are not already in the slot
  245. VisDnDColumnToSlotProvider.prototype._getCountItemsInDropData = function _getCountItemsInDropData(metadataColumnsWithMembers, slot) {
  246. var _ref;
  247. if (!metadataColumnsWithMembers) {
  248. return 1;
  249. }
  250. var memberParentIds = Object.keys(metadataColumnsWithMembers);
  251. var members = Object.values(metadataColumnsWithMembers);
  252. var flattenedMembers = (_ref = []).concat.apply(_ref, members);
  253. if (!flattenedMembers.length) {
  254. // dropped columns are not members, count all
  255. return memberParentIds.length;
  256. }
  257. var mappedItemIds = this._getMappedItemsOfTargetSlot(slot);
  258. // do not count items that already exist in the slot
  259. var diffs = memberParentIds.filter(function (id) {
  260. return mappedItemIds.indexOf(id) === -1;
  261. });
  262. return diffs.length;
  263. };
  264. VisDnDColumnToSlotProvider.prototype._acceptsCommon = function _acceptsCommon(source, target, countOfItemsToDrop, addAfter) {
  265. //IF this definition contains (or could contain) a multiMeasuresSeries item,
  266. //You CAN drop on or after in a values slot (even though values has maxItems of 1 => multi-items go where 'Measure Group (n)' is)
  267. if (target.info.slot.getId() === 'values' && SlotAPIHelper.isMultiMeasuresSeriesSlot(target.info.slot) || target.info.slot.getDefinition().isMultiMeasureSupported()) {
  268. return true;
  269. }
  270. //{ targetSlotId, sourceSlotId, indexInSourceSlot, sourceColumns, indexInTargetSlot, isReplace }
  271. var params = {
  272. targetSlotId: target.info.slot.getId(),
  273. sourceSlotId: undefined,
  274. indexInSourceSlot: undefined,
  275. sourceColumns: _.map(source.data.columns, function (column) {
  276. return column.metadataColumn;
  277. }),
  278. indexInTargetSlot: target.info.addAfter ? target.info.indexInSlot + 1 : target.info.indexInSlot,
  279. isReplace: !target.info.addAfter
  280. };
  281. var expandCollapseFeatureFlag = !this.dashboardAPI.getGlassCoreSvc('.FeatureChecker').checkValue('dashboard', 'expandCollapse', 'disabled');
  282. if (expandCollapseFeatureFlag && this.visualization.getType() === 'Crosstab') {
  283. if (!this.visDnDUtils.acceptsOlapV2(params)) {
  284. return false;
  285. }
  286. }
  287. //You CAN'T drop more items than the slot can allow.
  288. return !this.visDnDUtils.exceedsItemsLimit(countOfItemsToDrop, target.info.indexInSlot, addAfter, target.info.slot);
  289. };
  290. return VisDnDColumnToSlotProvider;
  291. }();
  292. });
  293. //# sourceMappingURL=VisDnDColumnToSlotProvider.js.map