Merge pull request #19639 from eileenmcnaughton/test_test
[civicrm-core.git] / ext / afform / admin / ang / afGuiEditor / elements / afGuiContainer.component.js
1 // https://civicrm.org/licensing
2 (function(angular, $, _) {
3 "use strict";
4
5 angular.module('afGuiEditor').component('afGuiContainer', {
6 templateUrl: '~/afGuiEditor/elements/afGuiContainer.html',
7 bindings: {
8 node: '<',
9 join: '<',
10 entityName: '<',
11 deleteThis: '&'
12 },
13 require: {editor: '^^afGuiEditor'},
14 controller: function($scope, crmApi4, dialogService, afGui) {
15 var ts = $scope.ts = CRM.ts('org.civicrm.afform_admin'),
16 ctrl = this;
17
18 this.$onInit = function() {
19 if (ctrl.node['#tag'] && ((ctrl.node['#tag'] in afGui.meta.blocks) || ctrl.join)) {
20 var blockNode = getBlockNode(),
21 blockTag = blockNode ? blockNode['#tag'] : null;
22 if (blockTag && (blockTag in afGui.meta.blocks) && !afGui.meta.blocks[blockTag].layout) {
23 ctrl.loading = true;
24 crmApi4('Afform', 'loadAdminData', {
25 definition: {name: afGui.meta.blocks[blockTag].name},
26 skipEntities: _.transform(afGui.meta.entities, function(result, entity, entityName) {
27 if (entity.fields) {
28 result.push(entityName);
29 }
30 }, [])
31 }, 0).then(function(data) {
32 afGui.addMeta(data);
33 initializeBlockContainer();
34 ctrl.loading = false;
35 });
36 }
37 initializeBlockContainer();
38 }
39 };
40
41 $scope.isSelectedFieldset = function(entityName) {
42 return entityName === ctrl.editor.getSelectedEntityName();
43 };
44
45 $scope.selectEntity = function() {
46 if (ctrl.node['af-fieldset']) {
47 ctrl.editor.selectEntity(ctrl.node['af-fieldset']);
48 }
49 };
50
51 $scope.tags = {
52 div: ts('Container'),
53 fieldset: ts('Fieldset')
54 };
55
56 // Block settings
57 var block = {};
58 $scope.block = null;
59
60 $scope.getSetChildren = function(val) {
61 var collection = block.layout || (ctrl.node && ctrl.node['#children']);
62 return arguments.length ? (collection = val) : collection;
63 };
64
65 $scope.isRepeatable = function() {
66 return ctrl.node['af-fieldset'] || (block.directive && afGui.meta.blocks[block.directive].repeat) || ctrl.join;
67 };
68
69 $scope.toggleRepeat = function() {
70 if ('af-repeat' in ctrl.node) {
71 delete ctrl.node.max;
72 delete ctrl.node.min;
73 delete ctrl.node['af-repeat'];
74 delete ctrl.node['add-icon'];
75 } else {
76 ctrl.node.min = '1';
77 ctrl.node['af-repeat'] = ts('Add');
78 }
79 };
80
81 $scope.getSetMin = function(val) {
82 if (arguments.length) {
83 if (ctrl.node.max && val > parseInt(ctrl.node.max, 10)) {
84 ctrl.node.max = '' + val;
85 }
86 if (!val) {
87 delete ctrl.node.min;
88 }
89 else {
90 ctrl.node.min = '' + val;
91 }
92 }
93 return ctrl.node.min ? parseInt(ctrl.node.min, 10) : null;
94 };
95
96 $scope.getSetMax = function(val) {
97 if (arguments.length) {
98 if (ctrl.node.min && val && val < parseInt(ctrl.node.min, 10)) {
99 ctrl.node.min = '' + val;
100 }
101 if (typeof val !== 'number') {
102 delete ctrl.node.max;
103 }
104 else {
105 ctrl.node.max = '' + val;
106 }
107 }
108 return ctrl.node.max ? parseInt(ctrl.node.max, 10) : null;
109 };
110
111 $scope.pickAddIcon = function() {
112 afGui.pickIcon().then(function(val) {
113 ctrl.node['add-icon'] = val;
114 });
115 };
116
117 function getBlockNode() {
118 return !ctrl.join ? ctrl.node : (ctrl.node['#children'] && ctrl.node['#children'].length === 1 ? ctrl.node['#children'][0] : null);
119 }
120
121 function setBlockDirective(directive) {
122 if (ctrl.join) {
123 ctrl.node['#children'] = [{'#tag': directive}];
124 } else {
125 delete ctrl.node['#children'];
126 delete ctrl.node['class'];
127 ctrl.node['#tag'] = directive;
128 }
129 }
130
131 function overrideBlockContents(layout) {
132 ctrl.node['#children'] = layout || [];
133 if (!ctrl.join) {
134 ctrl.node['#tag'] = 'div';
135 ctrl.node['class'] = 'af-container';
136 }
137 block.layout = block.directive = null;
138 }
139
140 $scope.layouts = {
141 'af-layout-rows': ts('Contents display as rows'),
142 'af-layout-cols': ts('Contents are evenly-spaced columns'),
143 'af-layout-inline': ts('Contents are arranged inline')
144 };
145
146 $scope.getLayout = function() {
147 if (!ctrl.node) {
148 return '';
149 }
150 return _.intersection(afGui.splitClass(ctrl.node['class']), _.keys($scope.layouts))[0] || 'af-layout-rows';
151 };
152
153 $scope.setLayout = function(val) {
154 var classes = ['af-container'];
155 if (val !== 'af-layout-rows') {
156 classes.push(val);
157 }
158 afGui.modifyClasses(ctrl.node, _.keys($scope.layouts), classes);
159 };
160
161 $scope.selectBlockDirective = function() {
162 if (block.directive) {
163 block.layout = _.cloneDeep(afGui.meta.blocks[block.directive].layout);
164 block.original = block.directive;
165 setBlockDirective(block.directive);
166 }
167 else {
168 overrideBlockContents(block.layout);
169 }
170 };
171
172 function initializeBlockContainer() {
173
174 // Cancel the below $watch expressions if already set
175 _.each(block.listeners, function(deregister) {
176 deregister();
177 });
178
179 block = $scope.block = {
180 directive: null,
181 layout: null,
182 original: null,
183 options: [],
184 listeners: []
185 };
186
187 _.each(afGui.meta.blocks, function(blockInfo, directive) {
188 if (directive === ctrl.node['#tag'] || (blockInfo.join && blockInfo.join === ctrl.getFieldEntityType())) {
189 block.options.push({
190 id: directive,
191 text: blockInfo.title
192 });
193 }
194 });
195
196 if (getBlockNode() && getBlockNode()['#tag'] in afGui.meta.blocks) {
197 block.directive = block.original = getBlockNode()['#tag'];
198 block.layout = _.cloneDeep(afGui.meta.blocks[block.directive].layout);
199 }
200
201 block.listeners.push($scope.$watch('block.layout', function (layout, oldVal) {
202 if (block.directive && layout && layout !== oldVal && !angular.equals(layout, afGui.meta.blocks[block.directive].layout)) {
203 overrideBlockContents(block.layout);
204 }
205 }, true));
206 }
207
208 $scope.saveBlock = function() {
209 var options = CRM.utils.adjustDialogDefaults({
210 width: '500px',
211 height: '300px',
212 autoOpen: false,
213 title: ts('Save block')
214 });
215 var model = {
216 title: '',
217 name: null,
218 type: 'block',
219 layout: ctrl.node['#children']
220 };
221 if (ctrl.join) {
222 model.join = ctrl.join;
223 }
224 if ($scope.block && $scope.block.original) {
225 model.title = afGui.meta.blocks[$scope.block.original].title;
226 model.name = afGui.meta.blocks[$scope.block.original].name;
227 model.block = afGui.meta.blocks[$scope.block.original].block;
228 }
229 else {
230 model.block = ctrl.getFieldEntityType();
231 }
232 dialogService.open('saveBlockDialog', '~/afGuiEditor/saveBlock.html', model, options)
233 .then(function(block) {
234 afGui.meta.blocks[block.directive_name] = block;
235 setBlockDirective(block.directive_name);
236 initializeBlockContainer();
237 });
238 };
239
240 this.node = ctrl.node;
241
242 this.getNodeType = function(node) {
243 if (!node || !node['#tag']) {
244 return null;
245 }
246 if (node['#tag'] === 'af-field') {
247 return 'field';
248 }
249 if ('af-fieldset' in node) {
250 return 'fieldset';
251 }
252 if (node['af-join']) {
253 return 'join';
254 }
255 if (node['#tag'] && node['#tag'] in afGui.meta.blocks) {
256 return 'container';
257 }
258 if (node['#tag'] && (node['#tag'].slice(0, 19) === 'crm-search-display-')) {
259 return 'searchDisplay';
260 }
261 var classes = afGui.splitClass(node['class']),
262 types = ['af-container', 'af-text', 'af-button', 'af-markup'],
263 type = _.intersection(types, classes);
264 return type.length ? type[0].replace('af-', '') : null;
265 };
266
267 this.removeElement = function(element) {
268 afGui.removeRecursive($scope.getSetChildren(), {$$hashKey: element.$$hashKey});
269 };
270
271 this.getEntityName = function() {
272 return ctrl.entityName ? ctrl.entityName.split('-join-')[0] : null;
273 };
274
275 // Returns the primary entity type for this container e.g. "Contact"
276 this.getMainEntityType = function() {
277 return ctrl.editor && ctrl.editor.getEntity(ctrl.getEntityName()).type;
278 };
279
280 // Returns the entity type for fields within this conainer (join entity type if this is a join, else the primary entity type)
281 this.getFieldEntityType = function(fieldName) {
282 // If entityName is declared for this fieldset, return entity-type or join-type
283 if (ctrl.entityName) {
284 var joinType = ctrl.entityName.split('-join-');
285 return joinType[1] || (ctrl.editor && ctrl.editor.getEntity(joinType[0]).type);
286 }
287 // If entityName is not declared, this field belongs to a search
288 var entityType,
289 prefix = _.includes(fieldName, '.') ? fieldName.split('.')[0] : null;
290 _.each(afGui.meta.searchDisplays, function(searchDisplay) {
291 if (prefix) {
292 _.each(searchDisplay['saved_search.api_params'].join, function(join) {
293 var joinInfo = join[0].split(' AS ');
294 if (prefix === joinInfo[1]) {
295 entityType = joinInfo[0];
296 return false;
297 }
298 });
299 }
300 if (!entityType && fieldName && afGui.getField(searchDisplay['saved_search.api_entity'], fieldName)) {
301 entityType = searchDisplay['saved_search.api_entity'];
302 }
303 if (entityType) {
304 return false;
305 }
306 });
307 return entityType || _.map(afGui.meta.searchDisplays, 'saved_search.api_entity')[0];
308 };
309
310 }
311 });
312
313 })(angular, CRM.$, CRM._);