123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454 |
- 'use strict';
- /**
- * Licensed Materials - Property of IBM
- * IBM Cognos Products: BI Cloud (C) Copyright IBM Corp. 2013, 2020
- * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
- */
- define(['jquery', 'underscore', './LayoutBaseView', '../../../../lib/@waca/dashboard-common/dist/ui/interaction/Utils', '../../../../app/nls/StringResources', '../../../../lib/@waca/core-client/js/core-client/utils/Deferred', '../../LayoutHelper', '../../../glass/util/InstrumentationUtil'], function ($, _, BaseLayout, utils, stringResources, Deferred, LayoutHelper, InstrumentationUtil) {
- var PageLayout = null;
- PageLayout = BaseLayout.extend({
- init: function init(options) {
- PageLayout.inherited('init', this, arguments);
- this.services = options.services;
- this.specializeConsumeView(['setPreferredLocation', 'isLayoutRelatedToDropZone', 'addRelatedModel', 'removeRelatedModel']);
- this.$el.parent().addClass('templateDropZoneContainer');
- this.updateRelatedContentState();
- this.whenIsReadyDfd = new Deferred();
- this.initializeDropZones();
- },
- initializeDropZones: function initializeDropZones() {
- this._dndManager = this.dashboardApi.getFeature('DashboardDnd.internal');
- if (!this.centerDrop) {
- this.centerDrop = $('<div class="centerDrop"><div class="dropIcon"><svg class="svgIcon" role="img" focusable="false"><use style="pointer-events: none; " xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#dashboard-fullscreen"></use></svg></div><div class="dropLabel">' + stringResources.get('dropZoneLabel') + '</div></div>');
- this.centerDrop.hide();
- this.$el.append(this.centerDrop);
- }
- var $centerDropZone = this.centerDrop.find('.dropIcon');
- this._dndManager.addDropTarget($centerDropZone[0], {
- accepts: this.accepts.bind(this),
- onDrop: this.onCentreDrop.bind(this),
- onDragEnter: this.onCentreDragEnter.bind(this),
- onDragLeave: this.onCentreDragLeave.bind(this),
- priority: -100 /* Indicate that any other drop zone takes priority */
- });
- // Set the layout node as a drop zone. This drop zone will not accept anything
- // but it is configured to receive the enter/leave/move events. This is used to show the maximize square target
- this._dndManager.addDropTarget(this.domNode, {
- accepts: function accepts() {
- return false;
- },
- onDragEnter: this.onDragZoneEnter.bind(this),
- onDragLeave: this.onDragZoneLeave.bind(this),
- onDrop: this.onDropZoneDrop.bind(this),
- receiveEventsWhenNotAccepting: true,
- /* absolute pages are -100 priority.
- * We set the drop zone priority to be higher (i.e. -99)
- * so that it takes priority in case the drop zone is the same size as the parent absolute page
- * We also need to set it to negative to indicate that any other drop zone takes priority */
- priority: -99
- });
- this._setupGlassDroppables($centerDropZone);
- this.whenIsReadyDfd.resolve();
- },
- whenIsReady: function whenIsReady() {
- return this.whenIsReadyDfd.promise;
- },
- _setupGlassDroppables: function _setupGlassDroppables($centerDropZone) {
- if (this.$el.glassDroppableV2) {
- //we are in the glass, accept pin drops
- var thisObj = this;
- //center zone accepts drops (maximizes to area in template)
- $centerDropZone.glassDroppableV2({
- onDrop: this._onPinDrop.bind(this),
- onEnter: this.onGlassCentreDragEnter.bind(this),
- onLeave: this.onCentreDragLeave.bind(this),
- allowOnDropPropagation: false
- });
- //this.$el tracks onEnter, onLeave so that it can show the center drop zone
- this.$el.glassDroppableV2({
- onEnter: function onEnter() {
- if (this.id === thisObj.$el[0].id) {
- thisObj.onDragZoneEnter();
- }
- },
- onLeave: function onLeave() {
- if (this.id === thisObj.$el[0].id) {
- thisObj.onDragZoneLeave();
- }
- },
- onDrop: function onDrop() {
- thisObj.onDropZoneDrop();
- return true;
- },
- allowOnDropPropagation: true
- });
- }
- },
- _showCenterDrop: function _showCenterDrop(data, isMovingOneWidget) {
- var excludeId = null;
- if (isMovingOneWidget) {
- var layout = data.nodeInfoList[0].node._layout;
- if (layout) {
- excludeId = layout.model.id;
- }
- }
- // We will not show the center drop if we already have a maximized widget
- // But if we are moving the widget that is already maximized, then we display the center.
- if (!this.hasMaximizedWidget() || excludeId && this.isWidgetMaximized(excludeId)) {
- this.centerDrop.show();
- }
- },
- onDragZoneEnter: function onDragZoneEnter(dragObject) {
- this.inDropZone = true;
- var data = dragObject && dragObject.data || {};
- var isNewDrop = !data.nodeInfoList;
- var isMovingOneWidget = data.nodeInfoList && data.nodeInfoList.length === 1;
- // Show the center drop when we are dropping a new widget or moving one widget.
- if (isNewDrop || isMovingOneWidget) {
- this._showCenterDrop(data, isMovingOneWidget);
- var dropTarget = this._dndManager.getDropTargetFromNode(this.centerDrop.find('.dropIcon')[0]);
- this._dndManager.reassessDropTarget(dropTarget);
- }
- },
- onDragZoneLeave: function onDragZoneLeave() {
- if (!this.centerDrop.hasClass('active')) {
- this.centerDrop.hide();
- }
- this.inDropZone = false;
- },
- onDropZoneDrop: function onDropZoneDrop() {
- this.deactivateAndhideCenterDropZone();
- this.inDropZone = false;
- },
- deactivateAndhideCenterDropZone: function deactivateAndhideCenterDropZone() {
- if (this.centerDrop) {
- this.centerDrop.removeClass('active');
- this.centerDrop.hide();
- }
- },
- destroy: function destroy() {
- if (this._dndManager) {
- this._dndManager.removeDropTarget(this.centerDrop.find('.dropIcon')[0]);
- this._dndManager.removeDropTarget(this.domNode);
- this._dndManager = null;
- }
- this.centerDrop.remove();
- this.centerDrop = null;
- if (this.$el.parent().find('.pagetemplateDropZone') <= 1) {
- this.$el.parent().removeClass('templateDropZoneContainer');
- }
- if ($.glassdnd && $.glassdnd.cancelDroppable) {
- $.glassdnd.cancelDroppable(this.$el);
- }
- PageLayout.inherited('destroy', this, arguments);
- },
- /**
- * Called by the DnD manager to check if this drop zone accept the dragged object
- * We only accept object with type widget and pin
- * @returns {Boolean}
- */
- accepts: function accepts(dragObject) {
- var canvasDnD = this.dashboardApi.getFeature('CanvasDnD');
- return canvasDnD.accepts(dragObject, {
- fromTemplate: true
- });
- },
- /**
- * Called by the DnD manager when we enter and move inside the centre of drop zone (only if the drop zone accepts the object)
- *
- */
- onCentreDragEnter: function onCentreDragEnter() {
- this.centerDrop.addClass('active');
- },
- /**
- * Called by the DnD manager when we enter and move inside the centre of drop zone (only if the drop zone accepts the object)
- *
- */
- onGlassCentreDragEnter: function onGlassCentreDragEnter() {
- this.centerDrop.show();
- this.centerDrop.addClass('active');
- },
- /**
- * Called by the drag&drop manager when we leave the centre drop zone
- *
- */
- onCentreDragLeave: function onCentreDragLeave() {
- this.centerDrop.removeClass('active');
- if (!this.inDropZone) {
- this.centerDrop.hide();
- }
- },
- /**
- * Called by the drag&drop manager when a drop happens at the centre
- *
- * @param dragObject
- * @param targetNode
- */
- onCentreDrop: function onCentreDrop(dragObject) {
- var _this = this;
- var promise = Promise.resolve();
- if (dragObject.data.operation === 'move') {
- promise = Promise.resolve(this._moveDrop(dragObject));
- } else if (dragObject.type === 'pin' && dragObject.data.operation === 'new') {
- promise = Promise.resolve(this._onPinDrop(dragObject));
- } else if (dragObject.data.operation === 'new' || dragObject.type === 'MODEL_ITEM' || dragObject.type === 'GRID_HEADER_ITEM') {
- promise = this._newDrop(dragObject);
- }
- return promise.then(function () {
- return _this.deactivateAndhideCenterDropZone();
- });
- },
- _moveDrop: function _moveDrop(dragObject) {
- var updateArray = [];
- var nodeInfo, nodeModel, options;
- for (var i = 0; i < dragObject.data.nodeInfoList.length; i++) {
- nodeInfo = dragObject.data.nodeInfoList[i];
- nodeModel = nodeInfo.node._layout.model;
- options = {
- style: {},
- parentId: this.model.getParent().id,
- id: nodeModel.id
- };
- this.setPreferredLayoutProperties(options);
- options.insertBefore = this.getWidgetIdForInsertBefore();
- updateArray.push(options);
- }
- var transactionApi = this.dashboardApi.getFeature('Transaction');
- var transactionToken = transactionApi.startTransaction();
- var nodeIds = updateArray.map(function (update) {
- return update.id;
- });
- var validate = false; // for move drop, no need for validation in layoutPropertiesProvider
- this.dashboardApi.getCanvas().moveContent(this.model.getParent().id, nodeIds, transactionToken);
- this.updateModel(updateArray, transactionToken, validate);
- transactionApi.endTransaction(transactionToken);
- },
- _newDrop: function _newDrop(dragObject) {
- var _this2 = this;
- return this._getModelToAddFromDragObject(dragObject).then(function (newModel) {
- if (newModel) {
- var widgetSpec = {
- model: newModel,
- parentId: _this2.id,
- layoutProperties: dragObject.data.layoutProperties || {}
- };
- InstrumentationUtil.trackWidget('created', _this2.dashboardApi, widgetSpec.model);
- _this2.setPreferredLocation(widgetSpec);
- _this2._addWidget(widgetSpec, dragObject.isTouch);
- }
- });
- },
- /**
- * a helper function that handles pin dropping
- *
- * @private
- *
- * @param {object} dragObject - The object to be dropped
- */
- _onPinDrop: function _onPinDrop(dragObject) {
- var pinSpec = dragObject.data.pinSpec;
- var isTouch = dragObject.isTouch;
- //gemini widget
- if (pinSpec.contentType === 'boardFragment') {
- this.setPreferredLayoutProperties(pinSpec.content.layout);
- }
- pinSpec.parentId = this.id;
- this.setMaximizedStateParentLocation(pinSpec);
- this._processWidgetSpecForPin(pinSpec, isTouch);
- this.deactivateAndhideCenterDropZone();
- },
- setPreferredLocation: function setPreferredLocation(options) {
- this.setPreferredLayoutProperties(options.layoutProperties);
- this.setMaximizedStateParentLocation(options);
- },
- setMaximizedStateParentLocation: function setMaximizedStateParentLocation(options) {
- options.parentId = this.model.getParent().id;
- options.insertBefore = this.getWidgetIdForInsertBefore();
- },
- setPreferredLayoutProperties: function setPreferredLayoutProperties(props) {
- if (!props.style) {
- props.style = {};
- }
- if (this.$el.is(':visible')) {
- //for selected tab/scene
- var contentOffset = this.$el.offset();
- var parentOffset = this.$el.parent().offset();
- props.style.top = Math.round(contentOffset.top - parentOffset.top);
- props.style.left = Math.round(contentOffset.left - parentOffset.left);
- props.style.height = this.$el.outerHeight();
- props.style.width = this.$el.outerWidth();
- this.moveToFitBoundaries(this.$el.parent().height(), this.$el.parent().width(), props.style);
- LayoutHelper.styleIntToPx(props.style);
- } else {
- //in the case of tab/scene hidden (for moving widgets from one tab/scene to another)
- var templateDropZone = this.$el[0];
- var style = this._calculateStylePercentage(templateDropZone.style);
- props.style = style;
- }
- },
- /**
- * Fix the old behavor where we always insert in a drop zone and place the object behind all existing objects.
- * The new logic will find the first object that is smaller than the dropzone (with a small amount of forgiveness) and insert before it.
- */
- getWidgetIdForInsertBefore: function getWidgetIdForInsertBefore() {
- var zoneHeight = this.$el.height();
- var zoneWidth = this.$el.width();
- var widgets = this.$el.siblings(':not(.pagetemplateDropZone)');
- var widgetNode = _.find(widgets, function (node) {
- if (node._layout) {
- var $node = $(node);
- return $node.outerHeight() < zoneHeight - 5 || $node.outerWidth() < zoneWidth - 5;
- }
- return false;
- });
- return widgetNode ? widgetNode._layout.model.id : null;
- },
- isLayoutRelatedToDropZone: function isLayoutRelatedToDropZone(node) {
- var props = {};
- this.setPreferredLayoutProperties(props);
- var isRelated = false;
- var topIsNear, leftIsNear, heightIsNear, widthIsNear;
- if (this.$el.is(':visible')) {
- //for selected tab/scene
- var position = utils.position(node);
- var size = utils.widgetSize(node);
- topIsNear = this.isNear(Math.round(position.top), props.style.top, 5);
- leftIsNear = this.isNear(Math.round(position.left), props.style.left, 5);
- heightIsNear = this.isNear(size.height, props.style.height, 10);
- widthIsNear = this.isNear(size.width, props.style.width, 10);
- } else {
- //in the case of tab/scene hidden (for moving widgets from one tab/scene to another)
- topIsNear = this.isNear(node.style.top, props.style.top, 10);
- leftIsNear = this.isNear(node.style.left, props.style.left, 10);
- heightIsNear = this.isNear(node.style.height, props.style.height, 10);
- widthIsNear = this.isNear(node.style.width, props.style.width, 10);
- }
- isRelated = topIsNear && leftIsNear && heightIsNear && widthIsNear;
- return isRelated;
- },
- /**
- * set a model as a related model to this drop zone. Related models are usually the ones snapped to this drop zone
- */
- addRelatedModel: function addRelatedModel(id, payloadData) {
- var relatedWidgets = this.model.relatedLayouts || '|';
- if (relatedWidgets.indexOf('|' + id + '|') === -1) {
- relatedWidgets += id + '|';
- }
- this.model.set({
- relatedLayouts: relatedWidgets
- }, {
- payloadData: payloadData.data
- });
- this.updateRelatedContentState();
- },
- hasMaximizedWidget: function hasMaximizedWidget() {
- return !!this.model.relatedLayouts;
- },
- isWidgetMaximized: function isWidgetMaximized(id) {
- var relatedWidgets = this.model.relatedLayouts || '|';
- return relatedWidgets.indexOf('|' + id + '|') !== -1;
- },
- /**
- * Remove a model as a related model to this drop zone. Related models are usually the ones snapped to this drop zone
- */
- removeRelatedModel: function removeRelatedModel(id, payloadData) {
- var relatedWidgets = this.model.relatedLayouts;
- if (relatedWidgets && relatedWidgets.indexOf('|' + id + '|') !== -1) {
- relatedWidgets = relatedWidgets.replace('|' + id + '|', '|');
- if (relatedWidgets === '|') {
- relatedWidgets = '';
- }
- this.model.set({
- relatedLayouts: relatedWidgets
- }, {
- payloadData: payloadData.data
- });
- }
- this.updateRelatedContentState();
- },
- /**
- * Add a css class to indicate of this drop zone has related layouts. This will be used to find an empty spot when adding widgets to the page
- */
- updateRelatedContentState: function updateRelatedContentState() {
- var relatedWidgets = this.model.relatedLayouts;
- if (!relatedWidgets) {
- this.$el.addClass('empty');
- } else {
- this.$el.removeClass('empty');
- }
- },
- isNear: function isNear(i, s, variance) {
- return Math.abs(parseInt(i, 10) - parseInt(s, 10)) <= variance;
- },
- _calculateStylePercentage: function _calculateStylePercentage(style) {
- var newStyle = {};
- newStyle.top = style.top;
- newStyle.left = style.left;
- newStyle.width = 100 - parseInt(style.right, 10) - parseInt(style.left, 10) + '%';
- newStyle.height = 100 - parseInt(style.bottom, 10) - parseInt(style.top, 10) + '%';
- return newStyle;
- }
- });
- return PageLayout;
- });
- //# sourceMappingURL=TemplateDropZone.js.map
|