This adds an "afform_default" property to the field definition.
It does not use the "default_value" property from getFields because that's more
to do with Civi's schema and often not appropriate for a form.
Fixes dev/core#2734
top: 4px;
}
.crm-container ul.crm-checkbox-list li label {
- display: block;
+ display: block !important;
padding: 2px 0 2px 22px;
margin: 0;
word-break: break-all;
text-align: right;
}
-#afGuiEditor .af-gui-bar {
+#afGuiEditor-canvas .af-gui-bar {
height: 22px;
width: 100%;
- opacity: 0;
transition: opacity .2s;
position:relative;
font-family: "Courier New", Courier, monospace;
font-size: 12px;
}
-#afGuiEditor [ui-sortable] .af-gui-bar {
+#afGuiEditor-canvas:not(.af-gui-menu-open) .af-gui-bar {
+ opacity: 0;
+}
+#afGuiEditor-canvas [ui-sortable] .af-gui-bar {
cursor: move;
background-color: #f2f2f2;
position: absolute;
opacity: 1;
transition: opacity .2s;
}
-#afGuiEditor-canvas .af-gui-dragtarget > .af-gui-bar {
+#afGuiEditor #afGuiEditor-canvas .af-gui-dragtarget > .af-gui-bar {
background-color: #d7e6ff;
opacity: 1;
transition: opacity .1s;
margin-bottom: 20px;
}
-#afGuiEditor .af-gui-container:hover,
+#afGuiEditor-canvas:not(.af-gui-menu-open) .af-gui-container:hover,
.af-gui-dragging #afGuiEditor .af-gui-container {
border: 2px dashed #757575;
}
width: 100%;
right: 0;
}
-#afGuiEditor .af-gui-field-input-type-select .input-group .dropdown-menu a {
- cursor: default;
-}
#afGuiEditor .af-gui-text-h1 {
font-weight: bolder;
.on('show.bs.dropdown', function() {
$scope.$apply(function() {
$scope.menu.open = true;
+ element.closest('#afGuiEditor-canvas').addClass('af-gui-menu-open');
});
})
.on('hidden.bs.dropdown', function() {
$scope.$apply(function() {
$scope.menu.open = false;
+ element.closest('#afGuiEditor-canvas').removeClass('af-gui-menu-open');
});
});
}
// Cribbed from the Api4 Explorer
angular.module('afGuiEditor').directive('afGuiFieldValue', function(afGui) {
return {
- scope: {
- field: '=afGuiFieldValue'
+ bindToController: {
+ field: '<afGuiFieldValue'
},
require: {
ngModel: 'ngModel',
editor: '^^afGuiEditor'
},
- link: function (scope, element, attrs, ctrl) {
- var ts = scope.ts = CRM.ts('org.civicrm.afform_admin'),
+ controller: function ($element, $timeout) {
+ var ts = CRM.ts('org.civicrm.afform_admin'),
+ ctrl = this,
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 options,
- $el = $(element),
+ $el = $($element),
inputType = field.input_type,
dataType = field.data_type;
multi = field.serialize || dataType === 'Array';
+ // Allow input_type to override dataType
+ if (inputType) {
+ multi = (dataType !== 'Boolean' &&
+ (inputType === 'CheckBox' || (field.input_attrs && field.input_attrs.multiple)));
+ }
if (inputType === 'Date') {
$el.crmDatepicker({time: (field.input_attrs && field.input_attrs.time) || false});
}
return list;
};
- // Copied from ng-list
- ctrl.ngModel.$parsers.push(parseList);
- ctrl.ngModel.$formatters.push(function(value) {
- return _.isArray(value) ? value.join(', ') : value;
- });
+ this.$onInit = function() {
+ // Copied from ng-list
+ ctrl.ngModel.$parsers.push(parseList);
+ ctrl.ngModel.$formatters.push(function(value) {
+ return _.isArray(value) ? value.join(',') : value;
+ });
- // Copied from ng-list
- ctrl.ngModel.$isEmpty = function(value) {
- return !value || !value.length;
- };
+ // Copied from ng-list
+ ctrl.ngModel.$isEmpty = function(value) {
+ return !value || !value.length;
+ };
- scope.$watchCollection('field', function(field) {
- destroyWidget();
- if (field) {
- makeWidget(field);
- }
- });
+ $timeout(function() {
+ makeWidget(ctrl.field);
+ });
+ };
}
};
});
</div>
</li>
<li>
- <a href ng-click="toggleRequired(); $event.stopPropagation();" title="{{:: ts('Require this field') }}">
+ <a href ng-click="toggleRequired(); $event.stopPropagation(); $event.target.blur();" title="{{:: ts('Require this field') }}">
<i class="crm-i fa-{{ getProp('required') ? 'check-' : '' }}square-o"></i>
{{:: ts('Required') }}
</a>
</li>
<li>
- <a href ng-click="toggleLabel(); $event.stopPropagation();" title="{{:: ts('Show field label') }}">
+ <a href ng-click="toggleDefaultValue(); $event.stopPropagation(); $event.target.blur();" title="{{:: ts('Pre-fill this field with a value') }}">
+ <i class="crm-i fa-{{ $ctrl.hasDefaultValue ? 'check-' : '' }}square-o"></i>
+ {{:: ts('Default value') }}
+ </a>
+</li>
+<li ng-if="$ctrl.hasDefaultValue">
+ <div ng-click="$event.stopPropagation()" class="af-gui-field-select-in-dropdown form-inline">
+ <input class="form-control" af-gui-field-value="$ctrl.fieldDefn" ng-model="getSet('afform_default')" ng-model-options="{getterSetter: true}" >
+ </div>
+</li>
+<li>
+ <a href ng-click="toggleLabel(); $event.stopPropagation(); $event.target.blur();" title="{{:: ts('Show field label') }}">
<i class="crm-i fa-{{ $ctrl.node.defn.label === false ? '' : 'check-' }}square-o"></i>
{{:: ts('Label') }}
</a>
</li>
<li>
- <a href ng-click="toggleHelp('pre'); $event.stopPropagation();" title="{{:: ts('Show help text above this field') }}">
+ <a href ng-click="toggleHelp('pre'); $event.stopPropagation(); $event.target.blur();" title="{{:: ts('Show help text above this field') }}">
<i class="crm-i fa-{{ propIsset('help_pre') ? 'check-' : '' }}square-o"></i>
{{:: ts('Pre help text') }}
</a>
</li>
<li>
- <a href ng-click="toggleHelp('post'); $event.stopPropagation();" title="{{:: ts('Show help text below this field') }}">
+ <a href ng-click="toggleHelp('post'); $event.stopPropagation(); $event.target.blur();" title="{{:: ts('Show help text below this field') }}">
<i class="crm-i fa-{{ propIsset('help_post') ? 'check-' : '' }}square-o" ></i>
{{:: ts('Post help text') }}
</a>
editor: '^^afGuiEditor',
container: '^^afGuiContainer'
},
- controller: function($scope, afGui) {
+ controller: function($scope, afGui, $timeout) {
var ts = $scope.ts = CRM.ts('org.civicrm.afform_admin'),
- ctrl = this;
-
- $scope.editingOptions = false;
- var yesNo = [
- {id: '1', label: ts('Yes')},
- {id: '0', label: ts('No')}
- ],
+ ctrl = this,
entityRefOptions = [],
singleElement = [''],
// When search-by-range is enabled the second element gets a suffix for some properties like "placeholder2"
rangeElements = ['', '2'],
dateRangeElements = ['1', '2'],
relativeDatesWithPickRange = CRM.afGuiEditor.dateRanges,
- relativeDatesWithoutPickRange = relativeDatesWithPickRange.slice(1);
+ relativeDatesWithoutPickRange = relativeDatesWithPickRange.slice(1),
+ yesNo = [
+ {id: '1', label: ts('Yes')},
+ {id: '0', label: ts('No')}
+ ];
+ $scope.editingOptions = false;
this.$onInit = function() {
+ ctrl.hasDefaultValue = !!getSet('afform_default');
+ ctrl.fieldDefn = angular.extend({}, ctrl.getDefn(), ctrl.node.defn);
ctrl.inputTypes = _.transform(_.cloneDeep(afGui.meta.inputType), function(inputTypes, type) {
if (inputTypeCanBe(type.name)) {
// Change labels for EntityRef fields
label: ts('Untitled'),
required: false
};
- defn.input_attrs = _.isEmpty(defn.input_attrs) ? {} : defn.input_attrs;
+ if (_.isEmpty(defn.input_attrs)) {
+ defn.input_attrs = {};
+ }
return defn;
};
$scope.toggleRequired = function() {
getSet('required', !getSet('required'));
- return false;
};
$scope.toggleHelp = function(position) {
getSet('help_' + position, $scope.propIsset('help_' + position) ? null : (ctrl.getDefn()['help_' + position] || ts('Enter text')));
- return false;
+ };
+
+ function defaultValueShouldBeArray() {
+ return ($scope.getProp('data_type') !== 'Boolean' &&
+ ($scope.getProp('input_type') === 'CheckBox' || $scope.getProp('input_attrs.multiple')));
+ }
+
+
+ $scope.toggleDefaultValue = function() {
+ if (ctrl.hasDefaultValue) {
+ getSet('afform_default', undefined);
+ ctrl.hasDefaultValue = false;
+ } else {
+ ctrl.hasDefaultValue = true;
+ }
+ };
+
+ $scope.defaultValueContains = function(val) {
+ var defaultVal = getSet('afform_default');
+ return defaultVal === val || (_.isArray(defaultVal) && _.includes(defaultVal, val));
+ };
+
+ $scope.toggleDefaultValueItem = function(val) {
+ if (defaultValueShouldBeArray()) {
+ if (!_.isArray(getSet('afform_default'))) {
+ ctrl.node.defn.afform_default = [];
+ }
+ if (_.includes(ctrl.node.defn.afform_default, val)) {
+ var newVal = _.without(ctrl.node.defn.afform_default, val);
+ getSet('afform_default', newVal.length ? newVal : undefined);
+ ctrl.hasDefaultValue = !!newVal.length;
+ } else {
+ ctrl.node.defn.afform_default.push(val);
+ ctrl.hasDefaultValue = true;
+ }
+ } else if (getSet('afform_default') === val) {
+ getSet('afform_default', undefined);
+ ctrl.hasDefaultValue = false;
+ } else {
+ getSet('afform_default', val);
+ ctrl.hasDefaultValue = true;
+ }
};
// Getter/setter for definition props
clearOut(ctrl.node, ['defn'].concat(path));
}
// When changing input_type
- if (propName === 'input_type' && ctrl.node.defn && ctrl.node.defn.search_range && !ctrl.canBeRange()) {
- delete ctrl.node.defn.search_range;
+ if (propName === 'input_type') {
+ if (ctrl.node.defn && ctrl.node.defn.search_range && !ctrl.canBeRange()) {
+ delete ctrl.node.defn.search_range;
+ clearOut(ctrl.node, ['defn']);
+ }
+ if (ctrl.node.defn && ctrl.node.defn.input_attrs && 'multiple' in ctrl.node.defn.input_attrs && !ctrl.canBeMultiple()) {
+ delete ctrl.node.defn.input_attrs.multiple;
+ clearOut(ctrl.node, ['defn', 'input_attrs']);
+ }
+ }
+ ctrl.fieldDefn = angular.extend({}, ctrl.getDefn(), ctrl.node.defn);
+
+ // When changing the multiple property, force-reset the default value widget
+ if (ctrl.hasDefaultValue && _.includes(['input_type', 'input_attrs.multiple'], propName)) {
+ ctrl.hasDefaultValue = false;
+ if (!defaultValueShouldBeArray() && _.isArray(getSet('afform_default'))) {
+ ctrl.node.defn.afform_default = ctrl.node.defn.afform_default[0];
+ } else if (defaultValueShouldBeArray() && _.isString(getSet('afform_default')) && ctrl.node.defn.afform_default.length) {
+ ctrl.node.defn.afform_default = ctrl.node.defn.afform_default.split(',');
+ }
+ $timeout(function() {
+ ctrl.hasDefaultValue = true;
+ });
}
return val;
}
-<ul class="crm-checkbox-list" ng-if="$ctrl.getOptions()">
+<ul class="crm-checkbox-list" ng-if="$ctrl.getOptions()" title="{{:: ts('Set default value') }}">
<li ng-repeat="opt in $ctrl.getOptions()" >
- <input type="checkbox" disabled >
+ <input type="checkbox" ng-checked="defaultValueContains(opt.id)" ng-click="toggleDefaultValueItem(opt.id)" >
<label>{{ opt.label }}</label>
</li>
</ul>
-<div class="form-inline">
+<div class="form-inline" title="{{:: ts('Set default value') }}">
<label ng-repeat="opt in $ctrl.getOptions()" class="radio" >
- <input class="crm-form-radio" type="radio" disabled />
+ <input class="crm-form-radio" type="radio" ng-checked="defaultValueContains(opt.id)" ng-click="toggleDefaultValueItem(opt.id)" >
{{ opt.label }}
</label>
</div>
<input autocomplete="off" class="form-control" placeholder="{{:: ts('Select') }}" title="{{:: ts('Click to add placeholder text') }}" ng-model="getSet('input_attrs.placeholder' + i)" ng-model-options="{getterSetter: true}" type="text" />
<div class="input-group-btn" af-gui-menu>
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="crm-i fa-caret-down"></i></button>
- <ul class="dropdown-menu" ng-if="menu.open">
+ <ul class="dropdown-menu" ng-if="menu.open" title="{{:: ts('Set default value') }}">
<li ng-repeat="opt in $ctrl.getOptions()" >
- <a href>{{ opt.label }}</a>
+ <a href ng-click="toggleDefaultValueItem(opt.id); $event.stopPropagation(); $event.target.blur();">
+ <i class="crm-i fa-{{defaultValueContains(opt.id) ? 'check-' : ''}}circle-o"></i>
+ {{ opt.label }}
+ </a>
</li>
</ul>
</div>
fieldName: '@name',
defn: '='
},
- controller: function($scope, $element, crmApi4) {
+ controller: function($scope, $element, crmApi4, $timeout) {
var ts = $scope.ts = CRM.ts('org.civicrm.afform'),
ctrl = this,
boolOptions = [{id: true, label: ts('Yes')}, {id: false, label: ts('No')}],
});
}
+ // Set default value
+ if (ctrl.defn.afform_default) {
+ // Wait for parent controllers to initialize
+ $timeout(function() {
+ $scope.dataProvider.getFieldData()[ctrl.fieldName] = ctrl.defn.afform_default;
+ });
+ }
+
};
$scope.getOptions = function () {