GUI Validate drag-n-drop actions
authorColeman Watts <coleman@civicrm.org>
Sun, 17 Nov 2019 15:19:12 +0000 (10:19 -0500)
committerCiviCRM <info@civicrm.org>
Wed, 16 Sep 2020 02:13:20 +0000 (19:13 -0700)
Prevent fields from being moved outside their entity fieldset(s)
Prevent fieldsets from being dragged into other fieldsets

ext/afform/gui/ang/afGuiEditor.js
ext/afform/gui/ang/afGuiEditor/block.html

index 61eaabac63908f86e67441918e1a6250a7312f7e..b6fb02bd7b9c92328298cf96a480bfb875271a67 100644 (file)
           }
         };
 
+        // Validates that a drag-n-drop action is allowed
+        $scope.onDrop = function(event, ui) {
+          var sort = ui.item.sortable;
+          // Check if this is a callback for an item dropped into a different container
+          // @see https://github.com/angular-ui/ui-sortable notes on canceling
+          if (!sort.received && sort.source[0] !== sort.droptarget[0]) {
+            var $source = $(sort.source[0]),
+              $target = $(sort.droptarget[0]),
+              $item = $(ui.item[0]);
+            // Fields cannot be dropped outside their own entity
+            if ($item.is('[af-gui-field]') || $item.has('[af-gui-field]').length) {
+              if ($source.closest('[data-entity]').attr('data-entity') !== $target.closest('[data-entity]').attr('data-entity')) {
+                return sort.cancel();
+              }
+            }
+            // Entity-fieldsets cannot be dropped into other entity-fieldsets
+            if (($item.is('[data-entity]') || $item.has('[data-entity]').length) && $target.closest('[data-entity]').length) {
+              return sort.cancel();
+            }
+          }
+        };
+
         $scope.tags = {
           div: ts('Block'),
           fieldset: ts('Fieldset')
index d8c862e92ab66c8aeefe68ab69a58b5f7ba95002..72d916f761df1def049b7b06fbbfc2f92c3482e1 100644 (file)
@@ -38,8 +38,8 @@
     </ul>
   </div>
 </div>
-<div ui-sortable="{handle: '.af-gui-bar', connectWith: '[ui-sortable]', cancel: 'input,textarea,button,select,option,a'}" ng-model="node['#children']" class="af-gui-layout {{ getLayout() }}">
-  <div ng-repeat="item in node['#children']" ng-show="block.getNodeType(item)">
+<div ui-sortable="{handle: '.af-gui-bar', update: onDrop, connectWith: '[ui-sortable]', cancel: 'input,textarea,button,select,option,a'}" ng-model="node['#children']" class="af-gui-layout {{ getLayout() }}">
+  <div ng-repeat="item in node['#children']" >
     <div ng-switch="block.getNodeType(item)">
       <div ng-switch-when="fieldset" af-gui-block="item" class="af-gui-block af-gui-fieldset af-gui-block-type-{{ item['#tag'] }}" ng-class="{'af-entity-selected': isSelectedFieldset(item['af-fieldset'])}" entity-name="item['af-fieldset']" data-entity="{{ item['af-fieldset'] }}" />
       <div ng-switch-when="block" af-gui-block="item" class="af-gui-block af-gui-block-type-{{ item['#tag'] }}" entity-name="entityName" />