GUI: Refactor entity panel to directive & add field search
authorColeman Watts <coleman@civicrm.org>
Fri, 8 Nov 2019 15:03:28 +0000 (10:03 -0500)
committerCiviCRM <info@civicrm.org>
Wed, 16 Sep 2020 02:13:20 +0000 (19:13 -0700)
ext/afform/gui/ang/afGuiEditor.css
ext/afform/gui/ang/afGuiEditor.js
ext/afform/gui/ang/afGuiEditor/entity.html [moved from ext/afform/gui/ang/afGuiEditor/config-entity.html with 52% similarity]
ext/afform/gui/ang/afGuiEditor/palette.html

index 2665fecc586e38c5a5a4b3a1f3994ef2afc99945..c1ceee8f184f24020530950f906d494b52309daa 100644 (file)
@@ -45,7 +45,7 @@
   display: none;
 }
 
-#afGuiEditor-palette-config .form-inline {
+#afGuiEditor-palette-config .af-gui-entity-values .form-inline {
   margin-bottom: 10px;
 }
 
   min-width: 110px;
 }
 
+#afGuiEditor input[type=search]::placeholder {
+  font-family: FontAwesome;
+  text-align: right;
+}
+#afGuiEditor input[type=search]:-ms-input-placeholder {
+  font-family: FontAwesome;
+  text-align: right;
+}
+#afGuiEditor input[type=search]::-ms-input-placeholder {
+  font-family: FontAwesome;
+  text-align: right;
+}
+
 #afGuiEditor .af-gui-bar {
   cursor: move;
   background-color: #efefef;
@@ -69,6 +82,9 @@
   opacity: 1;
   transition: opacity .2s;
 }
+#afGuiEditor-canvas .panel-body > div > .af-gui-bar {
+  top: -5px;
+}
 
 #afGuiEditor .af-gui-bar .btn.active {
   background-color: #b3b3b3;
   color: white;
 }
 
+#afGuiEditor .af-gui-entity-fields legend {
+  margin-bottom: 0 !important;
+}
+
 #afGuiEditor [ui-sortable] {
   min-height: 25px;
 }
index 835e6d07a18f88fd425d39a7b61ad1d4e36aa414..95b7b26b8f80eb0b4bb40ef2c61f1e65a4049d8e 100644 (file)
         });
       },
       controller: function($scope) {
-        $scope.ts = CRM.ts();
+        var ts = $scope.ts = CRM.ts();
         $scope.afform = null;
         $scope.saving = false;
         $scope.selectedEntity = null;
-        $scope.meta = CRM.afformAdminData;
-        $scope.controls = {};
-        $scope.fieldList = {};
+        $scope.meta = this.meta = CRM.afformAdminData;
+        this.scope = $scope;
         var editor = $scope.editor = this;
         var newForm = {
           title: ts('Untitled Form'),
@@ -74,7 +73,6 @@
           $scope.layout = getTags($scope.afform.layout, 'af-form')[0];
           evaluate($scope.layout['#children']);
           $scope.entities = getTags($scope.layout['#children'], 'af-entity', 'name');
-          _.each(_.keys($scope.entities), buildFieldList);
 
           // Set changesSaved to true on initial load, false thereafter whenever changes are made to the model
           $scope.$watch('afform', function () {
               }
             ]
           });
-          buildFieldList(entityType + num);
           return entityType + num;
         };
 
           editor.selectEntity(entityName);
         };
 
-        $scope.rebuildFieldList = function() {
-          $timeout(function() {
-            $scope.$apply(function() {
-              buildFieldList($scope.selectedEntity);
-            });
-          });
-        };
-
-        function buildFieldList(entityName) {
-          $scope.fieldList[entityName] = $scope.fieldList[entityName] || [];
-          $scope.fieldList[entityName].length = 0;
-          _.each($scope.meta.fields[$scope.entities[entityName].type], function(field) {
-            $scope.fieldList[entityName].push({
-              "#tag": "af-field",
-              name: field.name,
-              defn: {}
-            });
-          });
-        }
-
-        $scope.valuesFields = function() {
-          var fields = _.transform($scope.meta.fields[$scope.entities[$scope.selectedEntity].type], function(fields, field) {
-            fields.push({id: field.name, text: field.title, disabled: $scope.fieldInUse($scope.selectedEntity, field.name)});
-          }, []);
-          return {results: fields};
-        };
-
-        $scope.removeValue = function(entity, fieldName) {
-          delete entity.data[fieldName];
-        };
-
         $scope.save = function() {
           $scope.saving = true;
           CRM.api4('Afform', 'save', {records: [JSON.parse(angular.toJson($scope.afform))]})
             });
         };
 
-        $scope.$watch('controls.addValue', function(fieldName) {
-          if (fieldName) {
-            if (!$scope.entities[$scope.selectedEntity].data) {
-              $scope.entities[$scope.selectedEntity].data = {};
-            }
-            $scope.entities[$scope.selectedEntity].data[fieldName] = '';
-            $scope.controls.addValue = '';
-          }
-        });
-
-        // Checks if a field is on the form or set as a value
-        $scope.fieldInUse = function(entityName, fieldName) {
-          var data = $scope.entities[entityName].data || {},
-            found = false;
-          if (fieldName in data) {
-            return true;
-          }
-          return check($scope.layout['#children']);
-          function check(group) {
-            _.each(group, function(item) {
-              if (found) {
-                return false;
-              }
-              if (_.isPlainObject(item)) {
-                if ((!item['af-fieldset'] || (item['af-fieldset'] === entityName)) && item['#children']) {
-                  check(item['#children']);
-                }
-                if (item['#tag'] === 'af-field' && item.name === fieldName) {
-                  found = true;
-                }
-              }
-            });
-            return found;
-          }
-        };
-
         // Parse strings of javascript that php couldn't interpret
         function evaluate(collection) {
           _.each(collection, function(item) {
           });
         }
 
-        function expandFields(collection, entityType) {
-          _.each(collection, function (item) {
-            if (_.isPlainObject(item)) {
-              if (item['af-fieldset']) {
-                expandFields(item['#children'], editor.getEntity(item['af-fieldset']).type);
-              }
-              else if (item['#tag'] === 'af-field') {
-                item.defn = item.defn || {};
-                _.defaults(item.defn, _.cloneDeep(_.pick(editor.getField(entityType, item.name), ['title', 'input_type', 'input_attrs'])));
-              } else {
-                expandFields(item['#children'], entityType);
-              }
-            }
-          });
-        }
-
       }
     };
   });
     });
   }
 
+  angular.module('afGuiEditor').directive('afGuiEntity', function($timeout) {
+    return {
+      restrict: 'A',
+      templateUrl: '~/afGuiEditor/entity.html',
+      scope: {
+        entity: '=afGuiEntity',
+      },
+      require: '^^afGuiEditor',
+      link: function ($scope, element, attrs, editor) {
+        $scope.editor = editor;
+      },
+      controller: function ($scope) {
+        var ts = $scope.ts = CRM.ts();
+        $scope.controls = {};
+        $scope.fieldList = [];
+
+        $scope.valuesFields = function() {
+          var fields = $scope.editor ? _.transform($scope.editor.meta.fields[$scope.entity.type], function(fields, field) {
+            fields.push({id: field.name, text: field.title, disabled: $scope.fieldInUse(field.name)});
+          }, []) : [];
+          return {results: fields};
+        };
+
+        $scope.removeValue = function(entity, fieldName) {
+          delete entity.data[fieldName];
+        };
+
+        $scope.buildFieldList = function() {
+          $scope.fieldList.length = 0;
+          var search = $scope.controls.fieldSearch ? $scope.controls.fieldSearch.toLowerCase() : null;
+          _.each($scope.editor.meta.fields[$scope.entity.type], function(field) {
+            if (!search || _.contains(field.name, search) || _.contains(field.title.toLowerCase(), search)) {
+              $scope.fieldList.push({
+                "#tag": "af-field",
+                name: field.name,
+                defn: {}
+              });
+            }
+          });
+        };
+
+        $scope.clearSearch = function() {
+          $scope.controls.fieldSearch = '';
+        };
+
+        $scope.rebuildFieldList = function() {
+          $timeout(function() {
+            $scope.$apply(function() {
+              $scope.buildFieldList();
+            });
+          });
+        };
+
+        // Checks if a field is on the form or set as a value
+        $scope.fieldInUse = function(fieldName) {
+          var data = $scope.entity.data || {},
+            found = false;
+          if (fieldName in data) {
+            return true;
+          }
+          return check($scope.editor.scope.layout['#children']);
+          function check(group) {
+            _.each(group, function(item) {
+              if (found) {
+                return false;
+              }
+              if (_.isPlainObject(item)) {
+                if ((!item['af-fieldset'] || (item['af-fieldset'] === $scope.entity.name)) && item['#children']) {
+                  check(item['#children']);
+                }
+                if (item['#tag'] === 'af-field' && item.name === fieldName) {
+                  found = true;
+                }
+              }
+            });
+            return found;
+          }
+        };
+
+        $scope.$watch('controls.addValue', function(fieldName) {
+          if (fieldName) {
+            if (!$scope.entity.data) {
+              $scope.entity.data = {};
+            }
+            $scope.entity.data[fieldName] = '';
+            $scope.controls.addValue = '';
+          }
+        });
+
+        $scope.$watch('controls.fieldSearch', $scope.buildFieldList);
+      }
+    };
+  });
+
   angular.module('afGuiEditor').directive('afGuiBlock', function() {
     return {
       restrict: 'A',
       require: '^^afGuiEditor',
       link: function($scope, element, attrs, editor) {
         $scope.editor = editor;
-        $scope.ts = CRM.ts();
       },
       controller: function($scope) {
         $scope.block = this;
+        var ts = $scope.ts = CRM.ts();
         this.node = $scope.node;
 
         this.modifyClasses = function(item, toRemove, toAdd) {
         $scope.block = block;
       },
       controller: function($scope) {
+        var ts = $scope.ts = CRM.ts();
+
         $scope.tags = {
           p: ts('Normal Text'),
           legend: ts('Fieldset Legend'),
         $scope.block = block;
       },
       controller: function($scope) {
+        var ts = $scope.ts = CRM.ts();
 
         // TODO: Add action selector to UI
         // $scope.actions = {
similarity index 52%
rename from ext/afform/gui/ang/afGuiEditor/config-entity.html
rename to ext/afform/gui/ang/afGuiEditor/entity.html
index 25bc6ab1728f558b4951f243cd31891af553c444..558110b11b789907a4858519552bacb500c88551 100644 (file)
@@ -1,9 +1,9 @@
 <div>
-  <a href ng-click="editor.removeEntity(selectedEntity)" class="btn btn-sm btn-danger-outline pull-right">
+  <a href ng-click="editor.removeEntity(entity.name)" class="btn btn-sm btn-danger-outline pull-right">
     <i class="crm-i fa-trash"></i>
   </a>
 
-  <fieldset>
+  <fieldset class="af-gui-entity-values">
     <legend>{{ ts('Values') }}</legend>
     <div class="form-inline" ng-if="entity.data" ng-repeat="(fieldName, value) in entity.data">
       <label>{{ editor.getField(entity.type, fieldName).title }}:</label>
     </div>
   </fieldset>
 
-  <fieldset>
+  <fieldset class="af-gui-entity-fields">
     <legend>{{ ts('Fields') }}</legend>
-    <div class="af-gui-field-select-list" ui-sortable="{update: rebuildFieldList, items: '> div:not(.disabled)', connectWith: '[data-entity=' + selectedEntity + '] [ui-sortable]'}" ng-model="fieldList[selectedEntity]">
-      <div ng-repeat="field in fieldList[selectedEntity]" ng-class="{disabled: fieldInUse(selectedEntity, field.name)}">
-        {{ editor.getField(editor.getEntity(selectedEntity).type, field.name).title }}
+    <div class="af-gui-field-search form-inline">
+      <input ng-model="controls.fieldSearch" class="form-control" type="search" placeholder="&#xf002" title="{{ ts('Search fields') }}" />
+      <a href ng-click="clearSearch()" ng-show="!!controls.fieldSearch" title="{{ ts('Clear search') }}">
+        <i class="crm-i fa-times"></i>
+      </a>
+    </div>
+    <div class="af-gui-field-select-list" ui-sortable="{update: rebuildFieldList, items: '&gt; div:not(.disabled)', connectWith: '[data-entity=' + entity.name + '] [ui-sortable]'}" ng-model="fieldList">
+      <div ng-repeat="field in fieldList" ng-class="{disabled: fieldInUse(field.name)}">
+        {{ editor.getField(entity.type, field.name).title }}
       </div>
     </div>
   </fieldset>
index 3e3517aa20d5b1244f42e471fe75c1999e0e25f1..9249d3fa2f4c7692ff20f1afdd1c9e7859e5d03b 100644 (file)
@@ -22,5 +22,5 @@
       </li>
     </ul>
   <div class="panel-body" ng-include="'~/afGuiEditor/config-form.html'" ng-if="selectedEntity === null"></div>
-  <div class="panel-body" ng-include="'~/afGuiEditor/config-entity.html'" ng-repeat="entity in entities" ng-if="selectedEntity === entity.name"></div>
+  <div class="panel-body" af-gui-entity="entity" ng-repeat="entity in entities" ng-if="selectedEntity === entity.name"></div>
 </div>