Config forms - values section
authorColeman Watts <coleman@civicrm.org>
Mon, 28 Oct 2019 01:52:14 +0000 (21:52 -0400)
committerCiviCRM <info@civicrm.org>
Wed, 16 Sep 2020 02:13:19 +0000 (19:13 -0700)
ext/afform/gui/afform_gui.php
ext/afform/gui/ang/afGuiEditor.css
ext/afform/gui/ang/afGuiEditor.js
ext/afform/gui/ang/afGuiEditor/config-entity.html
ext/afform/gui/ang/afGuiEditor/config-form.html
ext/afform/gui/ang/afGuiEditor/main.html
ext/afform/gui/ang/afGuiEditor/palette.html

index 72caa9cf6464b371f630019382c6f90eaa4f4176..f72cb7f014cb9c0ce35ac8769c6c0592c85988b6 100644 (file)
@@ -182,16 +182,21 @@ function afform_gui_civicrm_buildAsset($asset, $params, &$mimeType, &$content) {
     $entityApi->addWhere('name', 'NOT LIKE', $nono);
   }
 
-  $contacts = Civi\Api4\Contact::getFields()
+  $contactFields = Civi\Api4\Contact::getFields()
     ->setCheckPermissions(FALSE)
     ->setIncludeCustom(TRUE)
     ->setLoadOptions(TRUE)
     ->setAction('Create')
     ->execute();
 
+  $contactSettings = [
+    'data' => [],
+  ];
+
   $mimeType = 'text/javascript';
   $content = "CRM.afformAdminData={";
   $content .= 'entities:' . json_encode((array) $entityApi->execute(), JSON_UNESCAPED_SLASHES) . ',';
-  $content .= 'fields:' . json_encode(['Contact' => (array) $contacts], JSON_UNESCAPED_SLASHES);
+  $content .= 'fields:' . json_encode(['Contact' => (array) $contactFields], JSON_UNESCAPED_SLASHES) . ',';
+  $content .= 'settings:' . json_encode(['Contact' => $contactSettings], JSON_UNESCAPED_SLASHES);
   $content .= '}';
 }
index 623114914452179d869b5d3ce794eec8879d2703..93afb919b58588ecb93bf756619d43a1851e858c 100644 (file)
@@ -1,30 +1,54 @@
-#afGuiEditor-main {
+#afGuiEditor {
   display: flex;
 }
 
-#afGuiEditor-main .panel.panel-default {
-  margin-bottom: 0;
+#afGuiEditor .panel.panel-default {
+  /*margin-bottom: 0;*/
 }
 
-#afGuiEditor-main #afGuiEditor-palette {
+#afGuiEditor #afGuiEditor-palette {
   flex: 1;
   margin-right: 5px;
 }
 
-#afGuiEditor-main #afGuiEditor-canvas {
+#afGuiEditor #afGuiEditor-canvas {
   flex: 1;
   margin-left: 5px;
 }
 
-#afGuiEditor-main #afGuiEditor-palette-tabs li > a {
+#afGuiEditor #afGuiEditor-palette-tabs li {
+  top: 1px;
+}
+
+#afGuiEditor #afGuiEditor-palette-tabs li > a {
   padding: 10px 15px;
   font-size: 12px;
 }
 
-#afGuiEditor-main #afGuiEditor-palette-tabs .dropdown-menu {
+#afGuiEditor #afGuiEditor-palette-tabs .dropdown-menu {
   max-height: 500px;
   overflow-y: auto;
 }
 
-#afGuiEditor-main #afGuiEditor-palette-config {
+#afGuiEditor .crm-editable-enabled,
+#afGuiEditor-palette-tabs > li > a > span {
+  display: inline-block;
+  padding: 0 4px !important;
+  border: 2px solid transparent !important;
+}
+#afGuiEditor .crm-editable-enabled:hover:not(:focus) {
+  border: 2px dashed grey !important;
+}
+#afGuiEditor .crm-editable-enabled:before,
+#afGuiEditor .crm-editable-enabled:after {
+  content: '';
+  display: none;
+}
+
+#afGuiEditor-palette-config .form-inline {
+  margin-bottom: 10px;
+}
+
+#afGuiEditor-palette-config .form-inline label {
+  min-width: 110px;
 }
index 6c58bd82144ba211a1f3d2cf0262609b66bac198..6741032c0b36f20fa614868c048c2c138970462e 100644 (file)
@@ -14,6 +14,7 @@
         $scope.afform = null;
         $scope.selectedEntity = null;
         $scope.meta = CRM.afformAdminData;
+        $scope.controls = {};
         var newForm = {
           title: ts('Untitled Form'),
           layout: {
           $scope.selectedEntity = entityName;
         };
 
+        $scope.getField = function(entityName, fieldName) {
+          return _.filter($scope.meta.fields[entityName], {name: fieldName})[0];
+        };
+
+        $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: field.name in $scope.entities[$scope.selectedEntity].data});
+          }, []);
+          return {results: fields};
+        };
+
+        $scope.removeValue = function(entity, fieldName) {
+          delete entity.data[fieldName];
+        };
+
+        $scope.$watch('controls.addValue', function(fieldName) {
+          if (fieldName) {
+            $scope.entities[$scope.selectedEntity].data[fieldName] = '';
+            $scope.controls.addValue = '';
+          }
+        });
+
       }
     };
   });
   }
 
   // Editable titles using ngModel & html5 contenteditable
+  // Cribbed from ContactLayoutEditor
   angular.module('afGuiEditor').directive("afGuiEditable", function() {
     return {
       restrict: "A",
     };
   });
 
+  // Cribbed from the Api4 Explorer
+  angular.module('afGuiEditor').directive('afGuiFieldValue', function() {
+    return {
+      scope: {
+        field: '=afGuiFieldValue'
+      },
+      require: 'ngModel',
+      link: function (scope, element, attrs, ctrl) {
+        var ts = scope.ts = CRM.ts(),
+          multi;
+
+        function destroyWidget() {
+          var $el = $(element);
+          if ($el.is('.crm-form-date-wrapper .crm-hidden-date')) {
+            $el.crmDatepicker('destroy');
+          }
+          if ($el.is('.select2-container + input')) {
+            $el.crmEntityRef('destroy');
+          }
+          $(element).removeData().removeAttr('type').removeAttr('placeholder').show();
+        }
+
+        function makeWidget(field) {
+          var $el = $(element),
+            inputType = field.input_type,
+            dataType = field.data_type;
+          multi = field.serialize || dataType === 'Array';
+          if (inputType === 'Date') {
+            $el.crmDatepicker({time: (field.input_attrs && field.input_attrs.time) || false});
+          }
+          else if (field.fk_entity || field.options || dataType === 'Boolean') {
+            if (field.fk_entity) {
+              $el.crmEntityRef({entity: field.fk_entity, select:{multiple: multi}});
+            } else if (field.options) {
+              var options = _.transform(field.options, function(options, val, key) {
+                options.push({id: key, text: val});
+              }, []);
+              $el.select2({data: options, multiple: multi});
+            } else if (dataType === 'Boolean') {
+              $el.attr('placeholder', ts('- select -')).crmSelect2({allowClear: false, multiple: multi, placeholder: ts('- select -'), data: [
+                {id: '1', text: ts('Yes')},
+                {id: '0', text: ts('No')}
+              ]});
+            }
+          } else if (dataType === 'Integer' && !multi) {
+            $el.attr('type', 'number');
+          }
+        }
+
+        // Copied from ng-list but applied conditionally if field is multi-valued
+        var parseList = function(viewValue) {
+          // If the viewValue is invalid (say required but empty) it will be `undefined`
+          if (_.isUndefined(viewValue)) return;
+
+          if (!multi) {
+            return viewValue;
+          }
+
+          var list = [];
+
+          if (viewValue) {
+            _.each(viewValue.split(','), function(value) {
+              if (value) list.push(_.trim(value));
+            });
+          }
+
+          return list;
+        };
+
+        // Copied from ng-list
+        ctrl.$parsers.push(parseList);
+        ctrl.$formatters.push(function(value) {
+          return _.isArray(value) ? value.join(', ') : value;
+        });
+
+        // Copied from ng-list
+        ctrl.$isEmpty = function(value) {
+          return !value || !value.length;
+        };
+
+        scope.$watchCollection('field', function(field) {
+          destroyWidget();
+          if (field) {
+            makeWidget(field);
+          }
+        });
+      }
+    };
+  });
+
 })(angular, CRM.$, CRM._);
index bc9dd7f3511f1464a00f7ce46602d91c9796c96e..76c71e8a1732bb57536ccea1d0c72620e8068f03 100644 (file)
@@ -1,5 +1,19 @@
 <div>
-  <a href ng-if="selectedEntity !== null" ng-click="removeEntity(selectedEntity)" class="btn btn-sm btn-danger pull-right">
+  <a href ng-click="removeEntity(selectedEntity)" class="btn btn-sm btn-danger pull-right">
     <i class="crm-i fa-trash"></i>
   </a>
+
+  <fieldset>
+    <legend>{{ ts('Values') }}</legend>
+    <div class="form-inline" ng-if="entity.data" ng-repeat="(fieldName, value) in entity.data">
+      <label>{{ getField(entity.type, fieldName).title }}:</label>
+      <input class="form-control" af-gui-field-value="getField(entity.type, fieldName)" ng-model="entity.data[fieldName]" />
+      <a href class="pull-right" ng-click="removeValue(entity, fieldName)">
+        <i class="crm-i fa-times"></i>
+      </a>
+    </div>
+    <div class="form-inline">
+      <input class="form-control" ng-model="controls.addValue" crm-ui-select="{data: valuesFields}" placeholder="Add value" />
+    </div>
+  </fieldset>
 </div>
index b80e6e067849565fa3d5c18a00e7d0384e03681b..2af098cdb7e8e81eb6de2e8f3cb6f77a502c0ffb 100644 (file)
@@ -1,3 +1,8 @@
-<div>
-
-</div>
+<label for="af_config_form_title">
+  {{ ts('Title:') }}
+</label>
+<input ng-model="afform.title" class="form-control" id="af_config_form_title" />
+<label for="af_config_form_server_route">
+  {{ ts('URL:') }}
+</label>
+<input ng-model="afform.server_route" class="form-control" id="af_config_form_server_route" />
index bf70aa714519efd1fd4690376d0baee4edf46238..499b9465f43073f874cc188460f9865e69495a2c 100644 (file)
@@ -1,7 +1,8 @@
 <div crm-ui-debug="afform"></div>
 <div crm-ui-debug="entities"></div>
+<div crm-ui-debug="meta"></div>
 <div id="bootstrap-theme">
-  <div id="afGuiEditor-main">
+  <div id="afGuiEditor">
     <div id="afGuiEditor-palette" ng-include="'~/afGuiEditor/palette.html'"></div>
     <div id="afGuiEditor-canvas" ng-include="'~/afGuiEditor/canvas.html'"></div>
   </div>
index fd7418b359181ceaf66d1b17086300d1fb777113..78ca04333451cca76248676cb1f3410268453940 100644 (file)
@@ -1,7 +1,9 @@
 <div id="afGuiEditor-palette-config" class="panel panel-default">
     <ul id="afGuiEditor-palette-tabs" class="panel-heading nav nav-tabs">
       <li role="presentation" ng-class="{active: selectedEntity === null}">
-        <a href ng-click="selectEntity(null)">{{ ts('Form Settings') }}</a>
+        <a href ng-click="selectEntity(null)">
+          <span>{{ ts('Form Settings') }}</span>
+        </a>
       </li>
       <li role="presentation" ng-repeat="entity in entities" ng-class="{active: selectedEntity === entity.name}">
         <a href ng-click="selectEntity(entity.name)">
@@ -10,7 +12,7 @@
       </li>
       <li role="presentation" class="dropdown">
         <a href class="dropdown-toggle" data-toggle="dropdown">
-          <i class="crm-i fa-plus"></i>
+          <span><i class="crm-i fa-plus"></i></span>
         </a>
         <ul class="dropdown-menu">
           <li ng-repeat="entity in meta.entities">
@@ -20,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-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>