Commit da27ede2 authored by colemanw's avatar colemanw
Browse files

Improve drag-n-drop placeholder & validation

parent 17c838d0
......@@ -261,6 +261,24 @@ function afform_gui_civicrm_buildAsset($asset, $params, &$mimeType, &$content) {
],
],
],
'fieldset' => [
'title' => ts('Fieldset'),
'element' => [
'#tag' => 'fieldset',
'af-fieldset' => NULL,
'#children' => [
[
'#tag' => 'legend',
'class' => 'af-text',
'#children' => [
[
'#text' => ts('Enter title'),
],
],
],
],
],
],
];
// Reformat options
......
......@@ -124,7 +124,7 @@
opacity: 1;
transition: opacity .2s;
}
#afGuiEditor-canvas .panel-body > div > .af-gui-bar {
#afGuiEditor-canvas-body > div > .af-gui-bar {
top: -5px;
}
......@@ -483,3 +483,9 @@
#afGuiEditor [af-gui-edit-options] h5 {
margin-left: 20px;
}
#afGuiEditor .af-gui-dropzone {
background-color: #e9eeff;
border: 2px solid #0071bd;
min-height: 30px;
}
......@@ -94,21 +94,9 @@
var pos = 1 + _.findLastIndex($scope.layout['#children'], {'#tag': 'af-entity'});
$scope.layout['#children'].splice(pos, 0, $scope.entities[type + num]);
// Create a new af-fieldset container for the entity
var fieldset = {
'#tag': 'fieldset',
'af-fieldset': type + num,
'#children': [
{
'#tag': 'legend',
'class': 'af-text',
'#children': [
{
'#text': meta.label + ' ' + num
}
]
}
]
};
var fieldset = _.cloneDeep(editor.meta.elements.fieldset.element);
fieldset['af-fieldset'] = type + num;
fieldset['#children'][0]['#children'][0]['#text'] = meta.label + ' ' + num;
// Add default contact name block
if (meta.entity === 'Contact') {
fieldset['#children'].push({'#tag': 'afblock-name-' + type.toLowerCase()});
......@@ -146,6 +134,28 @@
return $scope.selectedEntityName;
};
// Validates that a drag-n-drop action is allowed
this.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 ((sort.model['af-fieldset'] || $item.has('.af-gui-fieldset').length) && $target.closest('.af-gui-fieldset').length) {
return sort.cancel();
}
}
};
$scope.addEntity = function(entityType) {
var entityName = editor.addEntity(entityType);
editor.selectEntity(entityName);
......@@ -334,8 +344,12 @@
$scope.elementTitles.length = 0;
_.each($scope.editor.meta.elements, function(element, name) {
if (!search || _.contains(name, search) || _.contains(element.title.toLowerCase(), search)) {
$scope.elementList.push(_.cloneDeep(element.element));
$scope.elementTitles.push(element.title);
var node = _.cloneDeep(element.element);
if (name === 'fieldset') {
node['af-fieldset'] = $scope.entity.name;
}
$scope.elementList.push(node);
$scope.elementTitles.push(name === 'fieldset' ? ts('Fieldset for %1', {1: $scope.entity.label}) : element.title);
}
});
}
......@@ -459,32 +473,6 @@
}
};
// 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]);
// Dropping onto palette is ok; works like a trash can
if ($target.closest('#afGuiEditor-palette-config').length) {
return;
}
// 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.hasClass('af-gui-fieldset') || $item.has('.af-gui-fieldset').length) && $target.closest('.af-gui-fieldset').length) {
return sort.cancel();
}
}
};
$scope.tags = {
div: ts('Container'),
fieldset: ts('Fieldset')
......
......@@ -12,7 +12,7 @@
<div>{{ ts('Form Layout') }}</div>
</form>
</div>
<div class="panel-body">
<div id="afGuiEditor-canvas-body" class="panel-body">
<div ng-if="layout" af-gui-container="layout" entity-name="" />
</div>
</div>
......@@ -22,7 +22,7 @@
<ul class="dropdown-menu" ng-if="menu.open" ng-include="'~/afGuiEditor/canvas-menu.html'"></ul>
</div>
</div>
<div ui-sortable="{handle: '.af-gui-bar', update: onDrop, connectWith: '[ui-sortable]', cancel: 'input,textarea,button,select,option,a,.dropdown-menu'}" ng-model="getSetChildren" ng-model-options="{getterSetter: true}" class="af-gui-layout {{ getLayout() }}">
<div ui-sortable="{handle: '.af-gui-bar', connectWith: '[ui-sortable]', cancel: 'input,textarea,button,select,option,a,.dropdown-menu', placeholder: 'af-gui-dropzone', containment: '#afGuiEditor-canvas-body'}" ui-sortable-update="editor.onDrop" ng-model="getSetChildren" ng-model-options="{getterSetter: true}" class="af-gui-layout {{ getLayout() }}">
<div ng-repeat="item in getSetChildren()" >
<div ng-switch="container.getNodeType(item)">
<div ng-switch-when="fieldset" af-gui-container="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'] }}" />
......
......@@ -22,7 +22,7 @@
<div class="af-gui-entity-palette-select-list">
<div ng-if="blockList.length">
<label>{{ ts('Blocks') }}</label>
<div ui-sortable="{update: buildPaletteLists, items: '&gt; div:not(.disabled)', connectWith: '[data-entity=' + entity.name + '] &gt; [ui-sortable]'}" ng-model="blockList">
<div ui-sortable="{update: buildPaletteLists, items: '&gt; div:not(.disabled)', connectWith: '[data-entity=' + entity.name + '] &gt; [ui-sortable]', placeholder: 'af-gui-dropzone'}" ui-sortable-update="editor.onDrop" ng-model="blockList">
<div ng-repeat="block in blockList" ng-class="{disabled: blockInUse(block)}">
{{ blockTitles[$index] }}
</div>
......@@ -30,7 +30,7 @@
</div>
<div ng-if="elementList.length">
<label>{{ ts('Elements') }}</label>
<div ui-sortable="{update: buildPaletteLists, items: '&gt; div:not(.disabled)', connectWith: '[ui-sortable]'}" ng-model="elementList">
<div ui-sortable="{update: buildPaletteLists, items: '&gt; div:not(.disabled)', connectWith: '[ui-sortable]', placeholder: 'af-gui-dropzone'}" ui-sortable-update="editor.onDrop" ng-model="elementList">
<div ng-repeat="element in elementList" >
{{ elementTitles[$index] }}
</div>
......@@ -39,7 +39,7 @@
<div ng-repeat="fieldGroup in fieldList">
<div ng-if="fieldGroup.fields.length">
<label>{{ fieldGroup.label }}</label>
<div ui-sortable="{update: buildPaletteLists, items: '&gt; div:not(.disabled)', connectWith: '[data-entity=' + fieldGroup.entityName + '] &gt; [ui-sortable]'}" ng-model="fieldGroup.fields">
<div ui-sortable="{update: buildPaletteLists, items: '&gt; div:not(.disabled)', connectWith: '[data-entity=' + fieldGroup.entityName + '] &gt; [ui-sortable]', placeholder: 'af-gui-dropzone'}" ui-sortable-update="editor.onDrop" ng-model="fieldGroup.fields">
<div ng-repeat="field in fieldGroup.fields" ng-class="{disabled: fieldInUse(field.name)}">
{{ editor.getField(fieldGroup.entityType, field.name).title }}
</div>
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment