Afform - Predefined container styles
authorColeman Watts <coleman@civicrm.org>
Fri, 18 Mar 2022 19:24:34 +0000 (15:24 -0400)
committerColeman Watts <coleman@civicrm.org>
Sun, 20 Mar 2022 02:16:10 +0000 (22:16 -0400)
Adds a predefined "Panel Pane" style, for creating dashboards.

ext/afform/admin/Civi/AfformAdmin/AfformAdminMeta.php
ext/afform/admin/ang/afGuiEditor.css
ext/afform/admin/ang/afGuiEditor/afGuiMenuItemStyle.component.js [new file with mode: 0644]
ext/afform/admin/ang/afGuiEditor/afGuiMenuItemStyle.html [new file with mode: 0644]
ext/afform/admin/ang/afGuiEditor/elements/afGuiContainer-menu.html
ext/afform/admin/ang/afGuiEditor/elements/afGuiContainer.html
ext/afform/admin/ang/afGuiEditor/elements/afGuiSearchContainer-menu.html
ext/afform/core/ang/afCore.css

index 8ca5ec909c415422229d2ceeb7654fe37518682f..51cd1c151f7cfce1587028fd045ebb28643a09bc 100644 (file)
@@ -249,6 +249,7 @@ class AfformAdminMeta {
         'element' => [
           '#tag' => 'fieldset',
           'af-fieldset' => NULL,
+          'class' => 'af-container',
           'af-title' => E::ts('Enter title'),
           '#children' => [],
         ],
index 3b60112dd1e6f6a0e878c6c94700eb82f0ee7d1d..efb2c82151dac60fabcf33a29a6a983473175df5 100644 (file)
@@ -204,6 +204,13 @@ body.af-gui-dragging {
   margin-top: 10px;
 }
 
+/* Card style */
+#afGuiEditor .af-gui-container.af-container-style-pane {
+  box-shadow: 1px 2px 8px 1px rgb(0, 0, 0, .3);
+  background: linear-gradient(to bottom, #f2f2f2 0 22px, transparent 22px 100%) no-repeat;
+  border-radius: 4px;
+}
+
 #afGuiEditor af-gui-container,
 #afGuiEditor af-gui-markup,
 #afGuiEditor af-gui-field,
diff --git a/ext/afform/admin/ang/afGuiEditor/afGuiMenuItemStyle.component.js b/ext/afform/admin/ang/afGuiEditor/afGuiMenuItemStyle.component.js
new file mode 100644 (file)
index 0000000..ec0680c
--- /dev/null
@@ -0,0 +1,31 @@
+// https://civicrm.org/licensing
+(function(angular, $, _) {
+  "use strict";
+
+  // Menu item to control the border property of a node
+  angular.module('afGuiEditor').component('afGuiMenuItemStyle', {
+    templateUrl: '~/afGuiEditor/afGuiMenuItemStyle.html',
+    bindings: {
+      node: '='
+    },
+    controller: function($scope, afGui) {
+      var ts = $scope.ts = CRM.ts('org.civicrm.afform_admin'),
+        ctrl = this;
+
+      // Todo: Make this an option group so other extensions can add to it
+      this.styles = [
+        {name: 'af-container-style-pane', label: ts('Panel Pane')}
+      ];
+
+      $scope.getSetStyle = function(style) {
+        var options = _.map(ctrl.styles, 'name');
+        if (arguments.length) {
+          afGui.modifyClasses(ctrl.node, options, style);
+        }
+        return _.intersection(afGui.splitClass(ctrl.node['class']), options)[0] || '';
+      };
+
+    }
+  });
+
+})(angular, CRM.$, CRM._);
diff --git a/ext/afform/admin/ang/afGuiEditor/afGuiMenuItemStyle.html b/ext/afform/admin/ang/afGuiEditor/afGuiMenuItemStyle.html
new file mode 100644 (file)
index 0000000..2404397
--- /dev/null
@@ -0,0 +1,7 @@
+<div class="af-gui-field-select-in-dropdown form-inline" ng-click="$event.stopPropagation()">
+  <label>{{:: ts('Style:') }}</label>
+  <select class="form-control" ng-model="getSetStyle" ng-model-options="{getterSetter: true}">
+    <option value="">{{:: ts('None') }}</option>
+    <option ng-repeat="style in $ctrl.styles" value="{{:: style.name }}">{{:: style.label }}</option>
+  </select>
+</div>
index 9afec26ebda05041875fd5eedc7dc90658e02106..00b75cff271769ddabcdb31e3034d79589d78eb7 100644 (file)
@@ -31,6 +31,7 @@
   </div>
 </li>
 <li><af-gui-menu-item-collapsible ng-if="!block" node="$ctrl.node" class="af-gui-field-select-in-dropdown form-inline"></af-gui-menu-item-collapsible></li>
+<li><af-gui-menu-item-style node="$ctrl.node"></af-gui-menu-item-style></li>
 <li><af-gui-menu-item-border node="$ctrl.node"></af-gui-menu-item-border></li>
 <li><af-gui-menu-item-background node="$ctrl.node"></af-gui-menu-item-background></li>
 <li role="separator" class="divider"></li>
index 6e3fe030db3169ab89be97d01fae5691fecc8f38..c98ec2297e8a4b00df8bbb7b97a5ee3d40d76ffc 100644 (file)
 <div ng-if="!$ctrl.loading" ui-sortable="$ctrl.sortableOptions" ui-sortable-update="$ctrl.editor.onDrop" ng-model="getSetChildren" ng-model-options="{getterSetter: true}" class="af-gui-layout {{ getLayout() }}">
   <div ng-repeat="item in getSetChildren()" >
     <div ng-switch="$ctrl.getNodeType(item)">
-      <af-gui-container ng-switch-when="fieldset" node="item" delete-this="$ctrl.removeElement(item)" style="{{ item.style }}" class="af-gui-container af-gui-fieldset af-gui-container-type-{{ item['#tag'] }}" ng-class="{'af-entity-selected': isSelectedFieldset(item['af-fieldset'])}" entity-name="item['af-fieldset']" data-entity="{{ item['af-fieldset'] }}" ></af-gui-container>
-      <af-gui-container ng-switch-when="container" node="item" delete-this="$ctrl.removeElement(item)" style="{{ item.style }}" class="af-gui-container af-gui-container-type-{{ item['#tag'] }}" entity-name="$ctrl.entityName" data-entity="{{ $ctrl.getDataEntity() }}" ></af-gui-container>
+      <af-gui-container ng-switch-when="fieldset" node="item" delete-this="$ctrl.removeElement(item)" style="{{ item.style }}" class="af-gui-container af-gui-fieldset af-gui-container-type-{{ item['#tag'] + ' ' + item['class'] }}" ng-class="{'af-entity-selected': isSelectedFieldset(item['af-fieldset'])}" entity-name="item['af-fieldset']" data-entity="{{ item['af-fieldset'] }}" ></af-gui-container>
+      <af-gui-container ng-switch-when="container" node="item" delete-this="$ctrl.removeElement(item)" style="{{ item.style }}" class="af-gui-container af-gui-container-type-{{ item['#tag'] + ' ' + item['class'] }}" entity-name="$ctrl.entityName" data-entity="{{ $ctrl.getDataEntity() }}" ></af-gui-container>
       <af-gui-container ng-switch-when="join" node="item" delete-this="$ctrl.removeElement(item)" style="{{ item.style }}" class="af-gui-container" join="item['af-join']" entity-name="$ctrl.entityName + '-join-' + item['af-join']" data-entity="{{ $ctrl.entityName + '-join-' + item['af-join'] }}" ></af-gui-container>
       <af-gui-field ng-switch-when="field" node="item" delete-this="$ctrl.removeElement(item)" ></af-gui-field>
       <af-gui-text ng-switch-when="text" node="item" delete-this="$ctrl.removeElement(item)" class="af-gui-element af-gui-text" ></af-gui-text>
       <af-gui-markup ng-switch-when="markup" node="item" delete-this="$ctrl.removeElement(item)" class="af-gui-markup" ></af-gui-markup>
       <af-gui-button ng-switch-when="button" node="item" delete-this="$ctrl.removeElement(item)" class="af-gui-element af-gui-button" ></af-gui-button>
-      <af-gui-container ng-switch-when="searchFieldset" node="item" delete-this="$ctrl.removeElement(item)" style="{{ item.style }}" class="af-gui-container af-gui-fieldset af-gui-container-type-{{ item['#tag'] }}" ng-class="{'af-entity-selected': isSelectedSearchFieldset(item)}" data-entity="{{ getSearchKey(item) }}" ></af-gui-container>
+      <af-gui-container ng-switch-when="searchFieldset" node="item" delete-this="$ctrl.removeElement(item)" style="{{ item.style }}" class="af-gui-container af-gui-fieldset af-gui-container-type-{{ item['#tag'] + ' ' + item['class'] }}" ng-class="{'af-entity-selected': isSelectedSearchFieldset(item)}" data-entity="{{ getSearchKey(item) }}" ></af-gui-container>
       <af-gui-search-display ng-switch-when="searchDisplay" node="item" class="af-gui-element"></af-gui-search-display>
     </div>
   </div>
index ac3b8b7c303f805f69ecda3189af87b9597bcede..088e4bd712d6e678c84278e5738598cc54a8744a 100644 (file)
@@ -7,6 +7,7 @@
   </div>
 </li>
 <li><af-gui-menu-item-collapsible node="$ctrl.node" class="af-gui-field-select-in-dropdown form-inline"></af-gui-menu-item-collapsible></li>
+<li><af-gui-menu-item-style node="$ctrl.node"></af-gui-menu-item-style></li>
 <li><af-gui-menu-item-border node="$ctrl.node"></af-gui-menu-item-border></li>
 <li><af-gui-menu-item-background node="$ctrl.node"></af-gui-menu-item-background></li>
 <li role="separator" class="divider"></li>
index edc60124370f64b13215cb39bbacdb8f4fc5dba5..841cf8732a39c401d79bd47f7fe5b5e9a58973e6 100644 (file)
@@ -8,6 +8,7 @@ a.af-api4-action-idle {
 
 .af-container.af-layout-cols {
   display: flex;
+  flex-wrap: wrap;
 }
 .af-container.af-layout-cols > * {
   flex: 1;
@@ -17,6 +18,14 @@ a.af-api4-action-idle {
   margin-right: .5em;
   vertical-align: top;
 }
+.af-container.af-layout-cols > .af-title {
+  flex: 0 0 100%;
+}
+.af-container.af-layout-inline > .af-title {
+  display: block;
+  width: 100%;
+}
+
 af-form {
   display: block;
 }
@@ -47,3 +56,27 @@ af-form {
 .af-collapsible.af-collapsed > .af-title + * {
   display: none;
 }
+
+/* Card style */
+#bootstrap-theme .af-container-style-pane {
+  background-color: white;
+  border-radius: 4px;
+  box-shadow: 1px 2px 8px 1px rgba(0, 0, 0, 0.3);
+  margin: 10px;
+  padding: 5px;
+}
+#bootstrap-theme .af-container-style-pane > .af-title {
+  background-color: #70716b;
+  color: white;
+  padding: 5px;
+  border-radius: 4px 4px 0 0;
+  position: relative;
+  top: -5px;
+  left: -5px;
+  width: calc(100% + 10px);
+  margin-top: 0;
+  margin-bottom: 10px;
+}
+#bootstrap-theme .af-container-style-pane.af-collapsed > .af-title {
+  margin-bottom: 0;
+}