alchemy-form 0.2.0 → 0.2.1
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 +12 -0
- package/assets/stylesheets/form/elements/_query_builder.scss +7 -1
- package/controller/form_api_controller.js +6 -2
- package/element/al_button.js +39 -1
- package/element/al_field.js +4 -2
- package/element/al_form.js +4 -2
- package/element/al_query_builder_entry.js +50 -14
- package/element/al_query_builder_value.js +14 -5
- package/element/al_select.js +29 -10
- package/package.json +2 -2
- package/view/form/elements/query_builder_entry.hwk +16 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## 0.2.1 (2022-12-23)
|
|
2
|
+
|
|
3
|
+
* Enable `al-form` model validation
|
|
4
|
+
* Don't throw an error when a `al-field` element has no field name configured
|
|
5
|
+
* Overwrite preloaded `al-select` values with their processed variant
|
|
6
|
+
* Throw an error when a document does not have a `loadQueryBuilderData` method while using the `FormApi#queryBuilderData` action
|
|
7
|
+
* `QueryBuilderValue` instances will now always add a type to the `value_explicit` object
|
|
8
|
+
* Make `al-button` elements submit forms automatically
|
|
9
|
+
* Fix operator `al-select` element staying empty in a query builder entry
|
|
10
|
+
* Work around a race condition in the query builder
|
|
11
|
+
* Add `behaviour` attribute to `al-button` element
|
|
12
|
+
|
|
1
13
|
## 0.2.0 (2022-11-02)
|
|
2
14
|
|
|
3
15
|
* 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
|
|
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
|
-
|
|
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
|
}
|
package/element/al_button.js
CHANGED
|
@@ -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.
|
|
69
|
+
* @version 0.2.1
|
|
61
70
|
*/
|
|
62
71
|
Button.setMethod(function activate() {
|
|
63
72
|
|
|
@@ -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
|
|
package/element/al_field.js
CHANGED
|
@@ -770,7 +770,7 @@ Field.setMethod(function removeErrors() {
|
|
|
770
770
|
*
|
|
771
771
|
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
772
772
|
* @since 0.1.0
|
|
773
|
-
* @version 0.2.
|
|
773
|
+
* @version 0.2.1
|
|
774
774
|
*/
|
|
775
775
|
Field.setMethod(function retained() {
|
|
776
776
|
|
|
@@ -781,7 +781,9 @@ Field.setMethod(function retained() {
|
|
|
781
781
|
id += this.alchemy_form.id + '-';
|
|
782
782
|
}
|
|
783
783
|
|
|
784
|
-
|
|
784
|
+
if (this.field_name) {
|
|
785
|
+
id += this.field_name.slug();
|
|
786
|
+
}
|
|
785
787
|
|
|
786
788
|
this.id = id;
|
|
787
789
|
}
|
package/element/al_form.js
CHANGED
|
@@ -153,13 +153,13 @@ 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
|
|
156
|
+
* @version 0.2.1
|
|
157
157
|
*/
|
|
158
158
|
Form.setMethod(async function submit() {
|
|
159
159
|
|
|
160
160
|
let result;
|
|
161
161
|
|
|
162
|
-
if (
|
|
162
|
+
if (this.model) {
|
|
163
163
|
let model = alchemy.getModel(this.model);
|
|
164
164
|
|
|
165
165
|
if (model) {
|
|
@@ -183,6 +183,8 @@ Form.setMethod(async function submit() {
|
|
|
183
183
|
throw err;
|
|
184
184
|
}
|
|
185
185
|
}
|
|
186
|
+
|
|
187
|
+
return result;
|
|
186
188
|
});
|
|
187
189
|
|
|
188
190
|
/**
|
|
@@ -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
|
|
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({
|
|
175
|
-
|
|
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
|
|
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.
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
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({
|
|
139
|
-
|
|
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
|
});
|
package/element/al_select.js
CHANGED
|
@@ -137,7 +137,11 @@ AlchemySelect.setAssignedProperty('value', function getValue(value) {
|
|
|
137
137
|
});
|
|
138
138
|
|
|
139
139
|
/**
|
|
140
|
-
* The options property
|
|
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
|
|
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.
|
|
4
|
+
"version": "0.2.1",
|
|
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.
|
|
11
|
+
"alchemy-media" : "~0.7.1"
|
|
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
|
-
<
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
33
|
+
<div class="qb-value-input-wrapper">
|
|
26
34
|
|
|
35
|
+
</div>
|
|
27
36
|
</div>
|
|
28
37
|
</div>
|
|
29
38
|
<div class="qb-delete-wrapper">
|