alchemy-form 0.1.3 → 0.1.6

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 (42) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/assets/stylesheets/form/alchemy_field_array.scss +4 -0
  3. package/assets/stylesheets/form/alchemy_toggle.scss +2 -0
  4. package/assets/stylesheets/form/query_builder.scss +185 -0
  5. package/config/routes.js +8 -0
  6. package/controller/form_api_controller.js +51 -2
  7. package/element/00_form_base.js +37 -1
  8. package/element/20_query_builder_base.js +82 -0
  9. package/element/alchemy_field.js +65 -15
  10. package/element/alchemy_field_schema.js +45 -11
  11. package/element/alchemy_form.js +1 -12
  12. package/element/alchemy_select.js +68 -13
  13. package/element/alchemy_select_item.js +42 -1
  14. package/element/alchemy_table.js +123 -21
  15. package/element/query_builder.js +90 -0
  16. package/element/query_builder_entry.js +388 -0
  17. package/element/query_builder_group.js +221 -0
  18. package/element/query_builder_value.js +435 -0
  19. package/helper/form_actions/00_form_action.js +328 -0
  20. package/helper/form_actions/url_action.js +69 -0
  21. package/helper/query_builder_variable_definition/00_variable_definition.js +351 -0
  22. package/helper/query_builder_variable_definition/boolean_variable_definition.js +24 -0
  23. package/helper/query_builder_variable_definition/number_variable_definition.js +106 -0
  24. package/helper/query_builder_variable_definition/string_variable_definition.js +46 -0
  25. package/helper/widgets/alchemy_field_widget.js +48 -1
  26. package/helper/widgets/alchemy_form_widget.js +17 -2
  27. package/helper_field/query_builder_assignment.js +11 -0
  28. package/helper_field/query_builder_field.js +91 -0
  29. package/helper_field/query_builder_value.js +56 -0
  30. package/package.json +2 -2
  31. package/view/form/elements/alchemy_field_array.hwk +3 -1
  32. package/view/form/elements/alchemy_field_array_entry.hwk +3 -1
  33. package/view/form/elements/alchemy_field_schema.hwk +2 -4
  34. package/view/form/elements/alchemy_select_item.hwk +6 -1
  35. package/view/form/elements/query_builder.hwk +1 -0
  36. package/view/form/elements/query_builder_entry.hwk +33 -0
  37. package/view/form/elements/query_builder_group.hwk +64 -0
  38. package/view/form/elements/query_builder_value.hwk +10 -0
  39. package/view/form/inputs/edit/query_builder.hwk +5 -0
  40. package/view/form/inputs/edit/query_builder_assignment.hwk +6 -0
  41. package/view/form/inputs/edit/query_builder_value.hwk +11 -0
  42. package/view/form/select/qb_item.hwk +7 -0
@@ -23,7 +23,7 @@ FieldSchema.setTemplateFile('form/elements/alchemy_field_schema');
23
23
  *
24
24
  * @author Jelle De Loecker <jelle@elevenways.be>
25
25
  * @since 0.1.0
26
- * @version 0.1.3
26
+ * @version 0.1.4
27
27
  */
28
28
  FieldSchema.setProperty(function schema() {
29
29
 
@@ -34,13 +34,16 @@ FieldSchema.setProperty(function schema() {
34
34
  let other_field = this.getSchemaSupplierField();
35
35
  schema = null;
36
36
 
37
- console.log('Other field:', other_field, 'of', this)
38
-
39
37
  if (other_field && other_field.value && other_field.config && other_field.config.options) {
40
38
  let values = other_field.config.options.values;
41
39
 
42
40
  if (values) {
43
- schema = values[other_field.value];
41
+
42
+ if (values instanceof Classes.Alchemy.Map.Backed) {
43
+ schema = values.get(other_field.value);
44
+ } else {
45
+ schema = values[other_field.value];
46
+ }
44
47
 
45
48
  if (schema && schema.schema) {
46
49
  schema = schema.schema;
@@ -113,14 +116,11 @@ FieldSchema.setProperty(function value() {
113
116
  FieldSchema.setMethod(function getSchemaSupplierField() {
114
117
 
115
118
  if (!this.alchemy_field || !this.alchemy_field.config || !this.alchemy_field.config.options) {
116
- console.log('--no config found?')
117
119
  return;
118
120
  }
119
121
 
120
122
  let schema = this.alchemy_field.config.options.schema;
121
123
 
122
- console.log('Schema is...', schema);
123
-
124
124
  if (typeof schema == 'string') {
125
125
  let other_field_path = this.resolvePath(schema);
126
126
 
@@ -128,10 +128,7 @@ FieldSchema.setMethod(function getSchemaSupplierField() {
128
128
 
129
129
  let form = this.alchemy_field.alchemy_form;
130
130
 
131
- console.log(' -- Got form:', form);
132
-
133
131
  if (form) {
134
- console.log(' -- Looking for path:', other_field_path)
135
132
  return form.findFieldByPath(other_field_path);
136
133
  }
137
134
  }
@@ -155,4 +152,41 @@ FieldSchema.setMethod(function introduced() {
155
152
 
156
153
  this.rerender();
157
154
  }
158
- });
155
+ });
156
+
157
+ /**
158
+ * Get the original value
159
+ *
160
+ * @author Jelle De Loecker <jelle@elevenways.be>
161
+ * @since 0.1.4
162
+ * @version 0.1.4
163
+ */
164
+ FieldSchema.setProperty(function original_value() {
165
+
166
+ let field = this.alchemy_field,
167
+ path = this.field_path_in_record;
168
+
169
+ if (field && path) {
170
+ let form = field.alchemy_form || this.alchemy_form || this.field_context.alchemy_form;
171
+
172
+ if (form) {
173
+ return Object.path(form.document, path);
174
+ }
175
+ }
176
+
177
+ let context = this.field_context.alchemy_field_schema,
178
+ data;
179
+
180
+ if (context) {
181
+ data = context.original_value;
182
+ } else {
183
+ context = this.field_context.alchemy_form || this.alchemy_field.alchemy_form;
184
+ data = context.document;
185
+ }
186
+
187
+ path = this.field_path_in_current_schema;
188
+
189
+ let result = Object.path(data, path);
190
+
191
+ return result;
192
+ });
@@ -5,18 +5,7 @@
5
5
  * @since 0.1.0
6
6
  * @version 0.1.0
7
7
  */
8
- var Form = Function.inherits('Alchemy.Element.Form.Base', function Form() {
9
- Form.super.call(this);
10
- });
11
-
12
- /**
13
- * The stylesheet to load for this element
14
- *
15
- * @author Jelle De Loecker <jelle@develry.be>
16
- * @since 0.1.0
17
- * @version 0.1.0
18
- */
19
- Form.setStylesheetFile('form/form');
8
+ var Form = Function.inherits('Alchemy.Element.Form.Base', 'Form');
20
9
 
21
10
  /**
22
11
  * The stylesheet to load for this element
@@ -36,6 +36,26 @@ AlchemySelect.setProperty('_clear_count', 0);
36
36
  */
37
37
  AlchemySelect.setAttribute('total-item-count', {number: true});
38
38
 
39
+ /**
40
+ * The optional template to use for value items
41
+ * (The chosen value)
42
+ *
43
+ * @author Jelle De Loecker <jelle@develry.be>
44
+ * @since 0.1.6
45
+ * @version 0.1.6
46
+ */
47
+ AlchemySelect.setAttribute('value-item-template');
48
+
49
+ /**
50
+ * The optional template to use for option items
51
+ * (The items visible in the dropdown)
52
+ *
53
+ * @author Jelle De Loecker <jelle@develry.be>
54
+ * @since 0.1.6
55
+ * @version 0.1.6
56
+ */
57
+ AlchemySelect.setAttribute('option-item-template');
58
+
39
59
  /**
40
60
  * The hawkejs template to use
41
61
  *
@@ -244,12 +264,16 @@ AlchemySelect.setProperty(function loaded_items() {
244
264
  *
245
265
  * @author Jelle De Loecker <jelle@develry.be>
246
266
  * @since 0.1.0
247
- * @version 0.1.0
267
+ * @version 0.1.4
248
268
  *
249
269
  * @type {Number}
250
270
  */
251
271
  AlchemySelect.setProperty(function loaded_item_count() {
252
- return Object.size(this.options.values);
272
+ if (this.options.values) {
273
+ return this.options.values.size;
274
+ } else {
275
+ return 0;
276
+ }
253
277
  });
254
278
 
255
279
  /**
@@ -766,7 +790,7 @@ AlchemySelect.setMethod(function _ensureValueData(value) {
766
790
  *
767
791
  * @author Jelle De Loecker <jelle@develry.be>
768
792
  * @since 0.1.0
769
- * @version 0.1.0
793
+ * @version 0.1.4
770
794
  */
771
795
  AlchemySelect.setMethod(function _processPreloadedValues() {
772
796
 
@@ -784,9 +808,14 @@ AlchemySelect.setMethod(function _processPreloadedValues() {
784
808
  let item,
785
809
  key;
786
810
 
787
- for (key in values) {
811
+ let enum_values = new Classes.Alchemy.Map.Enum(values),
812
+ value;
813
+
814
+ for (let key of enum_values.keys()) {
815
+ value = enum_values.get(key);
816
+
788
817
  response.available++;
789
- item = Object.assign({}, values[key]);
818
+ item = Object.assign({}, value);
790
819
  item.id = key;
791
820
 
792
821
  response.items.push(item);
@@ -800,12 +829,16 @@ AlchemySelect.setMethod(function _processPreloadedValues() {
800
829
  *
801
830
  * @author Jelle De Loecker <jelle@develry.be>
802
831
  * @since 0.1.0
803
- * @version 0.1.0
832
+ * @version 0.1.6
804
833
  *
805
834
  * @param {Object} response
806
835
  */
807
836
  AlchemySelect.setMethod(function _processResponseData(response) {
808
837
 
838
+ if (!response) {
839
+ response = {};
840
+ }
841
+
809
842
  if (response.available) {
810
843
  this.total_item_count = response.available;
811
844
  }
@@ -814,6 +847,10 @@ AlchemySelect.setMethod(function _processResponseData(response) {
814
847
  record,
815
848
  item;
816
849
 
850
+ if (!records) {
851
+ records = [];
852
+ }
853
+
817
854
  for (record of records) {
818
855
  item = this._makeOption(record._id || record.id, record);
819
856
  this.addToDropdown(item);
@@ -915,7 +952,7 @@ AlchemySelect.setMethod(function _loadRemote(config) {
915
952
  *
916
953
  * @author Jelle De Loecker <jelle@develry.be>
917
954
  * @since 0.1.0
918
- * @version 0.1.0
955
+ * @version 0.1.4
919
956
  */
920
957
  AlchemySelect.setMethod(function recreateDropdownElements() {
921
958
 
@@ -925,8 +962,8 @@ AlchemySelect.setMethod(function recreateDropdownElements() {
925
962
 
926
963
  Hawkejs.removeChildren(this.dropdown_content);
927
964
 
928
- for (key in items) {
929
- item = items[key];
965
+ for (let key of items.keys()) {
966
+ item = items.get(key);
930
967
  item = this._makeOption(item.id, item);
931
968
  this.addToDropdown(item);
932
969
  }
@@ -1157,7 +1194,7 @@ AlchemySelect.setMethod(function close(event) {
1157
1194
  *
1158
1195
  * @author Jelle De Loecker <jelle@develry.be>
1159
1196
  * @since 0.1.0
1160
- * @version 0.1.0
1197
+ * @version 0.1.6
1161
1198
  *
1162
1199
  * @param {String} type "value" or "option"
1163
1200
  * @param {Mixed} value The actual value of this item
@@ -1172,9 +1209,19 @@ AlchemySelect.setMethod(function _makeValueItem(type, value, data) {
1172
1209
  // Set the type ("value" or "option")
1173
1210
  item.type = type;
1174
1211
 
1212
+ // Set the custom template to use, if any
1213
+ let custom_template = this[type + '-item-template'];
1214
+
1215
+ if (custom_template) {
1216
+ item.custom_template = custom_template;
1217
+ }
1218
+
1175
1219
  // Assign the value
1176
1220
  item.value = value;
1177
1221
 
1222
+ // Add a reference to this element
1223
+ item.alchemy_select = this;
1224
+
1178
1225
  if (!data) {
1179
1226
  data = this.getValueData(value);
1180
1227
  }
@@ -1188,7 +1235,11 @@ AlchemySelect.setMethod(function _makeValueItem(type, value, data) {
1188
1235
  this.options.values = {};
1189
1236
  }
1190
1237
 
1191
- this.options.values[value] = data;
1238
+ if (this.options.values instanceof Classes.Alchemy.Map.Enum) {
1239
+ this.options.values.set(value, data);
1240
+ } else {
1241
+ this.options.values[value] = data;
1242
+ }
1192
1243
  }
1193
1244
 
1194
1245
  // And the associated data
@@ -1207,8 +1258,12 @@ AlchemySelect.setMethod(function _makeValueItem(type, value, data) {
1207
1258
  return
1208
1259
  }
1209
1260
 
1210
- if (that.options.values && that.options.values[value]) {
1211
- data = that.options.values[value];
1261
+ if (that.options.values) {
1262
+ if (that.options.values instanceof Classes.Alchemy.Map.Enum) {
1263
+ data = that.options.values.get(value);
1264
+ } else if (that.options.values[value]) {
1265
+ data = that.options.values[value];
1266
+ }
1212
1267
  }
1213
1268
 
1214
1269
  this.data = data;
@@ -37,6 +37,15 @@ Item.setAttribute('type');
37
37
  */
38
38
  Item.setAttribute('selected', {boolean: true});
39
39
 
40
+ /**
41
+ * Should a custom template be used?
42
+ *
43
+ * @author Jelle De Loecker <jelle@elevenways.be>
44
+ * @since 0.1.6
45
+ * @version 0.1.6
46
+ */
47
+ Item.setAttribute('custom-template');
48
+
40
49
  /**
41
50
  * The value of this item
42
51
  *
@@ -53,4 +62,36 @@ Item.setAssignedProperty('value');
53
62
  * @since 0.1.0
54
63
  * @version 0.1.0
55
64
  */
56
- Item.setAssignedProperty('data');
65
+ Item.setAssignedProperty('data');
66
+
67
+ /**
68
+ * The parent alchemy-select item
69
+ *
70
+ * @author Jelle De Loecker <jelle@elevenways.be>
71
+ * @since 0.1.5
72
+ * @version 0.1.5
73
+ */
74
+ Item.enforceProperty(function alchemy_select(new_value) {
75
+
76
+ if (new_value == null) {
77
+ new_value = this.closest('alchemy-select');
78
+ }
79
+
80
+ return new_value;
81
+ });
82
+
83
+ /**
84
+ * The display item
85
+ *
86
+ * @author Jelle De Loecker <jelle@elevenways.be>
87
+ * @since 0.1.5
88
+ * @version 0.1.5
89
+ */
90
+ Item.setProperty(function display_title() {
91
+
92
+ if (!this.data) {
93
+ return '';
94
+ }
95
+
96
+ return this.data.title || this.data.name || this.data.username || this.data.$pk || this.value;
97
+ });
@@ -513,7 +513,7 @@ Table.setMethod(function getCurrentStateUrl() {
513
513
  *
514
514
  * @author Jelle De Loecker <jelle@elevenways.be>
515
515
  * @since 0.1.0
516
- * @version 0.1.0
516
+ * @version 0.1.6
517
517
  *
518
518
  * @param {Array} records
519
519
  */
@@ -530,6 +530,7 @@ Table.setMethod(function setRecords(records) {
530
530
  }
531
531
 
532
532
  this.showPagination();
533
+ this.attachContextMenus();
533
534
  });
534
535
 
535
536
  /**
@@ -593,7 +594,7 @@ Table.setMethod(function showPagination() {
593
594
  *
594
595
  * @author Jelle De Loecker <jelle@elevenways.be>
595
596
  * @since 0.1.0
596
- * @version 0.1.1
597
+ * @version 0.1.6
597
598
  *
598
599
  * @param {Object} entry
599
600
  *
@@ -604,7 +605,10 @@ Table.setMethod(function createDataRow(entry) {
604
605
  let field,
605
606
  value,
606
607
  tr = this.createElement('tr'),
607
- td;
608
+ td,
609
+ id = entry.$pk || entry._id || entry.id;
610
+
611
+ tr.dataset.pk = id;
608
612
 
609
613
  for (field of this.fieldset) {
610
614
  td = this.createElement('td');
@@ -623,31 +627,22 @@ Table.setMethod(function createDataRow(entry) {
623
627
  td.classList.add('aft-actions');
624
628
  tr.append(td);
625
629
 
626
- let actions;
627
-
628
- if (entry.$hold && entry.$hold.actions) {
629
- actions = entry.$hold.actions;
630
- } else {
631
- actions = entry.$actions;
632
- }
630
+ let actions = this.getEntryActions(entry, 'row');
633
631
 
634
632
  if (actions && actions.length) {
633
+
635
634
  let action,
636
635
  anchor;
637
636
 
637
+ // Iterate over all the defined actions
638
638
  for (action of actions) {
639
- anchor = this.createElement('a');
640
- anchor.dataset.name = action.name;
641
-
642
- if (action.icon) {
643
- let alico = this.createElement('al-ico');
644
- alico.setAttribute('type', action.icon);
645
- anchor.append(alico);
646
- } else {
647
- anchor.textContent = action.title || action.name;
639
+
640
+ anchor = action.constructElement(this);
641
+
642
+ if (!anchor) {
643
+ continue;
648
644
  }
649
645
 
650
- anchor.setAttribute('href', action.url);
651
646
  td.append(anchor);
652
647
  }
653
648
  }
@@ -658,6 +653,111 @@ Table.setMethod(function createDataRow(entry) {
658
653
  return tr;
659
654
  });
660
655
 
656
+ /**
657
+ * Get a list of actions
658
+ *
659
+ * @author Jelle De Loecker <jelle@elevenways.be>
660
+ * @since 0.1.6
661
+ * @version 0.1.6
662
+ *
663
+ * @param {Object} record
664
+ * @param {Stirng} filter
665
+ *
666
+ * @return {Alchemy.Form.Action.Action[]}
667
+ */
668
+ Table.setMethod(function getEntryActions(record, filter) {
669
+
670
+ let result = [];
671
+
672
+ if (!record) {
673
+ return result;
674
+ }
675
+
676
+ let actions;
677
+
678
+ if (record.$hold && record.$hold.actions) {
679
+ actions = record.$hold.actions;
680
+ } else {
681
+ actions = record.$actions;
682
+ }
683
+
684
+ if(!actions || !actions.length) {
685
+ return result;
686
+ }
687
+
688
+ for (let action of actions) {
689
+
690
+ if (!action.type) {
691
+ action.type = 'url';
692
+ }
693
+
694
+ // Make sure it's an action instance
695
+ action = Classes.Alchemy.Form.Action.Action.cast(action);
696
+
697
+ if (!action || (filter && !action.isAllowedIn(filter))) {
698
+ continue;
699
+ }
700
+
701
+ result.push(action);
702
+ }
703
+
704
+ return result;
705
+ });
706
+
707
+ /**
708
+ * Add context menus to rows
709
+ *
710
+ * @author Jelle De Loecker <jelle@elevenways.be>
711
+ * @since 0.1.6
712
+ * @version 0.1.6
713
+ *
714
+ * @param {Object} entry
715
+ */
716
+ Table.setMethod(function attachContextMenus() {
717
+
718
+ if (!Blast.isBrowser) {
719
+ return;
720
+ }
721
+
722
+ if (!this.assigned_data.records || !this.assigned_data.records.length) {
723
+ return;
724
+ }
725
+
726
+ for (let record of this.assigned_data.records) {
727
+
728
+ let context_actions = this.getEntryActions(record, 'context');
729
+
730
+ if (!context_actions || !context_actions.length) {
731
+ contintue;
732
+ }
733
+
734
+ let pk = record.$pk || record._id || record.id;
735
+
736
+ if (!pk) {
737
+ continue;
738
+ }
739
+
740
+ let tr = this.querySelector('[data-pk="' + pk + '"]');
741
+
742
+ if (!tr) {
743
+ continue;
744
+ }
745
+
746
+ tr.addEventListener('contextmenu', e => {
747
+
748
+ this.selectRow(tr);
749
+
750
+ let menu = this.createElement('he-context-menu');
751
+
752
+ for (let action of context_actions) {
753
+ action.addToContextMenu(menu);
754
+ }
755
+
756
+ menu.show(e);
757
+ });
758
+ }
759
+ });
760
+
661
761
  /**
662
762
  * On new fieldset
663
763
  *
@@ -931,12 +1031,14 @@ Table.setMethod(function sortData() {
931
1031
  *
932
1032
  * @author Jelle De Loecker <jelle@elevenways.be>
933
1033
  * @since 0.1.0
934
- * @version 0.1.0
1034
+ * @version 0.1.6
935
1035
  */
936
1036
  Table.setMethod(function introduced() {
937
1037
 
938
1038
  const that = this;
939
1039
 
1040
+ this.attachContextMenus();
1041
+
940
1042
  this.addEventListener('click', function onClick(e) {
941
1043
 
942
1044
  let target = e.target,
@@ -0,0 +1,90 @@
1
+ /**
2
+ * The alchemy-toggle custom element
3
+ *
4
+ * @author Jelle De Loecker <jelle@elevenways.be>
5
+ * @since 0.1.6
6
+ * @version 0.1.6
7
+ */
8
+ const QueryBuilder = Function.inherits('Alchemy.Element.Form.Base', 'QueryBuilder');
9
+
10
+ /**
11
+ * The template code
12
+ *
13
+ * @author Jelle De Loecker <jelle@elevenways.be>
14
+ * @since 0.1.6
15
+ * @version 0.1.6
16
+ */
17
+ QueryBuilder.setTemplate(`<alchemy-query-builder-group></alchemy-query-builder-group>`, {plain_html: true, render_immediate: true});
18
+
19
+ /**
20
+ * The stylesheet to load for this element
21
+ *
22
+ * @author Jelle De Loecker <jelle@elevenways.be>
23
+ * @since 0.1.6
24
+ * @version 0.1.6
25
+ */
26
+ QueryBuilder.setStylesheetFile('form/query_builder');
27
+
28
+ /**
29
+ * The dataprovider used to get the variables
30
+ *
31
+ * @author Jelle De Loecker <jelle@elevenways.be>
32
+ * @since 0.1.6
33
+ * @version 0.1.6
34
+ */
35
+ QueryBuilder.setAssignedProperty('dataprovider');
36
+
37
+ /**
38
+ * Getter for the header element
39
+ *
40
+ * @author Jelle De Loecker <jelle@elevenways.be>
41
+ * @since 0.1.6
42
+ * @version 0.1.6
43
+ */
44
+ QueryBuilder.addElementGetter('root_group', ':scope > alchemy-query-builder-group');
45
+
46
+ /**
47
+ * Get/set the value
48
+ *
49
+ * @author Jelle De Loecker <jelle@elevenways.be>
50
+ * @since 0.1.6
51
+ * @version 0.1.6
52
+ */
53
+ QueryBuilder.setProperty(function value() {
54
+ return this.root_group.value;
55
+ }, function setValue(value) {
56
+ this.assigned_data.value = value;
57
+
58
+ if (Blast.isBrowser && this.has_rendered) {
59
+ this.applyValue(value);
60
+ }
61
+ });
62
+
63
+ /**
64
+ * Apply the given value
65
+ *
66
+ * @author Jelle De Loecker <jelle@elevenways.be>
67
+ * @since 0.1.6
68
+ * @version 0.1.6
69
+ */
70
+ QueryBuilder.setMethod(function applyValue(value) {
71
+
72
+ if (!value) {
73
+ return;
74
+ }
75
+
76
+ this.root_group.applyValue(value);
77
+ });
78
+
79
+ /**
80
+ * Added to the dom
81
+ *
82
+ * @author Jelle De Loecker <jelle@elevenways.be>
83
+ * @since 0.1.6
84
+ * @version 0.1.6
85
+ */
86
+ QueryBuilder.setMethod(function introduced() {
87
+ if (this.assigned_data.value) {
88
+ this.applyValue(this.assigned_data.value);
89
+ }
90
+ });