// If it's not an object, don't mess with it.
continue;
}
+ // TODO: Teach the api to return options in this format
+ if (!empty($fieldInfo['options'])) {
+ $fieldInfo['options'] = CRM_Utils_Array::makeNonAssociative($fieldInfo['options'], 'key', 'label');
+ }
+
$fieldDefn = $existingFieldDefn ? CRM_Utils_JS::getRawProps($existingFieldDefn) : [];
foreach ($fieldInfo as $name => $prop) {
// Merge array props 1 level deep
$scope.fieldId = $scope.afFieldset.getDefn().modelName + '-' + $scope.fieldName;
$scope.getData = $scope.afFieldset.getData;
+ $el.addClass('af-field-type-' + _.kebabCase($scope.defn.input_type));
+ },
+ controller: function($scope) {
+
$scope.getOptions = function() {
+ return $scope.defn.options || [{key: '1', label: ts('Yes')}, {key: '0', label: ts('No')}];
+ };
+
+ $scope.select2Options = function() {
return {
- results: _.transform($scope.defn.options, function(result, val, key) {
- result.push({id: key, text: val});
+ results: _.transform($scope.getOptions(), function(result, opt) {
+ result.push({id: opt.key, text: opt.label});
}, [])
};
};
-
- $el.addClass('af-field-type-' + _.kebabCase($scope.defn.input_type));
}
};
});
<ul class="crm-checkbox-list" id="{{ fieldId }}" ng-if="defn.options">
- <li ng-repeat="(key, val) in defn.options" >
- <input type="checkbox" checklist-model="getData()[name]" id="{{ fieldId + key }}" checklist-value="key" />
- <label for="{{ fieldId + key }}">{{ val }}</label>
+ <li ng-repeat="opt in defn.options" >
+ <input type="checkbox" checklist-model="getData()[name]" id="{{ fieldId + opt.key }}" checklist-value="opt.key" />
+ <label for="{{ fieldId + opt.key }}">{{ opt.label }}</label>
</li>
</ul>
-<input type="checkbox" ng-if="!defn.options" id="{{ fieldId }}" ng-model="getData()[fieldName]" />
+<input type="checkbox" ng-if="!defn.options" id="{{ fieldId }}" ng-model="getData()[fieldName]" ng-true-value="'1'" ng-false-value="'0'" />
-<label ng-repeat="(key, val) in defn.options" >
- <input class="crm-form-radio" type="radio" ng-model="getData()[fieldName]" value="{{ key }}" />
- {{ val }}
+<label ng-repeat="(opt in getOptions()" >
+ <input class="crm-form-radio" type="radio" ng-model="getData()[fieldName]" value="{{ opt.key }}" />
+ {{ opt.label }}
</label>
-<input crm-ui-select="{data: getOptions, multiple: defn.input_attrs.multiple, placeholder: defn.input_attrs.placeholder}" id="{{ fieldId }}" ng-model="getData()[fieldName]" />
+<input crm-ui-select="{data: select2Options, multiple: defn.input_attrs.multiple, placeholder: defn.input_attrs.placeholder}" id="{{ fieldId }}" ng-model="getData()[fieldName]" />
->setIncludeCustom(TRUE)
->setLoadOptions(TRUE)
->setAction('create')
- ->setSelect(['name', 'title', 'input_type', 'input_attrs', 'required', 'options', 'help_pre', 'help_post', 'serialize'])
+ ->setSelect(['name', 'title', 'input_type', 'input_attrs', 'required', 'options', 'help_pre', 'help_post', 'serialize', 'data_type'])
->addWhere('input_type', 'IS NOT NULL')
->execute()
->indexBy('name');
+
+ // TODO: Teach the api to return options in this format
+ foreach ($data['fields'][$entityName] as $name => $field) {
+ if (!empty($field['options'])) {
+ $data['fields'][$entityName][$name]['options'] = CRM_Utils_Array::makeNonAssociative($field['options'], 'key', 'label');
+ }
+ else {
+ unset($data['fields'][$entityName][$name]['options']);
+ }
+ }
}
// Now adjust the field metadata
// Scan for input types
// FIXME: Need a way to load this from other extensions too
- foreach (glob(__DIR__ . '/ang/afGuiEditor/widgets/*.html') as $file) {
+ foreach (glob(__DIR__ . '/ang/afGuiEditor/inputType/*.html') as $file) {
$matches = [];
preg_match('/([-a-z_A-Z0-9]*).html/', $file, $matches);
- $data['widgets'][$matches[1]] = $matches[1];
+ $data['inputType'][$matches[1]] = $matches[1];
}
$mimeType = 'text/javascript';
#afGuiEditor .af-gui-block {
border: 2px dashed transparent;
- padding-top: 22px;
+ position: relative;
+ padding: 22px 3px 3px;
min-height: 40px;
}
}
/* grip handle */
#afGuiEditor [ui-sortable] .af-gui-bar:before,
-#afGuiEditor .af-gui-field-select-list > div:not(.disabled):hover:before {
+#afGuiEditor .af-gui-field-select-list > div:not(.disabled):hover:before,
+#afGuiEditor [af-gui-edit-options] [ui-sortable] li:before {
background-size: cover;
background: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1IiBoZWlnaHQ9IjUiPgo8cmVjdCB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSIjODg4Ij48L3JlY3Q+Cjwvc3ZnPg==");
width: 10px;
cursor: default;
}
-#afGuiEditor .af-gui-field-widget input[type=text].form-control {
+#afGuiEditor .af-gui-field-input input[type=text].form-control {
color: #9a9a9a;
}
-#afGuiEditor .af-gui-field-widget-number input[type=text].form-control {
+#afGuiEditor .af-gui-field-input-number input[type=text].form-control {
background-image: url('../images/number.png');
background-repeat: no-repeat;
background-position: center right 6px;
}
-#afGuiEditor .af-gui-field-widget input.crm-form-date {
+#afGuiEditor .af-gui-field-input input.crm-form-date {
width: 140px;
margin-right: -2px;
}
-#afGuiEditor .af-gui-field-widget input.crm-form-time {
+#afGuiEditor .af-gui-field-input input.crm-form-time {
width: 80px;
}
+#afGuiEditor .af-gui-field-input-type-radio label.radio {
+ font-weight: normal;
+ margin-right: 10px;
+}
+#afGuiEditor .af-gui-field-input-type-radio label.radio input[type=radio] {
+ margin: 0;
+
+}
+
#afGuiEditor .af-gui-text-h1 {
font-weight: bolder;
font-size: 16px;
#afGuiEditor .af-gui-field-help {
font-style: italic;
}
+
+
+#afGuiEditor.af-gui-editing-options {
+ pointer-events: none;
+ cursor: default;
+}
+#afGuiEditor.af-gui-editing-options .panel-heading,
+#afGuiEditor.af-gui-editing-options .af-gui-element,
+.af-gui-editing-options #afGuiEditor-palette .panel-body > * {
+ opacity: .5;
+}
+#afGuiEditor.af-gui-editing-options .af-gui-block {
+ border: 2px solid transparent;
+}
+#afGuiEditor.af-gui-editing-options .af-gui-bar:not(.af-gui-edit-options-bar) {
+ visibility: hidden;
+}
+#afGuiEditor.af-gui-editing-options .af-gui-bar:before {
+ background: none;
+}
+
+#afGuiEditor [af-gui-edit-options] {
+ border: 2px solid #0071bd;
+ pointer-events: auto;
+ cursor: auto;
+ padding-top: 35px;
+ position: relative;
+}
+
+#afGuiEditor [af-gui-edit-options] .af-gui-edit-options-bar {
+ height: 30px;
+ font-family: "Courier New", Courier, monospace;
+ font-size: 12px;
+ width: 100%;
+ background-color: #f2f2f2;
+ position: absolute;
+ top: 0;
+ left: 0;
+ padding-left: 15px;
+}
+#afGuiEditor [af-gui-edit-options] ul[ui-sortable] {
+ padding: 5px 20px 0;
+}
+#afGuiEditor [af-gui-edit-options] ul[ui-sortable] li {
+ list-style: none;
+ padding-left: 15px;
+ position: relative;
+ background-color:#e7ecf1;
+ cursor: move;
+}
+#afGuiEditor [af-gui-edit-options] ul[ui-sortable] li:nth-child(even) {
+ background-color:#f2f2f2;
+}
+#afGuiEditor [af-gui-edit-options] ul[ui-sortable] li > div {
+ width: calc(100% - 30px);
+ display: inline-block;
+}
+#afGuiEditor [af-gui-edit-options] ul.af-gui-edit-options-deleted li > div {
+ text-decoration: line-through;
+}
+#afGuiEditor [af-gui-edit-options] ul[ui-sortable] li .btn-xs {
+ border: 0 none;
+}
+#afGuiEditor [af-gui-edit-options] h5 {
+ margin-left: 20px;
+}
},
controller: function($scope) {
var ts = $scope.ts = CRM.ts();
+ $scope.editingOptions = false;
+ var yesNo = [
+ {key: '1', label: ts('Yes')},
+ {key: '0', label: ts('No')}
+ ];
$scope.getEntity = function() {
return $scope.editor ? $scope.editor.getEntity($scope.entityName) : {};
};
- $scope.getDefn = function() {
+ $scope.getDefn = this.getDefn = function() {
return $scope.editor ? $scope.editor.getField($scope.getEntity().type, $scope.node.name) : {};
};
- $scope.getOptions = function() {
+ $scope.hasOptions = function() {
+ var inputType = $scope.getProp('input_type');
+ return _.contains(['CheckBox', 'Radio', 'Select'], inputType) && !(inputType === 'CheckBox' && !$scope.getDefn().options);
+ };
+
+ $scope.getOptions = this.getOptions = function() {
+ if ($scope.node.defn && $scope.node.defn.options) {
+ return $scope.node.defn.options;
+ }
+ return $scope.getDefn().options || ($scope.getProp('input_type') === 'CheckBox' ? null : yesNo);
+ };
+
+ $scope.select2Options = function() {
return {
- results: _.transform($scope.getProp('options'), function(result, val, key) {
- result.push({id: key, text: val});
+ results: _.transform($scope.getOptions(), function(result, opt) {
+ result.push({id: opt.key, text: opt.label});
}, [])
};
};
+ $scope.resetOptions = function() {
+ delete $scope.node.defn.options;
+ };
+
+ $scope.editOptions = function() {
+ $scope.editingOptions = true;
+ $('#afGuiEditor').addClass('af-gui-editing-options');
+ };
+
+ $scope.inputTypeCanBe = function(type) {
+ var defn = $scope.getDefn();
+ switch (type) {
+ case 'CheckBox':
+ case 'Radio':
+ case 'Select':
+ return !(!defn.options && defn.data_type !== 'Boolean');
+
+ case 'TextArea':
+ case 'RichTextEditor':
+ return (defn.data_type === 'Text' || defn.data_type === 'String');
+ }
+ return true;
+ };
+
+ // Returns a value from either the local field defn or the base defn
$scope.getProp = function(propName) {
var path = propName.split('.'),
item = path.pop(),
return drillDown($scope.getDefn(), path)[item];
};
+ // Checks for a value in either the local field defn or the base defn
+ $scope.propIsset = function(propName) {
+ var val = $scope.getProp(propName);
+ return !(typeof val === 'undefined' || val === null);
+ };
+
$scope.toggleRequired = function() {
getSet('required', !getSet('required'));
return false;
};
$scope.toggleHelp = function(position) {
- getSet('help_' + position, getSet('help_' + position) === null ? ($scope.getDefn()['help_' + position] || ts('Enter text')) : null);
+ getSet('help_' + position, $scope.propIsset('help_' + position) ? null : ($scope.getDefn()['help_' + position] || ts('Enter text')));
return false;
};
return _.wrap(propName, getSet);
};
- // Returns a reference to a path n-levels deep within an object
- function drillDown(parent, path) {
- var container = parent;
- _.each(path, function(level) {
- container[level] = container[level] || {};
- container = container[level];
- });
- return container;
- }
-
// Getter/setter callback
function getSet(propName, val) {
if (arguments.length > 1) {
}
return $scope.getProp(propName);
}
+ this.getSet = getSet;
+
+ this.setEditingOptions = function(val) {
+ $scope.editingOptions = val;
+ };
+
+ // Returns a reference to a path n-levels deep within an object
+ function drillDown(parent, path) {
+ var container = parent;
+ _.each(path, function(level) {
+ container[level] = container[level] || {};
+ container = container[level];
+ });
+ return container;
+ }
+ }
+ };
+ });
+ angular.module('afGuiEditor').directive('afGuiEditOptions', function() {
+ return {
+ restrict: 'A',
+ templateUrl: '~/afGuiEditor/editOptions.html',
+ scope: true,
+ require: '^^afGuiField',
+ link: function ($scope, element, attrs, afGuiField) {
+ $scope.field = afGuiField;
+ $scope.options = JSON.parse(angular.toJson(afGuiField.getOptions()));
+ var optionKeys = _.map($scope.options, 'key');
+ $scope.deletedOptions = _.filter(JSON.parse(angular.toJson(afGuiField.getDefn().options || [])), function(item) {
+ return !_.contains(optionKeys, item.key);
+ });
+ $scope.originalLabels = _.transform(afGuiField.getDefn().options || [], function(originalLabels, item) {
+ originalLabels[item.key] = item.label;
+ }, {});
+ },
+ controller: function ($scope) {
+ var ts = $scope.ts = CRM.ts();
+
+ $scope.deleteOption = function(option, $index) {
+ $scope.options.splice($index, 1);
+ $scope.deletedOptions.push(option);
+ };
+
+ $scope.restoreOption = function(option, $index) {
+ $scope.deletedOptions.splice($index, 1);
+ $scope.options.push(option);
+ };
+
+ $scope.save = function() {
+ $scope.field.getSet('options', JSON.parse(angular.toJson($scope.options)));
+ $scope.close();
+ };
+
+ $scope.close = function() {
+ $scope.field.setEditingOptions(false);
+ $('#afGuiEditor').removeClass('af-gui-editing-options');
+ };
}
};
});
};
});
+ // Connect bootstrap dropdown.js with angular
+ // Allows menu content to be conditionally rendered only if open
+ // This gives a large performance boost for a page with lots of menus
+ angular.module('afGuiEditor').directive('afGuiMenu', function() {
+ return {
+ restrict: 'A',
+ link: function($scope, element, attrs) {
+ $scope.menu = {};
+ element
+ .on('show.bs.dropdown', function() {
+ $scope.$apply(function() {
+ $scope.menu.open = true;
+ });
+ })
+ .on('hidden.bs.dropdown', function() {
+ $scope.$apply(function() {
+ $scope.menu.open = false;
+ });
+ });
+ }
+ };
+ });
+
// Editable titles using ngModel & html5 contenteditable
// Cribbed from ContactLayoutEditor
angular.module('afGuiEditor').directive("afGuiEditable", function() {
<div class="af-gui-bar" ng-if="node['#tag'] !== 'af-form'" ng-click="selectEntity()" >
<span ng-if="block.getNodeType(node) == 'fieldset'">{{ editor.getEntity(entityName).label }}</span>
<span>{{ node['#tag'] }}</span>
- <div class="form-inline pull-right">
+ <div class="form-inline pull-right" af-gui-menu>
<div class="btn-group btn-group-xs" role="group">
<button type="button" class="btn btn-default" ng-class="{active: opt === getLayout()}" ng-repeat="(opt, label) in layouts" ng-click="setLayout(opt)" title="{{ label }}">
<i class="af-gui-layout-icon {{ opt }}" ></i>
<button type="button" class="btn btn-default btn-xs dropdown-toggle af-gui-add-block-button" data-toggle="dropdown" title="{{ ts('Add block') }}">
<span><i class="crm-i fa-plus"></i></span>
</button>
- <ul class="dropdown-menu">
+ <ul class="dropdown-menu" ng-if="menu.open">
<li><a href ng-click="addBlock('div.af-block')">{{ ts('Add block') }}</a></li>
<li><a href ng-click="addBlock('p.af-text')">{{ ts('Add text box') }}</a></li>
<li><a href ng-click="addBlock('button.af-button.btn-primary', {'crm-icon': 'fa-check', 'ng-click': 'modelListCtrl.submit()'})">{{ ts('Add button') }}</a></li>
+ <li role="separator" class="divider"></li>
<li><a href ng-click="block.removeBlock(node)"><span class="text-danger">{{ ts('Delete this block') }}</span></a></li>
</ul>
</div>
</div>
<div class="af-gui-bar" ng-if="node['#tag'] === 'af-form'" >
- <div class="form-inline pull-right">
+ <div class="form-inline pull-right" af-gui-menu>
<button type="button" class="btn btn-default btn-sm dropdown-toggle af-gui-add-canvas-button" data-toggle="dropdown" title="{{ ts('Add block') }}">
<span>Add <i class="crm-i fa-plus"></i></span>
</button>
- <ul class="dropdown-menu">
+ <ul class="dropdown-menu" ng-if="menu.open">
<li><a href ng-click="addBlock('div.af-block')">{{ ts('Add block') }}</a></li>
<li><a href ng-click="addBlock('p.af-text')">{{ ts('Add text box') }}</a></li>
<li><a href ng-click="addBlock('button.af-button.btn-primary', {'crm-icon': 'fa-check', 'ng-click': 'modelListCtrl.submit()'})">{{ ts('Add button') }}</a></li>
<div ui-sortable="{handle: '.af-gui-bar', connectWith: '[ui-sortable]', cancel: 'input,textarea,button,select,option,a'}" ng-model="node['#children']" class="af-gui-layout {{ getLayout() }}">
<div ng-repeat="item in node['#children']" ng-show="block.getNodeType(item)">
<div ng-switch="block.getNodeType(item)">
- <div ng-switch-when="fieldset" af-gui-block="item" class="af-gui-element af-gui-block af-gui-fieldset af-gui-block-type-{{ item['#tag'] }}" 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-element af-gui-block af-gui-block-type-{{ item['#tag'] }}" entity-name="entityName" />
- <div ng-switch-when="field" af-gui-field="item" class="af-gui-element af-gui-field" entity-name="entityName" />
+ <div ng-switch-when="fieldset" af-gui-block="item" class="af-gui-block af-gui-fieldset af-gui-block-type-{{ item['#tag'] }}" 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 af-gui-block-type-{{ item['#tag'] }}" entity-name="entityName" />
+ <div ng-switch-when="field" af-gui-field="item" entity-name="entityName" />
<div ng-switch-when="text" af-gui-text="item" class="af-gui-element af-gui-text" />
<div ng-switch-when="button" af-gui-button="item" class="af-gui-element af-gui-button" />
</div>
<div class="af-gui-bar">
<div class="form-inline pull-right">
- <div class="btn-group pull-right">
+ <div class="btn-group pull-right" af-gui-menu>
<button type="button" class="btn btn-default btn-xs dropdown-toggle af-gui-add-block-button" data-toggle="dropdown" title="{{ ts('Configure') }}">
<span><i class="crm-i fa-gear"></i></span>
</button>
- <ul class="dropdown-menu">
+ <ul class="dropdown-menu" ng-if="menu.open">
<li title="{{ ts('Button style') }}">
<a href ng-click="$event.stopPropagation()" class="af-gui-field-select-in-dropdown" >
<select class="form-control {{ getSetStyle().replace('btn', 'text') }}" ng-model="getSetStyle" ng-model-options="{getterSetter: true}">
</select>
</a>
</li>
+ <li role="separator" class="divider"></li>
<li><a href ng-click="block.removeBlock(node)"><span class="text-danger">{{ ts('Delete this button') }}</span></a></li>
</ul>
</div>
--- /dev/null
+<div class="af-gui-edit-options-bar" >
+ <h4 class="pull-left">{{ ts('Edit option list') }}</h4>
+ <div class="btn-group-sm pull-right">
+ <button type="button" class="btn btn-default" ng-click="close()">
+ <i class="crm-i fa-times-circle"></i>
+ {{ ts('Cancel') }}
+ </button>
+ <button type="button" class="btn btn-success" ng-click="save()">
+ <i class="crm-i fa-check-circle"></i>
+ {{ ts('Save') }}
+ </button>
+ </div>
+</div>
+<ul ui-sortable="{connectWith: '.af-gui-edit-options-deleted', cancel: 'input,textarea,button,select,option,a,[contenteditable]'}" ng-model="options" class="af-gui-edit-options-enabled">
+ <li ng-repeat="option in options">
+ <div af-gui-editable ng-model="option.label" default-value="originalLabels[option.key]" >{{ option.label }}</div>
+ <button type="button" class="btn btn-danger-outline btn-xs pull-right" ng-click="deleteOption(option, $index)" title="{{ ts('Remove option') }}">
+ <i class="crm-i fa-trash"></i>
+ </button>
+ </li>
+</ul>
+<h5 ng-show="deletedOptions.length">{{ ts('Deleted options') }}</h5>
+<ul ng-if="deletedOptions.length" ui-sortable="{connectWith: '.af-gui-edit-options-enabled'}" ng-model="deletedOptions" class="af-gui-edit-options-deleted">
+ <li ng-repeat="option in deletedOptions">
+ <div>{{ option.label }}</div>
+ <button type="button" class="btn btn-success-outline btn-xs pull-right" ng-click="restoreOption(option, $index)" title="{{ ts('Restore option') }}">
+ <i class="crm-i fa-arrow-circle-o-up"></i>
+ </button>
+ </li>
+</ul>
-<div class="af-gui-bar" title="{{ getEntity().label + ': ' + getDefn().title }}">
- <div class="form-inline pull-right">
-
- <div class="btn-group">
- <button type="button" class="btn btn-default btn-xs dropdown-toggle af-gui-add-block-button" data-toggle="dropdown" title="{{ ts('Configure') }}">
- <span><i class="crm-i fa-gear"></i></span>
- </button>
- <ul class="dropdown-menu dropdown-menu-right">
- <li title="{{ ts('Field type') }}">
- <a href ng-click="$event.stopPropagation()" class="af-gui-field-select-in-dropdown">
- <select class="form-control" ng-model="getSet('input_type')" ng-model-options="{getterSetter: true}">
- <option ng-repeat="(opt, label) in editor.meta.widgets" value="{{ opt }}">{{ label }}</option>
- </select>
- </a>
- </li>
- <li title="{{ ts('Require this field') }}">
- <a href ng-click="toggleRequired(); $event.stopPropagation();">
- <i class="crm-i" ng-class="{'fa-square-o': !getProp('required'), 'fa-check-square-o': getProp('required')}"></i>
- {{ ts('Required') }}
- </a>
- </li>
- <li title="{{ ts('Show help text above this field') }}">
- <a href ng-click="toggleHelp('pre'); $event.stopPropagation();">
- <i class="crm-i" ng-class="{'fa-square-o': !getProp('help_pre'), 'fa-check-square-o': getProp('help_pre')}"></i>
- {{ ts('Pre help text') }}
- </a>
- </li>
- <li title="{{ ts('Show help text below this field') }}">
- <a href ng-click="toggleHelp('post'); $event.stopPropagation();">
- <i class="crm-i" ng-class="{'fa-square-o': !getProp('help_post'), 'fa-check-square-o': getProp('help_post')}"></i>
- {{ ts('Post help text') }}
- </a>
- </li>
- <li title="{{ ts('Remove field from form') }}">
- <a href ng-click="block.removeBlock(node)"><span class="text-danger">{{ ts('Delete this field') }}</span></a>
- </li>
- </ul>
+<div af-gui-edit-options ng-if="editingOptions"></div>
+<div ng-if="!editingOptions" class="af-gui-element af-gui-field" >
+ <div class="af-gui-bar" title="{{ getEntity().label + ': ' + getDefn().title }}">
+ <div class="form-inline pull-right">
+ <div class="btn-group" af-gui-menu >
+ <button type="button" class="btn btn-default btn-xs dropdown-toggle af-gui-add-block-button" data-toggle="dropdown" title="{{ ts('Configure') }}">
+ <span><i class="crm-i fa-gear"></i></span>
+ </button>
+ <ul class="dropdown-menu dropdown-menu-right" ng-if="menu.open">
+ <li title="{{ ts('Field type') }}">
+ <a href ng-click="$event.stopPropagation()" class="af-gui-field-select-in-dropdown">
+ <select class="form-control" ng-model="getSet('input_type')" ng-model-options="{getterSetter: true}">
+ <option ng-repeat="(type, label) in editor.meta.inputType" value="{{ type }}" ng-if="inputTypeCanBe(type)">{{ label }}</option>
+ </select>
+ </a>
+ </li>
+ <li title="{{ ts('Require this field') }}">
+ <a href ng-click="toggleRequired(); $event.stopPropagation();">
+ <i class="crm-i" ng-class="{'fa-square-o': !getProp('required'), 'fa-check-square-o': getProp('required')}"></i>
+ {{ ts('Required') }}
+ </a>
+ </li>
+ <li title="{{ ts('Show help text above this field') }}">
+ <a href ng-click="toggleHelp('pre'); $event.stopPropagation();">
+ <i class="crm-i" ng-class="{'fa-square-o': !propIsset('help_pre'), 'fa-check-square-o': propIsset('help_pre')}"></i>
+ {{ ts('Pre help text') }}
+ </a>
+ </li>
+ <li title="{{ ts('Show help text below this field') }}">
+ <a href ng-click="toggleHelp('post'); $event.stopPropagation();">
+ <i class="crm-i" ng-class="{'fa-square-o': !propIsset('help_post'), 'fa-check-square-o': propIsset('help_post')}"></i>
+ {{ ts('Post help text') }}
+ </a>
+ </li>
+ <li role="separator" class="divider" ng-if="hasOptions()"></li>
+ <li title="{{ ts('Reset the option list for this field') }}" ng-if="hasOptions()" ng-click="$event.stopPropagation()">
+ <a href ng-click="resetOptions()">
+ <i class="crm-i fa-{{ node.defn.options ? '' : 'check-' }}circle-o"></i>
+ {{ ts('Use default option list') }}
+ </a>
+ </li>
+ <li title="{{ ts('Customize the option list for this field') }}" ng-if="hasOptions()">
+ <a href ng-click="editOptions()">
+ <i class="crm-i fa-{{ !node.defn.options ? '' : 'check-' }}circle-o"></i>
+ {{ ts('Customize field options') }}
+ </a>
+ </li>
+ <li role="separator" class="divider"></li>
+ <li title="{{ ts('Remove field from form') }}">
+ <a href ng-click="block.removeBlock(node)"><span class="text-danger">{{ ts('Delete this field') }}</span></a>
+ </li>
+ </ul>
+ </div>
</div>
</div>
-</div>
-<label ng-class="{'af-gui-field-required': getProp('required')}" class="af-gui-node-title">
- <span af-gui-editable ng-model="node.defn.title" default-value="getDefn().title">{{ getProp('title') }}</span>
-</label>
-<div class="af-gui-field-help" ng-if="getProp('help_pre') !== null">
- <span af-gui-editable ng-model="node.defn.help_pre" default-value="getDefn().help_pre">{{ getProp('help_pre') }}</span>
-</div>
-<div class="af-gui-field-widget af-gui-field-widget-{{ getProp('input_type').toLowerCase() }}" ng-include="'~/afGuiEditor/widgets/' + getProp('input_type') + '.html'"></div>
-<div class="af-gui-field-help" ng-if="getProp('help_post') !== null">
- <span af-gui-editable ng-model="node.defn.help_post" default-value="getDefn().help_post">{{ getProp('help_post') }}</span>
+ <label ng-class="{'af-gui-field-required': getProp('required')}" class="af-gui-node-title">
+ <span af-gui-editable ng-model="node.defn.title" default-value="getDefn().title">{{ getProp('title') }}</span>
+ </label>
+ <div class="af-gui-field-help" ng-if="propIsset('help_pre')">
+ <span af-gui-editable ng-model="node.defn.help_pre" default-value="getDefn().help_pre">{{ getProp('help_pre') }}</span>
+ </div>
+ <div class="af-gui-field-input af-gui-field-input-type-{{ getProp('input_type').toLowerCase() }}" ng-include="'~/afGuiEditor/inputType/' + getProp('input_type') + '.html'"></div>
+ <div class="af-gui-field-help" ng-if="propIsset('help_post')">
+ <span af-gui-editable ng-model="node.defn.help_post" default-value="getDefn().help_post">{{ getProp('help_post') }}</span>
+ </div>
</div>
--- /dev/null
+<ul class="crm-checkbox-list" id="{{ fieldId }}" ng-if="getOptions()">
+ <li ng-repeat="opt in getOptions()" >
+ <input type="checkbox" disabled />
+ <label>{{ opt.label }}</label>
+ </li>
+</ul>
+<input type="checkbox" disabled ng-if="!getOptions()" />
--- /dev/null
+<div class="form-inline">
+ <input autocomplete="off" class="form-control crm-form-date crm-placeholder-icon" placeholder="" ng-model="getSet('input_attrs.placeholder')" ng-model-options="{getterSetter: true}" type="text" />
+ <span class="addon fa fa-calendar"></span>
+ <input autocomplete="off" ng-if="getProp('input_attrs.time')" placeholder="" class="form-control crm-form-time crm-placeholder-icon" ng-model="getSet('input_attrs.timePlaceholder')" ng-model-options="{getterSetter: true}" type="text" />
+</div>
--- /dev/null
+<input autocomplete="off" class="form-control" ng-model="getSet('input_attrs.placeholder')" ng-model-options="{getterSetter: true}" type="text" />
--- /dev/null
+<div class="form-inline">
+ <label ng-repeat="opt in getOptions()" class="radio" >
+ <input class="crm-form-radio" type="radio" disabled />
+ {{ opt.label }}
+ </label>
+</div>
--- /dev/null
+<textarea autocomplete="off" class="crm-form-textarea" disabled ></textarea>
--- /dev/null
+<input autocomplete="off" crm-ui-select="{data: select2Options, multiple: defn.input_attrs.multiple}" />
--- /dev/null
+<input autocomplete="off" class="form-control" ng-model="getSet('input_attrs.placeholder')" ng-model-options="{getterSetter: true}" type="text" />
--- /dev/null
+<textarea autocomplete="off" class="crm-form-textarea" disabled ></textarea>
<div class="af-gui-bar">
<div class="form-inline pull-right">
- <div class="btn-group pull-right">
+ <div class="btn-group pull-right" af-gui-menu>
<button type="button" class="btn btn-default btn-xs dropdown-toggle af-gui-add-block-button" data-toggle="dropdown" title="{{ ts('Configure') }}">
<span><i class="crm-i fa-gear"></i></span>
</button>
- <ul class="dropdown-menu">
+ <ul class="dropdown-menu" ng-if="menu.open">
<li title="{{ ts('Text style') }}">
<a href ng-click="$event.stopPropagation()" class="af-gui-field-select-in-dropdown">
<select class="form-control" ng-model="node['#tag']">
</div>
</a>
</li>
+ <li role="separator" class="divider"></li>
<li>
<a href ng-click="block.removeBlock(node)"><span class="text-danger">{{ ts('Delete this text') }}</span></a>
</li>
+++ /dev/null
-<ul class="crm-checkbox-list" id="{{ fieldId }}" ng-if="getProp('options')">
- <li ng-repeat="(key, val) in getProp('options')" >
- <input type="checkbox" disabled />
- <label >{{ val }}</label>
- </li>
-</ul>
-<input type="checkbox" disabled ng-if="!getProp('options')" />
+++ /dev/null
-<div class="form-inline">
- <input class="form-control crm-form-date" ng-model="getSet('input_attrs.placeholder')" ng-model-options="{getterSetter: true}" type="text" />
- <span class="addon fa fa-calendar"></span>
- <input ng-if="getProp('input_attrs.time')" class="form-control crm-form-time" ng-model="getSet('input_attrs.timePlaceholder')" ng-model-options="{getterSetter: true}" type="text" />
-</div>
+++ /dev/null
-<input class="form-control" ng-model="getSet('input_attrs.placeholder')" ng-model-options="{getterSetter: true}" type="text" />
+++ /dev/null
-<label ng-repeat="(key, val) in getProp('options')" >
- <input class="crm-form-radio" type="radio" disabled value="{{ key }}" />
- {{ val }}
-</label>
+++ /dev/null
-<textarea class="crm-form-textarea" disabled ></textarea>
+++ /dev/null
-<input crm-ui-select="{data: getOptions, multiple: defn.input_attrs.multiple}" />
+++ /dev/null
-<input class="form-control" ng-model="getSet('input_attrs.placeholder')" ng-model-options="{getterSetter: true}" type="text" />
+++ /dev/null
-<textarea class="crm-form-textarea" disabled ></textarea>
<af-field name="first_name" />
<af-field name="last_name" />
</div>
- <af-field name="gender_id" />
+ <af-field name="gender_id" defn="{options: [{key: 1, label: 'Girl'}, {key: 2, label: 'Boy'}, {key: 3, label: 'Other'}]}"/>
<af-field name="constituent_information.Marital_Status" />
<af-field name="constituent_information.Marriage_Date" />
<af-field name="constituent_information.Most_Important_Issue" />