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
@@ -1,75 +1,191 @@
1
- al-table {
2
- display: flex;
3
- flex-flow: column;
4
- border: 1px solid #dadee0;
5
- background-color: white;
6
-
7
- table {
8
- width: 100%;
9
- color: inherit;
10
- }
1
+ @use "styleboost";
2
+ @use "styleboost/inputs";
11
3
 
12
- thead {
13
- td, th {
14
- background-color: #F4F5F9;
15
- padding: 1rem;
16
- border-bottom: 1px solid #dadee0;
17
- text-transform: uppercase;
18
- font-weight: 500;
19
- font-size: 0.9rem;
20
-
21
- al-icon {
22
- margin-left: 1rem;
4
+ @layer defaults {
5
+ $accent: styleboost.themePropertyVar('color', 'accent', 'table');
6
+ $accent_rgb: styleboost.themePropertyVar('color', 'accent-rgb', 'table');
7
+
8
+ al-table {
9
+ // Define custom properties for al-table
10
+ --al-table-color-header-bg: var(--color-t-current-bg-subtle, var(--color-t-primary-bg-subtle));
11
+ --al-table-color-header-fg: var(--color-t-current-bg-subtle-text, var(--color-t-primary-bg-subtle-text));
12
+ --al-table-color-footer-bg: var(--color-t-current-bg-subtle, var(--color-t-primary-bg-subtle));
13
+ --al-table-color-footer-fg: var(--color-t-current-bg-subtle-text, var(--color-t-primary-bg-subtle-text));
14
+ --al-table-color-border: var(--color-t-current-bg-subtle, var(--color-t-primary-bg-subtle));
15
+ --al-table-color-selected-bg: var(--color-t-current-bg-soft, var(--color-t-primary-bg-soft));
16
+ --al-table-color-selected-fg: var(--color-t-current-bg-soft-text, var(--color-t-primary-bg-soft-text));
17
+
18
+ display: flex;
19
+ flex-flow: column;
20
+ border-radius: var(--size-p-table-border-radius);
21
+ border-width: var(--size-p-table-border);
22
+ border-style: var(--style-p-table-border);
23
+ border-color: var(--al-table-color-border);
24
+ background-color: var(--color-p-table-bg);
25
+ overflow: hidden;
26
+
27
+ .aft-content {
28
+ overflow-x: auto;
29
+ scrollbar-width: thin;
30
+ }
31
+
32
+ table {
33
+ @extend .table;
34
+ width: 100%;
35
+ border: none;
36
+ border-radius: 0;
37
+ }
38
+
39
+ &.table-hover {
40
+ table {
41
+ @extend .table-hover;
23
42
  }
43
+ }
24
44
 
25
- &.sortable {
26
- cursor: pointer;
45
+ &.table-striped {
46
+ table {
47
+ @extend .table-striped;
27
48
  }
49
+ }
28
50
 
29
- a.sorting-anchor {
30
- text-decoration: none;
31
- color: inherit;
51
+ &.table-bordered {
52
+ table {
53
+ @extend .table-bordered;
32
54
  }
33
55
  }
34
56
 
35
- th input {
36
- width: 100%;
37
- padding: 0px 12px;
38
- border: 1px solid var(--color-input-border);
39
- color: var(--text-color);
40
- height: 2rem;
57
+ thead {
58
+ td,
59
+ th {
60
+ background-color: var(--al-table-color-header-bg);
61
+ color: var(--al-table-color-header-fg);
62
+ text-transform: uppercase;
63
+ font-weight: 500;
64
+ font-size: 0.9rem;
65
+ border-bottom-color: var(--al-table-color-border);
66
+
67
+ al-icon {
68
+ margin-left: 0.5rem;
69
+ vertical-align: middle;
70
+ }
71
+
72
+ &.sortable {
73
+ cursor: pointer;
74
+ user-select: none;
75
+
76
+ &:hover {
77
+ opacity: 0.8;
78
+ }
79
+ }
80
+
81
+ a.sorting-anchor {
82
+ text-decoration: none;
83
+ color: inherit;
84
+ display: block;
85
+ }
86
+ }
87
+
88
+ th input {
89
+ @extend .input-defaults;
90
+ width: 100%;
91
+ height: 2rem;
92
+ font-size: 0.875rem;
93
+ padding: 0.25rem 0.5rem;
94
+ }
41
95
  }
42
- }
43
96
 
44
- td, th {
45
- padding: 0.5rem 1rem;
46
- border-bottom: 1px solid #EDEFF0;
47
- cursor: default;
48
- }
97
+ tbody {
98
+ tr.aft-selected {
99
+ td,
100
+ th {
101
+ --color-s-table-bg: var(--al-table-color-selected-bg);
102
+ --color-s-table-bg-text: var(--al-table-color-selected-fg);
103
+ }
104
+ }
105
+ }
49
106
 
50
- td al-field[field-type="fixed_decimal"] {
51
- text-align: right;
52
- }
107
+ td al-field[field-type="fixed_decimal"] {
108
+ text-align: right;
109
+ }
53
110
 
54
- tr:last-of-type {
55
- td, th {
56
- border-bottom: 0;
111
+ footer {
112
+ background-color: var(--al-table-color-footer-bg);
113
+ color: var(--al-table-color-footer-fg);
114
+ padding: 0.5rem 1rem;
115
+ border-top: var(--size-p-table-border-inner) var(--style-p-table-border) var(--al-table-color-border);
116
+
117
+ &:empty {
118
+ display: none;
119
+ }
57
120
  }
58
121
  }
59
122
 
60
- tr.aft-selected {
123
+ old-al-table {
124
+
125
+
126
+ border: 1px solid #dadee0;
127
+ background-color: white;
128
+
129
+
130
+
131
+ thead {
132
+ td, th {
133
+ background-color: #F4F5F9;
134
+ padding: 1rem;
135
+ border-bottom: 1px solid #dadee0;
136
+ text-transform: uppercase;
137
+ font-weight: 500;
138
+ font-size: 0.9rem;
139
+
140
+ al-icon {
141
+ margin-left: 1rem;
142
+ }
143
+
144
+ &.sortable {
145
+ cursor: pointer;
146
+ }
147
+
148
+ a.sorting-anchor {
149
+ text-decoration: none;
150
+ color: inherit;
151
+ }
152
+ }
153
+
154
+ th input {
155
+ width: 100%;
156
+ padding: 0px 12px;
157
+ border: 1px solid var(--color-input-border);
158
+ color: var(--text-color);
159
+ height: 2rem;
160
+ }
161
+ }
162
+
61
163
  td, th {
62
- background-color: #edeff5;
164
+ padding: 0.5rem 1rem;
165
+ border-bottom: 1px solid #EDEFF0;
166
+ cursor: default;
167
+ }
168
+
169
+ tr:last-of-type {
170
+ td, th {
171
+ border-bottom: 0;
172
+ }
173
+ }
174
+
175
+ tr.aft-selected {
176
+ td, th {
177
+ background-color: #edeff5;
178
+ }
63
179
  }
64
- }
65
180
 
66
- footer {
67
- border-top: 1px solid #EDEFF0;
68
- background-color: #F4F5F9;
69
- padding: 0.5rem 1rem;
181
+ footer {
182
+ border-top: 1px solid #EDEFF0;
183
+ background-color: #F4F5F9;
184
+ padding: 0.5rem 1rem;
70
185
 
71
- &:empty {
72
- display: none;
186
+ &:empty {
187
+ display: none;
188
+ }
73
189
  }
74
190
  }
75
191
  }
@@ -1,46 +1,36 @@
1
- al-tab-list {
2
- display: flex;
3
- gap: 0.75rem;
4
- }
1
+ @use "styleboost";
2
+
3
+ @layer defaults {
4
+ al-tab-list {
5
+ display: flex;
6
+ gap: 0.75rem;
7
+ }
8
+
9
+ al-tab-button {
10
+ @extend .button;
11
+ z-index: 1;
5
12
 
6
- al-tab-button {
7
- font-size: 1.1rem;
8
- border: none;
9
- display: table-cell;
10
- text-align: center;
11
- vertical-align: middle;
12
- overflow: hidden;
13
- cursor: pointer;
14
- padding: 0.5rem 1rem;
15
- border-radius: 5px 5px 0 0;
16
- border: 1px solid var(--alchemy-tab-button-border, gray);
17
- border-bottom: 1px solid transparent;
18
- z-index: 1;
19
- background: var(--alchemy-tab-button-background, white);
20
- user-select: none;
13
+ al-icon {
14
+ margin-right: 0.5rem;
15
+ }
21
16
 
22
- al-icon {
23
- margin-right: 0.5rem;
17
+ &[active] {
18
+ font-weight: bold;
19
+ z-index: 3;
20
+ }
24
21
  }
25
22
 
26
- &[active] {
27
- font-weight: bold;
28
- z-index: 3;
29
- border-bottom: 1px solid var(--alchemy-tab-panel-background, white);
23
+ al-tab-panel {
24
+ display: block;
25
+ position: relative;
26
+
27
+ padding: 0.7rem;
28
+ z-index: 2;
29
+ margin-top: -1px;
30
30
  }
31
31
  }
32
32
 
33
33
  al-tab-panel {
34
- display: block;
35
- position: relative;
36
-
37
- padding: 0.7rem;
38
- border: 1px solid var(--alchemy-tab-panel-border, gray);
39
- border-radius: 0 0 5px 5px;
40
- background: var(--alchemy-tab-panel-background, white);
41
- z-index: 2;
42
- margin-top: -1px;
43
-
44
34
  &:not([active]) {
45
35
  display: none !important;
46
36
  }
File without changes
@@ -1,4 +1,3 @@
1
- @use "_badge.scss";
2
1
  @use "_button.scss";
3
2
  @use "_code_input.scss";
4
3
  @use "_feedback_input.scss";
@@ -17,3 +16,4 @@
17
16
  @use "_apex_charts.scss";
18
17
  @use "_virtual_scroll.scss";
19
18
  @use "_settings_editor.scss";
19
+ @use "_enum_badge.scss";
File without changes
package/config/routes.js CHANGED
@@ -23,4 +23,14 @@ Plugin.addRoute({
23
23
  policy : 'logged_in',
24
24
  is_system_route : true,
25
25
  permission : 'model.{model_name}.recompute.{field}',
26
+ });
27
+
28
+ Plugin.addRoute({
29
+ name : 'FormApi#enumInfo',
30
+ methods : 'get',
31
+ paths : '/api/form/data/enum_info/{model_name}/{id}',
32
+ policy : 'logged_in',
33
+ cache : true,
34
+ is_system_route : true,
35
+ permission : 'model.{model_name}.enum_info',
26
36
  });
@@ -88,6 +88,34 @@ FormApi.setAction(async function related(conduit) {
88
88
  conduit.end(result);
89
89
  });
90
90
 
91
+ /**
92
+ * The enum info action:
93
+ * Return information about a record for use in an enum element
94
+ *
95
+ * @author Jelle De Loecker <jelle@elevenways.be>
96
+ * @since 0.3.0
97
+ * @version 0.3.0
98
+ *
99
+ * @param {Conduit} conduit
100
+ */
101
+ FormApi.setAction(async function enumInfo(conduit, model_name, id) {
102
+
103
+ const model = this.getModel(model_name);
104
+ let record = await model.findByPk(id);
105
+
106
+ if (!record) {
107
+ return conduit.notFound();
108
+ }
109
+
110
+ let result = {
111
+ title : record.getDisplayFieldValue(),
112
+ icon : record.enum_icon || record.icon,
113
+ color : record.enum_color || record.color,
114
+ };
115
+
116
+ conduit.end(result);
117
+ });
118
+
91
119
  /**
92
120
  * The action to recompute field values
93
121
  *
@@ -70,7 +70,7 @@ Base.setStatic(function addParentTypeGetter(name, type) {
70
70
  *
71
71
  * @author Jelle De Loecker <jelle@elevenways.be>
72
72
  * @since 0.1.11
73
- * @version 0.1.11
73
+ * @version 0.3.0
74
74
  */
75
75
  Base.setAttribute('purpose', function getPurpose(value) {
76
76
 
@@ -78,10 +78,11 @@ Base.setAttribute('purpose', function getPurpose(value) {
78
78
  return value;
79
79
  }
80
80
 
81
+ // If could also be in the view-type attribute
81
82
  value = this.getAttribute('view-type');
82
83
 
83
- if (!value && this.alchemy_form) {
84
- value = this.alchemy_form.view_type;
84
+ if (!value) {
85
+ value = this.getParentFieldElement()?.purpose;
85
86
  }
86
87
 
87
88
  // Fallback to the "edit" type
@@ -98,9 +99,20 @@ Base.setAttribute('purpose', function getPurpose(value) {
98
99
  *
99
100
  * @author Jelle De Loecker <jelle@elevenways.be>
100
101
  * @since 0.1.11
101
- * @version 0.1.11
102
+ * @version 0.3.0
102
103
  */
103
- Base.setAttribute('mode');
104
+ Base.setAttribute('mode', function getMode(value) {
105
+
106
+ if (value) {
107
+ return value;
108
+ }
109
+
110
+ if (!value) {
111
+ value = this.getParentFieldElement()?.mode;
112
+ }
113
+
114
+ return value;
115
+ });
104
116
 
105
117
  /**
106
118
  * The zone determines where the form/field is being used.
@@ -116,8 +128,8 @@ Base.setAttribute('zone', function getZone(value) {
116
128
  return value;
117
129
  }
118
130
 
119
- if (!value && this.alchemy_form) {
120
- value = this.alchemy_form.zone;
131
+ if (!value) {
132
+ value = this.getParentFieldElement()?.zone;
121
133
  }
122
134
 
123
135
  return value;
@@ -163,14 +175,14 @@ Base.setProperty(function view_type() {
163
175
  *
164
176
  * @author Jelle De Loecker <jelle@elevenways.be>
165
177
  * @since 0.1.0
166
- * @version 0.1.0
178
+ * @version 0.3.0
167
179
  */
168
180
  Base.setProperty(function wrapper_type() {
169
181
 
170
182
  var value = this.getAttribute('wrapper-type');
171
183
 
172
- if (!value && this.alchemy_form) {
173
- value = this.alchemy_form.wrapper_type;
184
+ if (!value) {
185
+ value = this.getParentFieldElement()?.wrapper_type;
174
186
  }
175
187
 
176
188
  if (value == 'false') {
@@ -198,13 +210,14 @@ Base.setProperty(function wrapper_type() {
198
210
  *
199
211
  * @author Jelle De Loecker <jelle@elevenways.be>
200
212
  * @since 0.1.4
201
- * @version 0.1.4
213
+ * @version 0.3.0
202
214
  */
203
215
  Base.setProperty(function field_path_in_current_schema() {
204
216
 
205
- let result = [],
217
+ let ancestor,
218
+ result = [],
206
219
  parent = this.getParentField(),
207
- name;
220
+ name;
208
221
 
209
222
  name = this.getPathEntryName();
210
223
 
@@ -212,17 +225,28 @@ Base.setProperty(function field_path_in_current_schema() {
212
225
  result.push(name);
213
226
  }
214
227
 
215
- while (parent && !(parent instanceof Classes.Alchemy.Element.Form.FieldSchema)) {
216
- name = parent.getPathEntryName();
228
+ ancestor = parent;
229
+
230
+ while (ancestor && !(ancestor instanceof Classes.Alchemy.Element.Form.FieldSchema)) {
231
+ name = ancestor.getPathEntryName();
217
232
 
218
233
  if (name) {
219
234
  result.unshift(name);
220
235
  }
221
236
 
222
- parent = parent.getParentField();
237
+ ancestor = ancestor.getParentField();
223
238
  }
224
239
 
225
- return result.join('.');
240
+ let result_path = result.join('.');
241
+
242
+ // Don't allow a child field to have the same path as its parent
243
+ // This can happen when we use an `al-field` element that isn't
244
+ // part of a schema for some reason.
245
+ if (result.length == 1 && parent?.field_path_in_current_schema === result_path) {
246
+ return null;
247
+ }
248
+
249
+ return result_path;
226
250
  });
227
251
 
228
252
  /**
@@ -262,11 +286,24 @@ Base.setProperty(function field_path_in_record() {
262
286
  *
263
287
  * @author Jelle De Loecker <jelle@elevenways.be>
264
288
  * @since 0.1.3
265
- * @version 0.1.4
289
+ * @version 0.3.0
266
290
  *
267
291
  * @return {Alchemy.Element.Form.Base}
268
292
  */
269
293
  Base.setMethod(function getParentField() {
294
+ return this.getParentFieldElement();
295
+ });
296
+
297
+ /**
298
+ * Get the parent field/field-entry element
299
+ *
300
+ * @author Jelle De Loecker <jelle@elevenways.be>
301
+ * @since 0.1.3
302
+ * @version 0.3.0
303
+ *
304
+ * @return {Alchemy.Element.Form.Base}
305
+ */
306
+ Base.setMethod(function getParentFieldElement() {
270
307
 
271
308
  let parent = this.parentElement;
272
309
 
@@ -326,18 +363,20 @@ Base.setMethod(function getPathOrigin() {
326
363
  });
327
364
 
328
365
  /**
329
- * Resolve the given path
366
+ * Resolve the given path to an array
330
367
  *
331
368
  * @author Jelle De Loecker <jelle@elevenways.be>
332
369
  * @since 0.1.3
333
- * @version 0.1.3
370
+ * @version 0.3.0
334
371
  *
335
- * @param {String} name The name to resolve
336
- * @param {String} origin The origin to use (optional)
372
+ * @param {string} name The name to resolve
373
+ * @param {string} origin The origin to use (optional)
337
374
  *
338
- * @return {String}
375
+ * @return {string[]}
339
376
  */
340
- Base.setMethod(function resolvePath(name, origin) {
377
+ Base.setMethod(function resolvePathToArray(name, origin) {
378
+
379
+ name = name.split('.');
341
380
 
342
381
  if (origin == null) {
343
382
  origin = this.getPathOrigin();
@@ -358,7 +397,23 @@ Base.setMethod(function resolvePath(name, origin) {
358
397
  }
359
398
 
360
399
  origin.pop();
361
- origin.push(name);
362
-
363
- return origin.join('.');
400
+ origin.push(...name);
401
+
402
+ return origin;
403
+ });
404
+
405
+ /**
406
+ * Resolve the given path
407
+ *
408
+ * @author Jelle De Loecker <jelle@elevenways.be>
409
+ * @since 0.1.3
410
+ * @version 0.3.0
411
+ *
412
+ * @param {String} name The name to resolve
413
+ * @param {String} origin The origin to use (optional)
414
+ *
415
+ * @return {String}
416
+ */
417
+ Base.setMethod(function resolvePath(name, origin) {
418
+ return this.resolvePathToArray(name, origin).join('.');
364
419
  });
@@ -61,12 +61,21 @@ Button.setAttribute('behaviour', {type: 'token_list'});
61
61
  */
62
62
  Button.setAssignedProperty('action_instance');
63
63
 
64
+ /**
65
+ * The last error
66
+ *
67
+ * @author Jelle De Loecker <jelle@elevenways.be>
68
+ * @since 0.3.0
69
+ * @version 0.3.0
70
+ */
71
+ Button.setProperty('last_error', null);
72
+
64
73
  /**
65
74
  * Emit the activate event
66
75
  *
67
76
  * @author Jelle De Loecker <jelle@elevenways.be>
68
77
  * @since 0.2.0
69
- * @version 0.2.2
78
+ * @version 0.3.0
70
79
  */
71
80
  Button.setMethod(function activate() {
72
81
 
@@ -105,6 +114,8 @@ Button.setMethod(function activate() {
105
114
  let temp_state,
106
115
  duration;
107
116
 
117
+ this.last_error = err;
118
+
108
119
  if (err) {
109
120
  temp_state = 'error';
110
121
  duration = 2500;