Skip to content

Commit

Permalink
Fields drag n drop
Browse files Browse the repository at this point in the history
  • Loading branch information
colemanw authored and CiviCRM committed Sep 16, 2020
1 parent f3cd385 commit 65c9e7a
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 38 deletions.
1 change: 1 addition & 0 deletions ext/afform/core/afform.php
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ function afform_civicrm_alterAngular($angular) {
}
$entityType = $entities[$entityName]['type'];
$getFields = civicrm_api4($entityType, 'getFields', [
'action' => 'create',
'where' => [['name', '=', $fieldName]],
'select' => ['title', 'input_type', 'input_attrs', 'options'],
'loadOptions' => TRUE,
Expand Down
3 changes: 2 additions & 1 deletion ext/afform/gui/afform_gui.php
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,8 @@ function afform_gui_civicrm_buildAsset($asset, $params, &$mimeType, &$content) {
->setCheckPermissions(FALSE)
->setIncludeCustom(TRUE)
->setLoadOptions(TRUE)
->setAction('Create')
->setAction('create')
->setSelect(['name', 'title', 'input_type', 'input_attrs', 'options'])
->execute();

$contactSettings = [
Expand Down
46 changes: 34 additions & 12 deletions ext/afform/gui/ang/afGuiEditor.css
Original file line number Diff line number Diff line change
Expand Up @@ -64,18 +64,6 @@
width: 100%;
padding-left:15px;
}
/* grip handle */
#afGuiEditor .af-gui-bar:before {
background-size: cover;
background: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1IiBoZWlnaHQ9IjUiPgo8cmVjdCB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSIjODg4Ij48L3JlY3Q+Cjwvc3ZnPg==");
width: 10px;
height: 15px;
content: ' ';
display: block;
position: absolute;
left: 4px;
top: 5px;
}

#afGuiEditor-canvas:hover .af-gui-bar {
visibility: visible;
Expand Down Expand Up @@ -106,3 +94,37 @@
#afGuiEditor .af-gui-block:hover {
border: 2px dashed #757575;
}

#afGuiEditor [ui-sortable] {
min-height: 25px;
}

#afGuiEditor .af-gui-field-select-list {
max-height: 280px;
overflow-y: auto;
}

#afGuiEditor .af-gui-field-select-list > div {
cursor: move;
padding-left:15px;
position: relative;
}
#afGuiEditor .af-gui-field-select-list > div.disabled {
cursor: auto;
}
#afGuiEditor .af-gui-field-select-list > div:not(.disabled):hover {
background-color: #efefef;
}
/* grip handle */
#afGuiEditor .af-gui-bar:before,
#afGuiEditor .af-gui-field-select-list > div:not(.disabled):hover:before {
background-size: cover;
background: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1IiBoZWlnaHQ9IjUiPgo8cmVjdCB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSIjODg4Ij48L3JlY3Q+Cjwvc3ZnPg==");
width: 10px;
height: 15px;
content: ' ';
display: block;
position: absolute;
left: 4px;
top: 5px;
}
84 changes: 68 additions & 16 deletions ext/afform/gui/ang/afGuiEditor.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
(function(angular, $, _) {
angular.module('afGuiEditor', CRM.angRequires('afGuiEditor'));

angular.module('afGuiEditor').directive('afGuiEditor', function(crmApi4, $parse) {
angular.module('afGuiEditor').directive('afGuiEditor', function(crmApi4, $parse, $timeout) {
return {
restrict: 'A',
templateUrl: '~/afGuiEditor/main.html',
Expand All @@ -14,6 +14,7 @@
$scope.selectedEntity = null;
$scope.meta = CRM.afformAdminData;
$scope.controls = {};
$scope.fieldList = {};
$scope.editor = this;
var newForm = {
title: ts('Untitled Form'),
Expand Down Expand Up @@ -49,7 +50,8 @@
$scope.layout = getTags($scope.afform.layout, 'af-form')[0];
evaluate($scope.layout['#children']);
$scope.entities = getTags($scope.layout['#children'], 'af-entity', 'name');
$scope.fields = getAllFields($scope.layout['#children']);
expandFields($scope.layout['#children']);
_.each(_.keys($scope.entities), buildFieldList);
}

this.addEntity = function(entityType) {
Expand Down Expand Up @@ -91,10 +93,29 @@
return $scope.selectedEntity;
};

$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: _.cloneDeep(_.pick(field, ['title', 'input_type', 'input_attrs']))
});
});
}

$scope.valuesFields = function() {
var fields = _.transform($scope.meta.fields[$scope.entities[$scope.selectedEntity].type], function(fields, field) {
var data = $scope.entities[$scope.selectedEntity].data || {};
fields.push({id: field.name, text: field.title, disabled: field.name in data});
fields.push({id: field.name, text: field.title, disabled: $scope.fieldInUse($scope.selectedEntity, field.name)});
}, []);
return {results: fields};
};
Expand All @@ -113,6 +134,33 @@
}
});

// 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) {
if (_.isPlainObject(item)) {
Expand All @@ -129,6 +177,22 @@
});
}

function expandFields(collection, entityType) {
_.each(collection, function (item) {
if (_.isPlainObject(item)) {
if (item['af-fieldset']) {
expandFields(item['#children'], $scope.editor.getEntity(item['af-fieldset']).type);
}
else if (item['#tag'] === 'af-field') {
item.defn = item.defn || {};
_.defaults(item.defn, _.cloneDeep(_.pick($scope.editor.getField(entityType, item.name), ['title', 'input_type', 'input_attrs'])));
} else {
expandFields(item['#children'], entityType);
}
}
});
}

}
};
});
Expand All @@ -149,18 +213,6 @@
return indexBy ? _.indexBy(items, indexBy) : items;
}

// Lists fields by entity. Note that fields for an entity can be spread across several fieldsets.
function getAllFields(layout) {
var allFields = {};
_.each(getTags(layout, 'af-fieldset'), function(fieldset) {
if (!allFields[fieldset.model]) {
allFields[fieldset.model] = {};
}
_.assign(allFields[fieldset.model], getTags(fieldset['#children'], 'af-field', 'name'));
});
return allFields;
}

// Turns a space-separated list (e.g. css classes) into an array
function splitClass(str) {
if (_.isArray(str)) {
Expand Down
4 changes: 2 additions & 2 deletions ext/afform/gui/ang/afGuiEditor/block.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
<option ng-repeat="(opt, label) in tags" value="{{ opt }}">{{ label }}</option>
</select>
</div>
<div ui-sortable="{handle: '.af-gui-bar'}" ng-model="node['#children']">
<div ui-sortable="{handle: '.af-gui-bar', connectWith: '[ui-sortable]'}" ng-model="node['#children']">
<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" ng-class="{'af-entity-selected': isSelectedFieldset(item['af-fieldset'])}" entity-name="item['af-fieldset']" />
<div ng-switch-when="fieldset" af-gui-block="item" class="af-gui-block af-gui-fieldset" 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" entity-name="entityName" />
<div ng-switch-when="field" af-gui-field="item" class="af-gui-field" entity-name="entityName" />
<div ng-switch-when="text" af-gui-text="item" class="af-gui-text" />
Expand Down
11 changes: 5 additions & 6 deletions ext/afform/gui/ang/afGuiEditor/config-entity.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,11 @@

<fieldset>
<legend>{{ ts('Fields') }}</legend>
<ul class="crm-checkbox-list">
<li ng-repeat="(key, spec) in meta.fields[entity.type]" >
<input type="checkbox" id="field-{{ selectedEntity + '-' + key }}" />
<label for="field-{{ selectedEntity + '-' + key }}">{{ spec.title }}</label>
</li>
</ul>
<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)}">
{{ field.defn.title }}
</div>
</div>
</fieldset>

<fieldset>
Expand Down
2 changes: 1 addition & 1 deletion ext/afform/gui/ang/afGuiEditor/main.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<div crm-ui-debug="afform"></div>
<div crm-ui-debug="layout"></div>
<div crm-ui-debug="entities"></div>
<div crm-ui-debug="fields"></div>
<div crm-ui-debug="fieldList"></div>
<div crm-ui-debug="meta"></div>
<div id="bootstrap-theme">
<div id="afGuiEditor">
Expand Down

0 comments on commit 65c9e7a

Please sign in to comment.