alchemy-form 0.3.0-alpha.2 → 0.3.0-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/assets/stylesheets/form/elements/_button.scss +1 -1
  3. package/assets/stylesheets/form/elements/_enum_badge.scss +7 -0
  4. package/assets/stylesheets/form/elements/_feedback_input.scss +0 -0
  5. package/assets/stylesheets/form/elements/_field_array.scss +10 -3
  6. package/assets/stylesheets/form/elements/_query_builder.scss +0 -0
  7. package/assets/stylesheets/form/elements/_select.scss +176 -176
  8. package/assets/stylesheets/form/elements/_table.scss +169 -53
  9. package/assets/stylesheets/form/elements/_tabs.scss +25 -35
  10. package/assets/stylesheets/form/elements/_toggle.scss +0 -0
  11. package/assets/stylesheets/form/elements/index.scss +1 -1
  12. package/assets/stylesheets/form/general/_colors.scss +0 -0
  13. package/config/routes.js +10 -0
  14. package/controller/form_api_controller.js +28 -0
  15. package/element/00_form_base.js +82 -27
  16. package/element/al_button.js +12 -1
  17. package/element/al_enum_badge.js +157 -0
  18. package/element/al_field.js +134 -39
  19. package/element/al_field_array.js +22 -0
  20. package/element/al_field_schema.js +222 -55
  21. package/element/al_form.js +9 -6
  22. package/element/al_select.js +2 -2
  23. package/element/al_table.js +10 -1
  24. package/element/al_virtual_scroll.js +2 -1
  25. package/helper/field_recompute_handler.js +0 -2
  26. package/package.json +1 -1
  27. package/view/form/elements/al_enum_badge.hwk +9 -0
  28. package/view/form/elements/alchemy_field.hwk +2 -0
  29. package/view/form/elements/alchemy_field_array.hwk +17 -6
  30. package/view/form/elements/alchemy_field_array_entry.hwk +11 -5
  31. package/view/form/elements/alchemy_field_schema.hwk +2 -2
  32. package/view/form/elements/alchemy_select_item.hwk +1 -1
  33. package/view/form/inputs/edit/enum.hwk +1 -1
  34. package/view/form/inputs/edit/mixed.hwk +9 -0
  35. package/view/form/inputs/edit_sw/fallback.hwk +1 -0
  36. package/view/form/inputs/view/association_alias.hwk +16 -0
  37. package/view/form/inputs/view/schema.hwk +4 -0
  38. package/view/form/inputs/view/string.hwk +1 -1
  39. package/view/form/inputs/view_inline/belongs_to.hwk +16 -0
  40. package/view/form/inputs/view_inline/boolean.hwk +2 -2
  41. package/view/form/inputs/view_inline/datetime.hwk +1 -1
  42. package/view/form/inputs/view_inline/enum.hwk +11 -7
  43. package/view/form/inputs/view_inline/objectid.hwk +1 -1
  44. package/view/form/inputs/view_inline/string.hwk +1 -1
  45. package/view/form/wrappers/edit_sw/default.hwk +1 -0
  46. package/assets/stylesheets/form/elements/_badge.scss +0 -41
@@ -0,0 +1,157 @@
1
+ /**
2
+ * The al-enum-badge class:
3
+ * Shows a selected enum value as a badge.
4
+ *
5
+ * @author Jelle De Loecker <jelle@elevenways.be>
6
+ * @since 0.3.0
7
+ * @version 0.3.0
8
+ */
9
+ const EnumBadge = Function.inherits('Alchemy.Element.Form.Base', 'EnumBadge');
10
+
11
+ /**
12
+ * The template to use for the content of this element
13
+ *
14
+ * @author Jelle De Loecker <jelle@develry.be>
15
+ * @since 0.3.0
16
+ * @version 0.3.0
17
+ */
18
+ EnumBadge.setTemplateFile('form/elements/al_enum_badge');
19
+
20
+ /**
21
+ * The ID of the enum value to show.
22
+ * Can be a string, a number, an ObjectId representation.
23
+ *
24
+ * @author Jelle De Loecker <jelle@elevenways.be>
25
+ * @since 0.3.0
26
+ * @version 0.3.0
27
+ */
28
+ EnumBadge.setAttribute('enum-id');
29
+
30
+ /**
31
+ * The optional Model to get the info from,
32
+ * in case the source is a Model.
33
+ *
34
+ * @author Jelle De Loecker <jelle@elevenways.be>
35
+ * @since 0.3.0
36
+ * @version 0.3.0
37
+ */
38
+ EnumBadge.setAttribute('model');
39
+
40
+ /**
41
+ * The optional enum-class to use (if the source is not just a model)
42
+ * This class has to be available on the client-side!
43
+ *
44
+ * @author Jelle De Loecker <jelle@elevenways.be>
45
+ * @since 0.3.0
46
+ * @version 0.3.0
47
+ */
48
+ EnumBadge.setAttribute('enum-class');
49
+
50
+ /**
51
+ * See if we need to prepare any render variables
52
+ *
53
+ * @author Jelle De Loecker <jelle@elevenways.be>
54
+ * @since 0.3.0
55
+ * @version 0.3.0
56
+ */
57
+ EnumBadge.setMethod(function prepareRenderVariables() {
58
+
59
+ let enum_id = this.enum_id;
60
+
61
+ if (!enum_id) {
62
+ return;
63
+ }
64
+
65
+ let enum_class = this.enum_class;
66
+
67
+ if (!enum_class) {
68
+
69
+ if (this.model) {
70
+ return this.prepareRenderVariablesFromModel();
71
+ }
72
+
73
+ return;
74
+ }
75
+
76
+ let constructor,
77
+ parent = Object.path(Classes, enum_class);
78
+
79
+ if (parent?.is_namespace) {
80
+ parent = parent[parent.name];
81
+ }
82
+
83
+ if (!parent) {
84
+ return;
85
+ }
86
+
87
+ // Not sure this will always be correct.
88
+ // Might have to loop over all the children.
89
+ // Or use `getDescendantsDict()` or `getDescendantsMap()`
90
+ constructor = parent.getDescendant(enum_id);
91
+
92
+ if (!constructor) {
93
+ return;
94
+ }
95
+
96
+ let title = constructor.title,
97
+ color,
98
+ index,
99
+ icon;
100
+
101
+ let enum_info = constructor.enum_info;
102
+
103
+ if (enum_info) {
104
+ if (enum_info.title) {
105
+ title = enum_info.title;
106
+ }
107
+
108
+ if (enum_info.icon) {
109
+ icon = enum_info.icon;
110
+ }
111
+
112
+ if (enum_info.index) {
113
+ index = enum_info.index;
114
+ }
115
+
116
+ if (enum_info.color) {
117
+ color = enum_info.color;
118
+ }
119
+ }
120
+
121
+ return {title, color, index, icon};
122
+ });
123
+
124
+ /**
125
+ * Get render variables from a model
126
+ *
127
+ * @author Jelle De Loecker <jelle@elevenways.be>
128
+ * @since 0.3.0
129
+ * @version 0.3.0
130
+ */
131
+ EnumBadge.setMethod(async function prepareRenderVariablesFromModel() {
132
+
133
+ let enum_id = this.enum_id,
134
+ model = this.model;
135
+
136
+ if (!enum_id || !model) {
137
+ return;
138
+ }
139
+
140
+ let resource_options = {
141
+ name : 'FormApi#enumInfo',
142
+ params : {
143
+ model_name : model,
144
+ id : enum_id
145
+ }
146
+ };
147
+
148
+ let result;
149
+
150
+ try {
151
+ result = await this.hawkejs_helpers.Alchemy.getResource(resource_options);
152
+ } catch (err) {
153
+ console.error('Failed to get enum info:', err);
154
+ }
155
+
156
+ return result;
157
+ });
@@ -72,6 +72,24 @@ Field.setAttribute('field-view');
72
72
  */
73
73
  Field.setAttribute('wrapper-view');
74
74
 
75
+ /**
76
+ * Minimum amount of values (in case of an array)
77
+ *
78
+ * @author Jelle De Loecker <jelle@elevenways.be>
79
+ * @since 0.1.12
80
+ * @version 0.1.12
81
+ */
82
+ Field.setAttribute('min-entry-count', {type: 'number'});
83
+
84
+ /**
85
+ * Maximum amount of values (in case of an array)
86
+ *
87
+ * @author Jelle De Loecker <jelle@elevenways.be>
88
+ * @since 0.1.12
89
+ * @version 0.1.12
90
+ */
91
+ Field.setAttribute('max-entry-count', {type: 'number'});
92
+
75
93
  /**
76
94
  * Is this a read only field?
77
95
  *
@@ -176,7 +194,7 @@ Field.enforceProperty(function config(new_value, old_value) {
176
194
 
177
195
  // If an explicit field is set without a schema,
178
196
  // we need to remember it for serializing purposes
179
- if (new_value && !new_value.schema) {
197
+ if (new_value && (!new_value.schema || new_value == new_value.schema)) {
180
198
  this.assigned_data.field_config = new_value;
181
199
  }
182
200
 
@@ -196,7 +214,7 @@ Field.enforceProperty(function config(new_value, old_value) {
196
214
  }
197
215
 
198
216
  if (new_value && new_value.constructor && new_value.constructor.type_name) {
199
- this.field_type = new_value.constructor.type_name;
217
+ this.field_type = new_value.constructor.type_path || new_value.constructor.type_name;
200
218
  } else if (new_value) {
201
219
  this.field_type = null;
202
220
  }
@@ -209,10 +227,16 @@ Field.enforceProperty(function config(new_value, old_value) {
209
227
  *
210
228
  * @author Jelle De Loecker <jelle@elevenways.be>
211
229
  * @since 0.1.0
212
- * @version 0.1.9
230
+ * @version 0.3.0
213
231
  */
214
232
  Field.enforceProperty(function schema(new_value, old_value) {
215
233
 
234
+ let forced_value = !!new_value;
235
+
236
+ if (!new_value && this.assigned_data?.schema) {
237
+ new_value = this.assigned_data.schema;
238
+ }
239
+
216
240
  if (!new_value) {
217
241
  // See if this is in a schema field
218
242
  if (this.alchemy_field_schema) {
@@ -238,6 +262,10 @@ Field.enforceProperty(function schema(new_value, old_value) {
238
262
  }
239
263
  }
240
264
 
265
+ if (forced_value) {
266
+ this.assignData('schema', new_value);
267
+ }
268
+
241
269
  return new_value;
242
270
  });
243
271
 
@@ -246,10 +274,14 @@ Field.enforceProperty(function schema(new_value, old_value) {
246
274
  *
247
275
  * @author Jelle De Loecker <jelle@elevenways.be>
248
276
  * @since 0.1.0
249
- * @version 0.1.0
277
+ * @version 0.3.0
250
278
  */
251
279
  Field.setProperty(function is_array() {
252
280
 
281
+ if (this.assigned_data?.is_array != null) {
282
+ return this.assigned_data.is_array;
283
+ }
284
+
253
285
  let config = this.config;
254
286
 
255
287
  if (config) {
@@ -257,6 +289,8 @@ Field.setProperty(function is_array() {
257
289
  }
258
290
 
259
291
  return false;
292
+ }, function setValue(value) {
293
+ this.assignData('is_array', value);
260
294
  });
261
295
 
262
296
  /**
@@ -264,14 +298,18 @@ Field.setProperty(function is_array() {
264
298
  *
265
299
  * @author Jelle De Loecker <jelle@elevenways.be>
266
300
  * @since 0.1.3
267
- * @version 0.1.3
301
+ * @version 0.3.0
268
302
  */
269
- Field.setProperty(function contains_schema() {
303
+ Field.setProperty(function contains_schema() {
270
304
 
271
305
  let config = this.config;
272
306
 
273
307
  if (config) {
274
- return config instanceof Classes.Alchemy.Field.Schema;
308
+ if (config instanceof Classes.Alchemy.Field.Schema) {
309
+ return true;
310
+ }
311
+
312
+ return Classes.Alchemy.Client.Schema.isSchema(config);
275
313
  }
276
314
 
277
315
  return false;
@@ -345,17 +383,17 @@ Field.setProperty(function field_path_in_schema() {
345
383
  *
346
384
  * @author Jelle De Loecker <jelle@elevenways.be>
347
385
  * @since 0.1.3
348
- * @version 0.1.3
386
+ * @version 0.3.0
349
387
  *
350
388
  * @return {String}
351
389
  */
352
- Field.setMethod(function getPathEntryName() {
390
+ Field.setMethod(function getPathEntryName() {
353
391
 
354
392
  if (this.config && this.config.name) {
355
393
  return this.config.name;
356
394
  }
357
395
 
358
- return '';
396
+ return this.field_name;
359
397
  });
360
398
 
361
399
  /**
@@ -385,21 +423,17 @@ Field.setProperty(function model() {
385
423
  *
386
424
  * @author Jelle De Loecker <jelle@elevenways.be>
387
425
  * @since 0.1.0
388
- * @version 0.1.11
426
+ * @version 0.3.0
389
427
  */
390
428
  Field.enforceProperty(function view_file(new_value, old_value) {
391
429
 
392
430
  if (!new_value) {
393
431
 
394
432
  let view_type = this.view_type,
395
- field_view = this.field_view || this.field_type;
396
-
397
- if (!field_view) {
398
- let config = this.config;
433
+ field_view = this.field_view;
399
434
 
400
- if (config) {
401
- field_view = config.constructor.type_name;
402
- }
435
+ if (!field_view) {
436
+ field_view = this.getFieldType().replaceAll('.', '_');
403
437
  }
404
438
 
405
439
  new_value = this.generateTemplatePath('inputs', view_type, field_view);
@@ -441,7 +475,7 @@ Field.enforceProperty(function wrapper_file(new_value, old_value) {
441
475
  *
442
476
  * @author Jelle De Loecker <jelle@elevenways.be>
443
477
  * @since 0.1.0
444
- * @version 0.1.11
478
+ * @version 0.3.0
445
479
  */
446
480
  Field.setProperty(function view_files() {
447
481
 
@@ -472,6 +506,9 @@ Field.setProperty(function view_files() {
472
506
  return false;
473
507
  }
474
508
 
509
+ // Fallback to the fallback
510
+ result.push(this.generateTemplatePath('inputs', view_type, 'fallback'));
511
+
475
512
  // Fallback to a simple string input
476
513
  result.push(this.generateTemplatePath('inputs', view_type, 'string'));
477
514
 
@@ -524,21 +561,46 @@ Field.setProperty(function wrapper_files() {
524
561
  return result;
525
562
  });
526
563
 
564
+ /**
565
+ * Get the original value container
566
+ *
567
+ * @author Jelle De Loecker <jelle@elevenways.be>
568
+ * @since 0.3.0
569
+ * @version 0.3.0
570
+ */
571
+ Field.setAssignedProperty(function original_value_container() {
572
+
573
+ if (this.assigned_data.original_value_container != null) {
574
+ return this.assigned_data.original_value_container;
575
+ }
576
+
577
+ let form = this.alchemy_form;
578
+
579
+ if (form) {
580
+ return form.document;
581
+ }
582
+ });
583
+
527
584
  /**
528
585
  * Get the original value
529
586
  *
530
587
  * @author Jelle De Loecker <jelle@elevenways.be>
531
588
  * @since 0.1.0
532
- * @version 0.1.8
589
+ * @version 0.3.0
533
590
  */
534
591
  Field.setProperty(function original_value() {
535
592
 
536
- if (this.assigned_data.original_value != null) {
593
+ if (this.assigned_data.original_value !== undefined) {
537
594
  return this.assigned_data.original_value;
538
595
  }
539
596
 
540
- let alchemy_field_schema = this.alchemy_field_schema,
541
- path = this.field_path_in_current_schema;
597
+ let path = this.field_path_in_current_schema;
598
+
599
+ if (path == null) {
600
+ return;
601
+ }
602
+
603
+ let alchemy_field_schema = this.alchemy_field_schema;
542
604
 
543
605
  if (alchemy_field_schema) {
544
606
  let original_schema_value = alchemy_field_schema.original_value;
@@ -552,13 +614,13 @@ Field.setProperty(function original_value() {
552
614
  return;
553
615
  }
554
616
 
555
- let form = this.alchemy_form;
617
+ let original_container = this.original_value_container;
556
618
 
557
- if (form && form.document) {
558
- return Object.path(form.document, path);
619
+ if (original_container) {
620
+ return Object.path(original_container, path);
559
621
  }
560
622
  }, function setOriginalValue(value) {
561
- return this.assigned_data.original_value = value;
623
+ return this.assignData('original_value', value);
562
624
  });
563
625
 
564
626
  /**
@@ -566,26 +628,38 @@ Field.setProperty(function original_value() {
566
628
  *
567
629
  * @author Jelle De Loecker <jelle@elevenways.be>
568
630
  * @since 0.1.0
569
- * @version 0.2.0
631
+ * @version 0.3.0
570
632
  */
571
633
  Field.setProperty(function value_element() {
572
634
 
573
- let input;
635
+ let special_input;
574
636
 
575
637
  // Translations always get preference
576
638
  if (this.is_translatable) {
577
- input = this.querySelector('al-field-translatable');
639
+ special_input = this.querySelector('al-field-translatable');
578
640
  } else if (this.is_array) {
579
- input = this.querySelector('al-field-array');
641
+ special_input = this.querySelector('al-field-array');
580
642
  } else if (this.contains_schema) {
581
- input = this.querySelector('al-field-schema');
643
+ special_input = this.querySelector('al-field-schema');
582
644
  }
583
645
 
584
- if (!input) {
585
- input = this.querySelector('.alchemy-field-value');
646
+ let main_input = this.querySelector('.alchemy-field-value');
647
+
648
+ // If there is no main input, but there is a special input, simply use that
649
+ if (!main_input) {
650
+ return special_input;
651
+ }
652
+
653
+ if (!special_input) {
654
+ return main_input;
586
655
  }
587
656
 
588
- return input;
657
+ // Use the top level one
658
+ if (special_input.contains(main_input)) {
659
+ return special_input;
660
+ }
661
+
662
+ return main_input;
589
663
  });
590
664
 
591
665
  /**
@@ -602,7 +676,7 @@ Field.setProperty(function value() {
602
676
  if (element) {
603
677
  let value = element.value;
604
678
 
605
- if (this.config) {
679
+ if (this.config?.castContainedValues) {
606
680
  value = this.config.castContainedValues(value);
607
681
  }
608
682
 
@@ -615,7 +689,7 @@ Field.setProperty(function value() {
615
689
 
616
690
  let has_changed = !Object.alike(this[LAST_SET_VALUE], value);
617
691
 
618
- if (this.original_value == null) {
692
+ if (this.original_value === undefined) {
619
693
  this.original_value = value;
620
694
  }
621
695
 
@@ -661,6 +735,27 @@ Field.setProperty(function allow_empty_value_placeholder() {
661
735
  return !!(this.applied_options?.empty_value_placeholder ?? true);
662
736
  });
663
737
 
738
+ /**
739
+ * Set the value container (like the `Document` instance) the value came from.
740
+ * Will only be set if there is no `alchemy_form` available.
741
+ *
742
+ * @author Jelle De Loecker <jelle@elevenways.be>
743
+ * @since 0.3.0
744
+ * @version 0.3.0
745
+ */
746
+ Field.setMethod(function rememberOriginalValueContainer(container) {
747
+
748
+ if (!container) {
749
+ return;
750
+ }
751
+
752
+ if (this.alchemy_form) {
753
+ return;
754
+ }
755
+
756
+ this.original_value_container = container;
757
+ });
758
+
664
759
  /**
665
760
  * Create the empty value placeholder text
666
761
  *
@@ -681,7 +776,7 @@ Field.setMethod(function createEmptyValuePlaceholderText() {
681
776
  microcopy = Classes.Alchemy.Microcopy('empty-value-placeholder', {
682
777
  field_name : this.field_name,
683
778
  model_name : this.model,
684
- field_type : this.field_type,
779
+ field_type : this.getFieldType(),
685
780
  zone : this.zone,
686
781
  path : this.config?.path_in_document,
687
782
  });
@@ -785,7 +880,7 @@ Field.setMethod(function getFieldType() {
785
880
  }
786
881
  }
787
882
 
788
- return result;
883
+ return result || '';
789
884
  });
790
885
 
791
886
  /**
@@ -16,6 +16,24 @@ const FieldArray = Function.inherits('Alchemy.Element.Form.FieldCustom', 'FieldA
16
16
  */
17
17
  FieldArray.setTemplateFile('form/elements/alchemy_field_array');
18
18
 
19
+ /**
20
+ * Minimum amount of values (in case of an array)
21
+ *
22
+ * @author Jelle De Loecker <jelle@elevenways.be>
23
+ * @since 0.3.0
24
+ * @version 0.3.0
25
+ */
26
+ FieldArray.setAttribute('min-entry-count', {type: 'number'});
27
+
28
+ /**
29
+ * Maximum amount of values (in case of an array)
30
+ *
31
+ * @author Jelle De Loecker <jelle@elevenways.be>
32
+ * @since 0.3.0
33
+ * @version 0.3.0
34
+ */
35
+ FieldArray.setAttribute('max-entry-count', {type: 'number'});
36
+
19
37
  /**
20
38
  * Get the live value
21
39
  *
@@ -53,6 +71,10 @@ FieldArray.setMethod(function introduced() {
53
71
 
54
72
  const that = this;
55
73
 
74
+ if (this.max_entry_count === 1) {
75
+ return;
76
+ }
77
+
56
78
  this.onEventSelector('click', '.remove', function onClick(e) {
57
79
 
58
80
  e.preventDefault();