alchemy-form 0.2.0 → 0.2.2

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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,20 @@
1
+ ## 0.2.2 (2023-01-23)
2
+
3
+ * Fix `al-button`'s `activate` event not being cancelable
4
+ * Add `placeholder` support to the string field
5
+
6
+ ## 0.2.1 (2022-12-23)
7
+
8
+ * Enable `al-form` model validation
9
+ * Don't throw an error when a `al-field` element has no field name configured
10
+ * Overwrite preloaded `al-select` values with their processed variant
11
+ * Throw an error when a document does not have a `loadQueryBuilderData` method while using the `FormApi#queryBuilderData` action
12
+ * `QueryBuilderValue` instances will now always add a type to the `value_explicit` object
13
+ * Make `al-button` elements submit forms automatically
14
+ * Fix operator `al-select` element staying empty in a query builder entry
15
+ * Work around a race condition in the query builder
16
+ * Add `behaviour` attribute to `al-button` element
17
+
1
18
  ## 0.2.0 (2022-11-02)
2
19
 
3
20
  * Use the `al-` prefix for all custom elements instead of nothing or `alchemy-`
@@ -102,12 +102,18 @@ al-query-builder-entry {
102
102
  min-width: 11rem;
103
103
  }
104
104
 
105
+ .small-title {
106
+ color: gray;
107
+ margin-bottom: 5px;
108
+ display: block;
109
+ }
110
+
105
111
  .qb-delete-wrapper {
106
112
  flex: 1;
107
113
  text-align: right;
108
114
  }
109
115
 
110
- .qb-value-wrapper {
116
+ .qb-value-wrapper .qb-value-nested-wrapper {
111
117
  display: flex;
112
118
  gap: 0.3rem;
113
119
  }
@@ -67,7 +67,7 @@ FormApi.setAction(async function related(conduit) {
67
67
  *
68
68
  * @author Jelle De Loecker <jelle@elevenways.be>
69
69
  * @since 0.1.6
70
- * @version 0.1.6
70
+ * @version 0.2.1
71
71
  *
72
72
  * @param {Conduit} conduit
73
73
  */
@@ -84,7 +84,11 @@ FormApi.setAction(async function queryBuilderData(conduit) {
84
84
  const doc = await model.findByPk(body.$pk);
85
85
 
86
86
  if (doc) {
87
- result = await doc.loadQueryBuilderData(config);
87
+ if (typeof doc.loadQueryBuilderData != 'function') {
88
+ throw new Error('The document class of "' + body.model + '" has no loadQueryBuilderData(config) method');
89
+ } else {
90
+ result = await doc.loadQueryBuilderData(config);
91
+ }
88
92
  }
89
93
  }
90
94
  }
@@ -43,6 +43,15 @@ Button.setAttribute('tabindex', {default: 0});
43
43
  */
44
44
  Button.setAttribute('activatable-states', {type: 'token_list'});
45
45
 
46
+ /**
47
+ * Preconfigured behaviours
48
+ *
49
+ * @author Jelle De Loecker <jelle@elevenways.be>
50
+ * @since 0.2.1
51
+ * @version 0.2.1
52
+ */
53
+ Button.setAttribute('behaviour', {type: 'token_list'});
54
+
46
55
  /**
47
56
  * Action instances
48
57
  *
@@ -57,7 +66,7 @@ Button.setAssignedProperty('action_instance');
57
66
  *
58
67
  * @author Jelle De Loecker <jelle@elevenways.be>
59
68
  * @since 0.2.0
60
- * @version 0.2.0
69
+ * @version 0.2.2
61
70
  */
62
71
  Button.setMethod(function activate() {
63
72
 
@@ -73,7 +82,7 @@ Button.setMethod(function activate() {
73
82
  return;
74
83
  }
75
84
 
76
- let event = this.emit('activate');
85
+ let event = this.emit('activate', {cancelable: true});
77
86
 
78
87
  if (event.defaultPrevented) {
79
88
  return;
@@ -81,6 +90,35 @@ Button.setMethod(function activate() {
81
90
 
82
91
  if (this.action_instance) {
83
92
  this.action_instance.execute(event);
93
+ } else if (this.behaviour.contains('submit')) {
94
+ let form = this.queryUp('al-form, form');
95
+
96
+ if (form) {
97
+ let pledge = form.submit();
98
+
99
+ if (Pledge.isThenable(pledge)) {
100
+ let old_state = this.state;
101
+ this.setState('busy');
102
+
103
+ Pledge.done(pledge, err => {
104
+
105
+ let temp_state,
106
+ duration;
107
+
108
+ if (err) {
109
+ temp_state = 'error';
110
+ duration = 2500;
111
+ } else {
112
+ temp_state = 'done';
113
+ duration = 1000;
114
+ }
115
+
116
+ this.setState(temp_state, duration, old_state);
117
+ });
118
+ } else {
119
+ this.setState('busy', 1000);
120
+ }
121
+ }
84
122
  }
85
123
  });
86
124
 
@@ -88,6 +88,15 @@ Field.setAttribute('readonly', {boolean: true});
88
88
  */
89
89
  Field.setAssignedProperty('widget_settings');
90
90
 
91
+ /**
92
+ * The placeholder
93
+ *
94
+ * @author Jelle De Loecker <jelle@elevenways.be>
95
+ * @since 0.2.2
96
+ * @version 0.2.2
97
+ */
98
+ Field.setAttribute('placeholder');
99
+
91
100
  /**
92
101
  * Get the parent al-form element
93
102
  *
@@ -770,7 +779,7 @@ Field.setMethod(function removeErrors() {
770
779
  *
771
780
  * @author Jelle De Loecker <jelle@elevenways.be>
772
781
  * @since 0.1.0
773
- * @version 0.2.0
782
+ * @version 0.2.1
774
783
  */
775
784
  Field.setMethod(function retained() {
776
785
 
@@ -781,7 +790,9 @@ Field.setMethod(function retained() {
781
790
  id += this.alchemy_form.id + '-';
782
791
  }
783
792
 
784
- id += this.field_name.slug();
793
+ if (this.field_name) {
794
+ id += this.field_name.slug();
795
+ }
785
796
 
786
797
  this.id = id;
787
798
  }
@@ -153,13 +153,38 @@ Form.setMethod(Hawkejs.SERIALIZE_FORM, function serializeForm() {
153
153
  *
154
154
  * @author Jelle De Loecker <jelle@elevenways.be>
155
155
  * @since 0.1.0
156
- * @version 0.1.0
156
+ * @version 0.2.2
157
157
  */
158
158
  Form.setMethod(async function submit() {
159
159
 
160
160
  let result;
161
161
 
162
- if (false && this.model) {
162
+ await this.validate();
163
+
164
+ try {
165
+ result = await hawkejs.scene.onFormSubmit(this, null, {render_error: false});
166
+ } catch (err) {
167
+ if (err instanceof Classes.Alchemy.Error.Validation) {
168
+ this.showError(err);
169
+ } else {
170
+ this.showError(err);
171
+ throw err;
172
+ }
173
+ }
174
+
175
+ return result;
176
+ });
177
+
178
+ /**
179
+ * Validate this form
180
+ *
181
+ * @author Jelle De Loecker <jelle@elevenways.be>
182
+ * @since 0.2.2
183
+ * @version 0.2.2
184
+ */
185
+ Form.setMethod(async function validate() {
186
+
187
+ if (this.model) {
163
188
  let model = alchemy.getModel(this.model);
164
189
 
165
190
  if (model) {
@@ -174,15 +199,6 @@ Form.setMethod(async function submit() {
174
199
  }
175
200
  }
176
201
 
177
- try {
178
- result = await hawkejs.scene.onFormSubmit(this, null, {render_error: false});
179
- } catch (err) {
180
- if (err instanceof Classes.Alchemy.Error.Validation) {
181
- this.showError(err);
182
- } else {
183
- throw err;
184
- }
185
- }
186
202
  });
187
203
 
188
204
  /**
@@ -165,14 +165,23 @@ QueryBuilderEntry.setMethod(async function loadData(config, element) {
165
165
  *
166
166
  * @author Jelle De Loecker <jelle@elevenways.be>
167
167
  * @since 0.1.6
168
- * @version 0.1.6
168
+ * @version 0.2.1
169
169
  */
170
170
  QueryBuilderEntry.setMethod(async function loadValueTypeData(config) {
171
171
 
172
172
  let items = [];
173
173
 
174
- items.push({id: 'variable', title: 'Other variable'});
175
- items.push({id: 'value', title: 'Value'});
174
+ items.push({
175
+ id: 'variable',
176
+ title: 'Variable',
177
+ description: 'Use the value of a variable',
178
+ });
179
+
180
+ items.push({
181
+ id: 'value',
182
+ title: 'Value',
183
+ description: 'Supply the value now',
184
+ });
176
185
 
177
186
  return items;
178
187
  });
@@ -231,12 +240,46 @@ QueryBuilderEntry.setMethod(async function loadVariableData(config) {
231
240
  return result;
232
241
  });
233
242
 
243
+ /**
244
+ * Get the field_select variable definition
245
+ *
246
+ * @author Jelle De Loecker <jelle@elevenways.be>
247
+ * @since 0.2.1
248
+ * @version 0.2.1
249
+ *
250
+ * @return {Promise<Object>}
251
+ */
252
+ QueryBuilderEntry.setMethod(async function getFieldSelectVariableDefinition(variable_name, attempt = 0) {
253
+
254
+ let variable_def = this.field_select.getValueData(variable_name);
255
+
256
+ if (variable_def) {
257
+ return variable_def;
258
+ }
259
+
260
+ await this.field_select.waitForTasks();
261
+ variable_def = this.field_select.getValueData(variable_name);
262
+
263
+ if (!variable_def) {
264
+ variable_def = await this.field_select.awaitValueData(variable_name);
265
+ }
266
+
267
+ if (!variable_def && attempt == 0) {
268
+ // There is an annoying race condition where one select gets valid data,
269
+ // but the other field doesn't. So in that case: wait a little bit and try agian.
270
+ await Pledge.after(250);
271
+ return this.getFieldSelectVariableDefinition(variable_name, 1);
272
+ }
273
+
274
+ return variable_def;
275
+ });
276
+
234
277
  /**
235
278
  * Load operator data
236
279
  *
237
280
  * @author Jelle De Loecker <jelle@elevenways.be>
238
281
  * @since 0.1.6
239
- * @version 0.1.6
282
+ * @version 0.2.1
240
283
  */
241
284
  QueryBuilderEntry.setMethod(async function loadOperatorData(config) {
242
285
 
@@ -246,12 +289,11 @@ QueryBuilderEntry.setMethod(async function loadOperatorData(config) {
246
289
  return;
247
290
  }
248
291
 
249
- let variable_def = this.field_select.getValueData(type);
292
+ let variable_def = await this.getFieldSelectVariableDefinition(type);
250
293
 
251
294
  let items = [];
252
295
 
253
296
  if (variable_def) {
254
-
255
297
  if (!this.type || this.type == 'logical') {
256
298
  items.include(variable_def.getLogicalOperators());
257
299
  } else if (this.type == 'assignment') {
@@ -267,7 +309,7 @@ QueryBuilderEntry.setMethod(async function loadOperatorData(config) {
267
309
  *
268
310
  * @author Jelle De Loecker <jelle@elevenways.be>
269
311
  * @since 0.1.6
270
- * @version 0.1.6
312
+ * @version 0.2.1
271
313
  */
272
314
  QueryBuilderEntry.setMethod(async function showValueInput() {
273
315
 
@@ -277,13 +319,7 @@ QueryBuilderEntry.setMethod(async function showValueInput() {
277
319
  return;
278
320
  }
279
321
 
280
- let entry = this.field_select.getValueData(variable_name);
281
-
282
- if (!entry) {
283
- await this.field_select.waitForTasks();
284
-
285
- entry = this.field_select.getValueData(variable_name);
286
- }
322
+ let entry = await this.getFieldSelectVariableDefinition(variable_name);
287
323
 
288
324
  if (!entry) {
289
325
  return;
@@ -80,7 +80,7 @@ QueryBuilderValue.addElementGetter('value_input_wrapper', '.qb-value-input-wrapp
80
80
  *
81
81
  * @author Jelle De Loecker <jelle@elevenways.be>
82
82
  * @since 0.1.6
83
- * @version 0.1.6
83
+ * @version 0.2.1
84
84
  */
85
85
  QueryBuilderValue.setProperty(function value() {
86
86
 
@@ -105,7 +105,7 @@ QueryBuilderValue.setProperty(function value() {
105
105
  if (input) {
106
106
 
107
107
  let value_explicit = {
108
- type : this.getValueType(input),
108
+ type : this.getValueType(input) || 'unknown_value_type',
109
109
  value : null,
110
110
  }
111
111
 
@@ -129,14 +129,23 @@ QueryBuilderValue.setProperty(function value() {
129
129
  *
130
130
  * @author Jelle De Loecker <jelle@elevenways.be>
131
131
  * @since 0.1.6
132
- * @version 0.1.6
132
+ * @version 0.2.1
133
133
  */
134
134
  QueryBuilderValue.setMethod(async function loadSourceTypeData(config) {
135
135
 
136
136
  let items = [];
137
137
 
138
- items.push({id: 'variable', title: 'Variable'});
139
- items.push({id: 'value', title: 'Value'});
138
+ items.push({
139
+ id: 'variable',
140
+ title: 'Variable',
141
+ description: 'Use the value of a variable',
142
+ });
143
+
144
+ items.push({
145
+ id: 'value',
146
+ title: 'Value',
147
+ description: 'Supply the value now',
148
+ });
140
149
 
141
150
  return items;
142
151
  });
@@ -137,7 +137,11 @@ AlchemySelect.setAssignedProperty('value', function getValue(value) {
137
137
  });
138
138
 
139
139
  /**
140
- * The options property (will be stored in assigned_data)
140
+ * The options property:
141
+ * this contains configuration options of this element.
142
+ *
143
+ * It does NOT contain things to select from.
144
+ * Use `values` for that instead!
141
145
  *
142
146
  * @author Jelle De Loecker <jelle@develry.be>
143
147
  * @since 0.1.0
@@ -240,7 +244,7 @@ AlchemySelect.setProperty(function loaded_items() {
240
244
  */
241
245
  AlchemySelect.setProperty(function loaded_item_count() {
242
246
  if (this.options.values) {
243
- return this.options.values.size;
247
+ return this.options.values.size || 0;
244
248
  } else {
245
249
  return 0;
246
250
  }
@@ -746,6 +750,22 @@ AlchemySelect.setMethod(function getValueData(value) {
746
750
  return data;
747
751
  });
748
752
 
753
+ /**
754
+ * Get the data for a specific value,
755
+ * but make sure it's loaded
756
+ *
757
+ * @author Jelle De Loecker <jelle@develry.be>
758
+ * @since 0.2.1
759
+ * @version 0.2.1
760
+ *
761
+ * @param {*} value
762
+ *
763
+ * @return {Pledge<Object>}
764
+ */
765
+ AlchemySelect.setMethod(function awaitValueData(value) {
766
+ return this._ensureValueData(value);
767
+ });
768
+
749
769
  /**
750
770
  * Ensure the data for the given value is loaded
751
771
  *
@@ -793,7 +813,7 @@ AlchemySelect.setMethod(function _ensureValueData(value) {
793
813
  *
794
814
  * @author Jelle De Loecker <jelle@develry.be>
795
815
  * @since 0.1.0
796
- * @version 0.1.4
816
+ * @version 0.2.1
797
817
  */
798
818
  AlchemySelect.setMethod(function _processPreloadedValues() {
799
819
 
@@ -808,12 +828,14 @@ AlchemySelect.setMethod(function _processPreloadedValues() {
808
828
  items : [],
809
829
  };
810
830
 
811
- let item,
812
- key;
831
+ let item;
813
832
 
814
833
  let enum_values = new Classes.Alchemy.Map.Enum(values),
815
834
  value;
816
835
 
836
+ // Make sure to use this new enum instance from now on
837
+ this.options.values = enum_values;
838
+
817
839
  for (let key of enum_values.keys()) {
818
840
  value = enum_values.get(key);
819
841
 
@@ -847,9 +869,7 @@ AlchemySelect.setMethod(function _processResponseData(response, page) {
847
869
  this.total_item_count = response.available;
848
870
  }
849
871
 
850
- let records = response.items || response.records,
851
- record,
852
- item;
872
+ let records = response.items || response.records;
853
873
 
854
874
  if (!records) {
855
875
  records = [];
@@ -955,8 +975,7 @@ AlchemySelect.setMethod(function applyFetchedData(err, result, config) {
955
975
  AlchemySelect.setMethod(function recreateDropdownElements() {
956
976
 
957
977
  let items = this.loaded_items,
958
- item,
959
- key;
978
+ item;
960
979
 
961
980
  Hawkejs.removeChildren(this.dropdown_content);
962
981
 
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "alchemy-form",
3
3
  "description": "Form plugin for Alchemy",
4
- "version": "0.2.0",
4
+ "version": "0.2.2",
5
5
  "repository": {
6
6
  "type" : "git",
7
7
  "url" : "https://github.com/11ways/alchemy-form.git"
8
8
  },
9
9
  "peerDependencies": {
10
10
  "alchemymvc" : ">=1.2.0",
11
- "alchemy-media" : "~0.7.0"
11
+ "alchemy-media" : ">=0.7.2"
12
12
  },
13
13
  "license": "MIT",
14
14
  "engines": {
@@ -1,4 +1,6 @@
1
1
  <div class="qb-field-wrapper">
2
+ <small class="small-title">Field</small>
3
+
2
4
  <al-select
3
5
  class="qb-field"
4
6
  #dataprovider={% self %}
@@ -7,6 +9,8 @@
7
9
  ></al-select>
8
10
  </div>
9
11
  <div class="qb-operator-wrapper">
12
+ <small class="small-title">Operator</small>
13
+
10
14
  <al-select
11
15
  class="qb-operator"
12
16
  #dataprovider={% self %}
@@ -15,15 +19,20 @@
15
19
  ></al-select>
16
20
  </div>
17
21
  <div class="qb-value-wrapper">
18
- <al-select
19
- class="qb-value-type"
20
- #dataprovider={% self %}
21
- value-item-template="form/select/qb_item"
22
- option-item-template="form/select/qb_item"
23
- ></al-select>
22
+ <small class="small-title">Value</small>
23
+
24
+ <div class="qb-value-nested-wrapper">
25
+
26
+ <al-select
27
+ class="qb-value-type"
28
+ #dataprovider={% self %}
29
+ value-item-template="form/select/qb_item"
30
+ option-item-template="form/select/qb_item"
31
+ ></al-select>
24
32
 
25
- <div class="qb-value-input-wrapper">
33
+ <div class="qb-value-input-wrapper">
26
34
 
35
+ </div>
27
36
  </div>
28
37
  </div>
29
38
  <div class="qb-delete-wrapper">
@@ -10,4 +10,5 @@
10
10
  type={% type %}
11
11
  form=<% form_id %>
12
12
  name=<% path %>
13
+ placeholder={% alchemy_field.placeholder %}
13
14
  >