alchemy-widget 0.1.5 → 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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,23 @@
1
+ ## 0.1.6 (2022-10-12)
2
+
3
+ * Allow hiding widgets from the add-menu
4
+ * Let actions return their button contents as elements instead of only html
5
+ * Cancel clicks on widgets when editing them
6
+ * Fix getting the `hawkejs_renderer` instance in a widget
7
+ * Use `alchemy-chimera` style for the widget configuration dialog
8
+ * Make renders wait for widgets that have to render their content asynchronously
9
+ * Allow setting the element to use in a Text widget
10
+ * Fix Header-widget level actions
11
+ * Load the icon fonts as soon as the editor starts
12
+ * Make the `rerender` method async
13
+ * Use `child_class` property in the populate method
14
+ * Add filter logic to widgets for getting specific values
15
+ * Add abstract `Partial` widget class, to easily create a new widget with a pre-defined layout
16
+ * Wait for widgets to render their contents before starting editor
17
+ * Add `can_be_removed` property to widget elements
18
+ * Add `can_be_moved` property to widget elements
19
+ * Throw an error if `alchemy-form` is loaded before this plugin
20
+
1
21
  ## 0.1.5 (2022-07-14)
2
22
 
3
23
  * Unselect widgets when stopping the editor
@@ -1,5 +1,12 @@
1
1
  @import "alchemy-widget-symbols.scss";
2
2
 
3
+ alchemy-widgets,
4
+ alchemy-widgets-row,
5
+ alchemy-widgets-column,
6
+ alchemy-widget {
7
+
8
+ }
9
+
3
10
  alchemy-widgets,
4
11
  alchemy-widgets-row,
5
12
  alchemy-widgets-column {
@@ -11,7 +18,14 @@ alchemy-widgets-column {
11
18
  }
12
19
 
13
20
  > * {
14
- flex: 10;
21
+ flex: 10 10 auto;
22
+ }
23
+ }
24
+
25
+ alchemy-widget {
26
+ &.aw-editing {
27
+ outline: 2px dashed rgba(0, 0, 0, 0.3);
28
+ outline-offset: -2px;
15
29
  }
16
30
  }
17
31
 
@@ -35,7 +49,7 @@ alchemy-widgets-column {
35
49
 
36
50
  > alchemy-widget-add-area {
37
51
  position: absolute;
38
- bottom: 1.5rem;
52
+ bottom: 0.2rem;
39
53
  left: 50%;
40
54
  transform: translateX(-50%);
41
55
  }
@@ -67,6 +81,18 @@ alchemy-widgets-row {
67
81
  }
68
82
  }
69
83
 
84
+ alchemy-widget-add-area {
85
+ background: rgba(255,255,255,0.5);
86
+ padding: 0.5rem 2rem;
87
+ border-radius: 2rem;
88
+ z-index: 99999999;
89
+
90
+ .widget-button {
91
+ min-height: 2.5rem;
92
+ padding: 1rem;
93
+ }
94
+ }
95
+
70
96
  alchemy-widgets-row,
71
97
  alchemy-widgets > alchemy-widgets-column,
72
98
  alchemy-widgets-column > alchemy-widgets-column,
@@ -164,7 +190,10 @@ alchemy-widget {
164
190
  min-height: 2rem;
165
191
 
166
192
  &:hover {
167
- background: rgba(60, 60, 120, 0.1);
193
+ background: rgba(60, 60, 120, 0.2);
194
+
195
+ // This actually causes some glitches on Firefox :/
196
+ backdrop-filter: blur(4px);
168
197
  }
169
198
  }
170
199
 
@@ -181,6 +210,7 @@ alchemy-widget-toolbar {
181
210
  background-color: white;
182
211
  border-radius: 4px;
183
212
  border: 1px solid #dadada;
213
+ font-size: 2rem;
184
214
 
185
215
  &[hidden] {
186
216
  display: none;
@@ -200,8 +230,8 @@ alchemy-widget-context {
200
230
  }
201
231
 
202
232
  alchemy-widget-toolbar {
203
- min-height: 2rem;
204
- min-width: 2rem;
233
+ min-height: 2.5rem;
234
+ min-width: 2.5rem;
205
235
  display: flex;
206
236
 
207
237
  > * {
@@ -209,7 +239,9 @@ alchemy-widget-toolbar {
209
239
  }
210
240
 
211
241
  .aw-toolbar-button {
242
+ font-size: 2.5rem;
212
243
  border-radius: 4px;
244
+ padding: 0.6rem;
213
245
 
214
246
  &:hover {
215
247
  color: rgb(112, 118, 132);
@@ -240,6 +272,22 @@ alchemy-widget[type="header"] {
240
272
  }
241
273
  }
242
274
 
275
+ alchemy-widget[type="text"] {
276
+
277
+ &.aw-editing {
278
+ &,
279
+ & > * {
280
+ min-width: 5rem;
281
+ min-height: 3rem;
282
+ }
283
+
284
+ & > * {
285
+ display: inline-block;
286
+ width: 100%;
287
+ }
288
+ }
289
+ }
290
+
243
291
  .aw-toolbar-button {
244
292
 
245
293
  .aw-header-h {
@@ -255,4 +303,14 @@ alchemy-widget[type="header"] {
255
303
 
256
304
  table-of-contents {
257
305
  display: block;
306
+ }
307
+
308
+ [data-he-template="widget/widget_config"] {
309
+ alchemy-label {
310
+ padding: 0.5rem;
311
+
312
+ [data-he-name="field-title"] {
313
+ display: block;
314
+ }
315
+ }
258
316
  }
package/bootstrap.js CHANGED
@@ -1,3 +1,7 @@
1
+ if (alchemy.plugins.form) {
2
+ throw new Error('The alchemy-form plugin has to be loaded AFTER alchemy-widget');
3
+ }
4
+
1
5
  Router.add({
2
6
  name : 'AlchemyWidgets#save',
3
7
  methods : 'post',
@@ -12,7 +12,7 @@ const AlchemyWidgets = Function.inherits('Alchemy.Controller', 'AlchemyWidgets')
12
12
  *
13
13
  * @author Jelle De Loecker <jelle@elevenways.be>
14
14
  * @since 0.1.5
15
- * @version 0.1.5
15
+ * @version 0.1.6
16
16
  *
17
17
  * @param {Object[]} fields
18
18
  *
@@ -59,18 +59,94 @@ AlchemyWidgets.setMethod(async function aggregate(widgets) {
59
59
  result[model.name] = record;
60
60
  }
61
61
  }
62
-
62
+
63
+ let field_definition;
64
+
65
+ if (widget.value_path) {
66
+ field_definition = model.getField(widget.field + '.' + widget.value_path);
67
+ } else {
68
+ field_definition = model.getField(widget.field);
69
+ }
70
+
71
+ if (!field_definition) {
72
+ continue;
73
+ }
74
+
75
+ // The optional translation key
63
76
  let field_language = widget.field_languages?.[widget.field];
64
-
65
- if (field_language) {
66
- if (!record[widget.field]) {
67
- record[widget.field] = {};
77
+ let target_field_value = record[widget.field];
78
+ let target_container;
79
+ let target_key;
80
+ let path_for_language;
81
+
82
+ // Do incredibly complicated filter stuff
83
+ if (widget.filter_value) {
84
+
85
+ if (!widget.filter_target || !widget.value_path) {
86
+ continue;
87
+ }
88
+
89
+ // Create the root field if needed
90
+ if (!target_field_value) {
91
+ target_field_value = [];
92
+ record[widget.field] = target_field_value;
93
+ }
94
+
95
+ if (target_field_value && Array.isArray(target_field_value)) {
96
+ target_key = widget.value_path;
97
+
98
+ for (let index = 0; index < target_field_value.length; index++) {
99
+ let entry = target_field_value[index];
100
+
101
+ if (entry[widget.filter_target] == widget.filter_value) {
102
+ target_container = entry;
103
+ path_for_language = widget.field + '.' + index + '.' + target_key;
104
+ break;
105
+ }
106
+ }
107
+
108
+ if (!target_container) {
109
+ target_container = {
110
+ [widget.filter_target] : widget.filter_value,
111
+ [target_key] : {},
112
+ };
113
+
114
+ let new_index = target_field_value.push(target_container) - 1;
115
+ path_for_language = widget.field + '.' + new_index + '.' + target_key;
116
+ }
68
117
  }
69
-
70
- record[widget.field][field_language] = widget.value;
71
118
  } else {
72
- record[widget.field] = widget.value;
119
+ target_container = record;
120
+ target_key = widget.field;
121
+ }
122
+
123
+ if (!field_language && path_for_language) {
124
+ field_language = widget.field_languages?.[path_for_language];
125
+ }
126
+
127
+ if (!field_language && field_definition.is_translatable) {
128
+ field_language = this.conduit.active_prefix;
129
+
130
+ if (!field_language) {
131
+ continue;
132
+ }
73
133
  }
134
+
135
+ if (field_language) {
136
+ if (!target_container) {
137
+ target_container = record[widget.field] = {};
138
+ }
139
+
140
+
141
+ if (!target_container[target_key]) {
142
+ target_container[target_key] = {};
143
+ }
144
+
145
+ target_container = target_container[target_key];
146
+ target_key = field_language;
147
+ }
148
+
149
+ target_container[target_key] = widget.value;
74
150
  }
75
151
 
76
152
  return Object.values(result);
@@ -114,6 +114,46 @@ Base.setProperty(function can_be_saved() {
114
114
  return true;
115
115
  });
116
116
 
117
+ /**
118
+ * Can this widget be removed?
119
+ *
120
+ * @author Jelle De Loecker <jelle@elevenways.be>
121
+ * @since 0.1.6
122
+ * @version 0.1.6
123
+ *
124
+ * @type {Boolean}
125
+ */
126
+ Base.setProperty(function can_be_removed() {
127
+
128
+ // Widgets without a "parent_instance" are mostly
129
+ // hardcoded in some template (like in a Partial widget)
130
+ if (!this.instance?.parent_instance) {
131
+ return false;
132
+ }
133
+
134
+ return true;
135
+ });
136
+
137
+ /**
138
+ * Can this widget be moved?
139
+ *
140
+ * @author Jelle De Loecker <jelle@elevenways.be>
141
+ * @since 0.1.6
142
+ * @version 0.1.6
143
+ *
144
+ * @type {Boolean}
145
+ */
146
+ Base.setProperty(function can_be_moved() {
147
+
148
+ // Widgets without a "parent_instance" are mostly
149
+ // hardcoded in some template (like in a Partial widget)
150
+ if (!this.instance?.parent_instance) {
151
+ return false;
152
+ }
153
+
154
+ return true;
155
+ });
156
+
117
157
  /**
118
158
  * Get a sibling container
119
159
  *
@@ -232,13 +272,26 @@ Base.setMethod(function rerender() {
232
272
  *
233
273
  * @author Jelle De Loecker <jelle@elevenways.be>
234
274
  * @since 0.1.0
235
- * @version 0.1.0
275
+ * @version 0.1.6
236
276
  */
237
277
  Base.setMethod(function introduced() {
238
278
 
239
279
  if (this.hasAttribute('editing')) {
240
280
  this.startEditor();
241
281
  }
282
+
283
+ this.addEventListener('click', e => {
284
+
285
+ let is_editing = this.instance?.editing;
286
+
287
+ if (is_editing) {
288
+ let anchor = e.target.closest('a');
289
+
290
+ if (anchor) {
291
+ e.preventDefault();
292
+ }
293
+ }
294
+ });
242
295
  });
243
296
 
244
297
  /**
@@ -246,7 +299,7 @@ Base.setMethod(function introduced() {
246
299
  *
247
300
  * @author Jelle De Loecker <jelle@elevenways.be>
248
301
  * @since 0.1.5
249
- * @version 0.1.5
302
+ * @version 0.1.6
250
303
  */
251
304
  Base.setMethod(function gatherSaveData() {
252
305
 
@@ -263,6 +316,9 @@ Base.setMethod(function gatherSaveData() {
263
316
  pk : this.record.$pk,
264
317
  field : this.field,
265
318
  value : this.value,
319
+ filter_target : this.filter_target,
320
+ filter_value : this.filter_value,
321
+ value_path : this.value_path,
266
322
  field_languages : this.record.$hold.translated_fields,
267
323
  };
268
324
 
@@ -52,6 +52,42 @@ Widget.setAssignedProperty('record');
52
52
  */
53
53
  Widget.setAssignedProperty('field');
54
54
 
55
+ /**
56
+ * The path to the value inside the field
57
+ *
58
+ * @author Jelle De Loecker <jelle@elevenways.be>
59
+ * @since 0.1.6
60
+ * @version 0.1.6
61
+ */
62
+ Widget.setAssignedProperty('value_path');
63
+
64
+ /**
65
+ * The path to the field to filter on
66
+ *
67
+ * @author Jelle De Loecker <jelle@elevenways.be>
68
+ * @since 0.1.6
69
+ * @version 0.1.6
70
+ */
71
+ Widget.setAssignedProperty('filter_target');
72
+
73
+ /**
74
+ * The filter value
75
+ *
76
+ * @author Jelle De Loecker <jelle@elevenways.be>
77
+ * @since 0.1.6
78
+ * @version 0.1.6
79
+ */
80
+ Widget.setAssignedProperty('filter_value');
81
+
82
+ /**
83
+ * CSS classes to put on the direct children
84
+ *
85
+ * @author Jelle De Loecker <jelle@elevenways.be>
86
+ * @since 0.1.6
87
+ * @version 0.1.6
88
+ */
89
+ Widget.setAttribute('child-class');
90
+
55
91
  /**
56
92
  * Is this widget being edited?
57
93
  *
@@ -89,11 +125,11 @@ Widget.setProperty(function value() {
89
125
  *
90
126
  * @author Jelle De Loecker <jelle@elevenways.be>
91
127
  * @since 0.1.5
92
- * @version 0.1.5
128
+ * @version 0.1.6
93
129
  */
94
130
  Widget.setMethod(function onRecordAssignment(new_record, old_val) {
95
131
  if (new_record && this.field) {
96
- let value = new_record[this.field];
132
+ let value = this.getValueFromRecord(new_record);
97
133
  this.applyValue(value);
98
134
  }
99
135
  });
@@ -103,21 +139,83 @@ Widget.setMethod(function onRecordAssignment(new_record, old_val) {
103
139
  *
104
140
  * @author Jelle De Loecker <jelle@elevenways.be>
105
141
  * @since 0.1.5
106
- * @version 0.1.5
142
+ * @version 0.1.6
107
143
  */
108
144
  Widget.setMethod(function onFieldAssignment(new_field, old_val) {
109
145
  if (new_field && this.record) {
110
- let value = this.record[new_field];
146
+ let value = this.getValueFromRecord(this.record);
111
147
  this.applyValue(value);
112
148
  }
113
149
  });
114
150
 
151
+ /**
152
+ * Get the values to work with from the given record
153
+ *
154
+ * @author Jelle De Loecker <jelle@elevenways.be>
155
+ * @since 0.1.6
156
+ * @version 0.1.6
157
+ */
158
+ Widget.setMethod(function getValueFromRecord(record) {
159
+
160
+ if (!record) {
161
+ return;
162
+ }
163
+
164
+ let value;
165
+
166
+ if (this.field) {
167
+ value = record[this.field];
168
+ } else {
169
+ value = record;
170
+ }
171
+
172
+ if (!value) {
173
+ return;
174
+ }
175
+
176
+ if (this.filter_value) {
177
+ if (!Array.isArray(value) || !this.filter_target) {
178
+ return;
179
+ }
180
+
181
+ let inputs = value;
182
+ value = [];
183
+
184
+ for (let input of inputs) {
185
+ if (input[this.filter_target] == this.filter_value) {
186
+ value.push(input);
187
+ }
188
+ }
189
+ }
190
+
191
+ if (this.value_path) {
192
+ if (!Array.isArray(value)) {
193
+ return;
194
+ }
195
+
196
+ let inputs = value;
197
+ value = [];
198
+
199
+ for (let input of inputs) {
200
+ if (input[this.value_path]) {
201
+ value.push(input[this.value_path]);
202
+ }
203
+ }
204
+ }
205
+
206
+ if (Array.isArray(value)) {
207
+ value = value[0];
208
+ }
209
+
210
+ return value;
211
+ });
212
+
115
213
  /**
116
214
  * Apply the given value
117
215
  *
118
216
  * @author Jelle De Loecker <jelle@elevenways.be>
119
217
  * @since 0.1.5
120
- * @version 0.1.5
218
+ * @version 0.1.6
121
219
  */
122
220
  Widget.setMethod(function applyValue(value) {
123
221
 
@@ -157,7 +255,11 @@ Widget.setMethod(function applyValue(value) {
157
255
  }
158
256
 
159
257
  this.instance.config = config;
160
- this.instance.populateWidget();
258
+ let promise = this.instance.populateWidget();
259
+
260
+ if (promise) {
261
+ this.delayAssemble(promise);
262
+ }
161
263
  });
162
264
 
163
265
  /**
@@ -24,7 +24,7 @@ let AddArea = Function.inherits('Alchemy.Element.Widget.Base', function AlchemyW
24
24
  *
25
25
  * @author Jelle De Loecker <jelle@elevenways.be>
26
26
  * @since 0.1.0
27
- * @version 0.1.0
27
+ * @version 0.1.6
28
28
  */
29
29
  AddArea.setMethod(function showTypes(event) {
30
30
 
@@ -42,7 +42,7 @@ AddArea.setMethod(function showTypes(event) {
42
42
 
43
43
  for (let widget of widgets) {
44
44
 
45
- if (widget.type_name == 'container') {
45
+ if (!widget.canBeAdded(that.parentElement)) {
46
46
  continue;
47
47
  }
48
48
 
@@ -15,7 +15,7 @@ let Toolbar = Function.inherits('Alchemy.Element.Widget.Base', function AlchemyW
15
15
  *
16
16
  * @author Jelle De Loecker <jelle@elevenways.be>
17
17
  * @since 0.1.0
18
- * @version 0.1.0
18
+ * @version 0.1.6
19
19
  *
20
20
  * @param {HTMLElement} widget
21
21
  */
@@ -37,7 +37,24 @@ Toolbar.setMethod(function showWidgetActions(widget) {
37
37
  let button = this.createElement('button');
38
38
  button.classList.add('aw-toolbar-button');
39
39
 
40
- button.innerHTML = action.getButtonHTML();
40
+ let content = action.getButtonContent();
41
+
42
+ if (!content) {
43
+ content = action.title;
44
+ }
45
+
46
+ if (typeof content == 'string') {
47
+ button.innerText = content;
48
+ } else if (content) {
49
+ if (Array.isArray(content)) {
50
+ for (let entry of content) {
51
+ button.append(entry);
52
+ }
53
+ } else {
54
+ button.append(content);
55
+ }
56
+ }
57
+
41
58
  button.setAttribute('title', action.title);
42
59
 
43
60
  let is_selected = action.isAlreadySelected(widget);
@@ -141,30 +141,31 @@ Action.setMethod(function test(widget) {
141
141
  *
142
142
  * @author Jelle De Loecker <jelle@elevenways.be>
143
143
  * @since 0.1.0
144
- * @version 0.1.0
144
+ * @version 0.1.6
145
145
  *
146
- * @return {String}
146
+ * @return {HTMLElement|String}
147
147
  */
148
- Action.setMethod(function getButtonHTML() {
148
+ Action.setMethod(function getButtonContent() {
149
149
 
150
- let html;
150
+ let result;
151
151
 
152
152
  if (this.icon) {
153
153
 
154
154
  let icon = this.icon;
155
155
 
156
156
  if (typeof icon == 'string') {
157
- html = '<i class="' + icon + '"></i>';
157
+ result = Blast.Classes.Hawkejs.Hawkejs.createElement('al-ico');
158
+ result.setIcon(icon);
158
159
  } else if (icon.html) {
159
- html = icon.html;
160
+ result = Blast.Classes.Hawkejs.parseHTML(icon.html);
160
161
  }
161
162
  }
162
163
 
163
- if (!html) {
164
- html = this.name;
164
+ if (!result) {
165
+ result = this.name;
165
166
  }
166
167
 
167
- return html;
168
+ return result;
168
169
  });
169
170
 
170
171
  /**
@@ -77,14 +77,20 @@ Widget.setProperty(function element() {
77
77
  *
78
78
  * @author Jelle De Loecker <jelle@elevenways.be>
79
79
  * @since 0.1.0
80
- * @version 0.1.0
80
+ * @version 0.1.6
81
81
  *
82
82
  * @type {Hawkejs.Renderer}
83
83
  */
84
84
  Widget.enforceProperty(function hawkejs_renderer(new_value) {
85
85
 
86
- if (!new_value && Blast.isBrowser) {
87
- new_value = hawkejs.scene.general_renderer;
86
+ if (!new_value) {
87
+ if (this.parent_instance && this.parent_instance.hawkejs_renderer) {
88
+ new_value = this.parent_instance.hawkejs_renderer;
89
+ } else if (this.widget && this.widget.hawkejs_renderer) {
90
+ new_value = this.widget.hawkejs_renderer;
91
+ } else if (Blast.isBrowser) {
92
+ new_value = hawkejs.scene.general_renderer;
93
+ }
88
94
  }
89
95
 
90
96
  return new_value;
@@ -95,7 +101,7 @@ Widget.enforceProperty(function hawkejs_renderer(new_value) {
95
101
  *
96
102
  * @author Jelle De Loecker <jelle@elevenways.be>
97
103
  * @since 0.1.0
98
- * @version 0.1.5
104
+ * @version 0.1.6
99
105
  */
100
106
  Widget.constitute(function prepareSchema() {
101
107
 
@@ -131,6 +137,8 @@ Widget.constitute(function prepareSchema() {
131
137
  return widget_el.can_be_saved;
132
138
  });
133
139
 
140
+ save.setIcon('floppy-disk');
141
+
134
142
  // Add the remove action
135
143
  let remove = this.createAction('remove', 'Remove');
136
144
 
@@ -141,16 +149,16 @@ Widget.constitute(function prepareSchema() {
141
149
  });
142
150
 
143
151
  remove.setTester(function removeTester(widget_el, handle) {
144
- // The root alchemy-widgets element should not be removed
145
- return widget_el.tagName !== 'ALCHEMY-WIDGETS';
152
+ return widget_el.can_be_removed;
146
153
  });
147
154
 
148
- remove.setIcon('gg-trash');
155
+ remove.setIcon('trash');
149
156
 
150
157
  // Add the config action
151
158
  let config = this.createAction('config', 'Config');
152
159
 
153
160
  config.close_toolbar = true;
161
+ config.setIcon('gears');
154
162
 
155
163
  config.setHandler(function configAction(widget_el, handle) {
156
164
  widget_el.instance.showConfig();
@@ -167,10 +175,15 @@ Widget.constitute(function prepareSchema() {
167
175
  });
168
176
 
169
177
  move_left.setTester(function moveLeftTest(widget_el, handle) {
178
+
179
+ if (!widget_el.can_be_moved) {
180
+ return false;
181
+ }
182
+
170
183
  return !!handle.previousElementSibling;
171
184
  });
172
185
 
173
- move_left.setIcon('gg-arrow-left');
186
+ move_left.setIcon('arrow-left');
174
187
 
175
188
  let move_right = this.createAction('move-right', 'Move right');
176
189
 
@@ -183,6 +196,10 @@ Widget.constitute(function prepareSchema() {
183
196
 
184
197
  move_right.setTester(function moveRightTest(widget_el, handle) {
185
198
 
199
+ if (!widget_el.can_be_moved) {
200
+ return false;
201
+ }
202
+
186
203
  let next = handle.nextElementSibling;
187
204
 
188
205
  if (!next || next.tagName == 'ALCHEMY-WIDGET-ADD-AREA') {
@@ -192,7 +209,7 @@ Widget.constitute(function prepareSchema() {
192
209
  return true;
193
210
  });
194
211
 
195
- move_right.setIcon('gg-arrow-right');
212
+ move_right.setIcon('arrow-right');
196
213
 
197
214
  // The move-in-left action
198
215
  let move_in_left = this.createAction('move-in-left', 'Move in left');
@@ -209,10 +226,15 @@ Widget.constitute(function prepareSchema() {
209
226
  });
210
227
 
211
228
  move_in_left.setTester(function moveLeftTest(widget_el, handle) {
229
+
230
+ if (!widget_el.can_be_moved) {
231
+ return false;
232
+ }
233
+
212
234
  return !!handle.previous_container;
213
235
  });
214
236
 
215
- move_in_left.setIcon('gg-arrow-left');
237
+ move_in_left.setIcon('arrow-left');
216
238
 
217
239
  // The move-in-right action
218
240
  let move_in_right = this.createAction('move-in-right', 'Move in right');
@@ -229,17 +251,23 @@ Widget.constitute(function prepareSchema() {
229
251
  });
230
252
 
231
253
  move_in_right.setTester(function moveRightTest(widget_el, handle) {
232
- console.log('Right test of:', handle)
254
+
255
+ if (!widget_el.can_be_moved) {
256
+ return false;
257
+ }
258
+
233
259
  return !!handle.next_container;
234
260
  });
235
261
 
236
- move_in_right.setIcon('gg-arrow-right');
262
+ move_in_right.setIcon('arrow-right');
237
263
 
238
264
  let css_class = this.createAction('css-class', 'CSS Class');
239
265
 
240
266
  css_class.setHandler(function setCssClass(widget_el, handle) {
241
267
  widget_el.instance.showConfig(['main_class_names']);
242
268
  });
269
+
270
+ css_class.setIcon('tags');
243
271
  });
244
272
 
245
273
  /**
@@ -279,6 +307,43 @@ Widget.setStatic(function createAction(name, title) {
279
307
  return action;
280
308
  });
281
309
 
310
+ /**
311
+ * Add a check to see if the widget can be added to the current location
312
+ *
313
+ * @author Jelle De Loecker <jelle@elevenways.be>
314
+ * @since 0.1.6
315
+ * @version 0.1.6
316
+ *
317
+ * @param {Boolean|Function} checker
318
+ */
319
+ Widget.setStatic(function setAddChecker(checker) {
320
+ this.add_checker = checker;
321
+ });
322
+
323
+ /**
324
+ * Actually perform the add-check
325
+ *
326
+ * @author Jelle De Loecker <jelle@elevenways.be>
327
+ * @since 0.1.6
328
+ * @version 0.1.6
329
+ *
330
+ * @param {Alchemy.Element.Widget.Base} widget_element
331
+ */
332
+ Widget.setStatic(function canBeAdded(widget_element) {
333
+
334
+ if (this.add_checker != null) {
335
+ const type = typeof this.add_checker;
336
+
337
+ if (type == 'function') {
338
+ return this.add_checker(widget_element);
339
+ } else if (type == 'boolean') {
340
+ return this.add_checker;
341
+ }
342
+ }
343
+
344
+ return true;
345
+ });
346
+
282
347
  /**
283
348
  * unDry an object
284
349
  *
@@ -460,11 +525,11 @@ Widget.setMethod(function _createPopulatedWidgetElement() {
460
525
  });
461
526
 
462
527
  /**
463
- * Dummy populate method
528
+ * Populate the contents of the widget
464
529
  *
465
530
  * @author Jelle De Loecker <jelle@elevenways.be>
466
531
  * @since 0.1.0
467
- * @version 0.1.2
532
+ * @version 0.1.6
468
533
  */
469
534
  Widget.setMethod(function populateWidget() {
470
535
 
@@ -480,6 +545,16 @@ Widget.setMethod(function populateWidget() {
480
545
  }
481
546
  }
482
547
 
548
+ let child_classes = this.widget.child_class;
549
+
550
+ if (child_classes) {
551
+ let children = this.widget.children,
552
+ i;
553
+
554
+ for (i = 0; i < children.length; i++) {
555
+ Hawkejs.addClasses(children[i], child_classes);
556
+ }
557
+ }
483
558
  });
484
559
 
485
560
  /**
@@ -487,16 +562,23 @@ Widget.setMethod(function populateWidget() {
487
562
  *
488
563
  * @author Jelle De Loecker <jelle@elevenways.be>
489
564
  * @since 0.1.0
490
- * @version 0.1.0
565
+ * @version 0.1.6
491
566
  */
492
- Widget.setMethod(function startEditor() {
567
+ Widget.setMethod(async function startEditor() {
493
568
 
494
569
  // Show this is being edited
495
570
  this.editing = true;
496
571
 
572
+ // Make sure the icon font is loaded
573
+ if (this.hawkejs_renderer?.helpers?.Media) {
574
+ this.hawkejs_renderer.helpers.Media.loadIconFont();
575
+ }
576
+
497
577
  // Add the appropriate class to the current widget wrapper
498
578
  this.widget.classList.add('aw-editing');
499
579
 
580
+ await this.widget.waitForTasks();
581
+
500
582
  if (typeof this._startEditor == 'function') {
501
583
  this._startEditor();
502
584
  }
@@ -529,13 +611,13 @@ Widget.setMethod(function stopEditor() {
529
611
  *
530
612
  * @author Jelle De Loecker <jelle@elevenways.be>
531
613
  * @since 0.1.0
532
- * @version 0.1.0
614
+ * @version 0.1.6
533
615
  */
534
- Widget.setMethod(function rerender() {
616
+ Widget.setMethod(async function rerender() {
535
617
 
536
618
  Hawkejs.removeChildren(this.widget);
537
619
 
538
- this.populateWidget();
620
+ await this.populateWidget();
539
621
 
540
622
  if (this.editing) {
541
623
  this.startEditor();
@@ -560,7 +642,7 @@ Widget.setMethod(function syncConfig() {
560
642
  *
561
643
  * @author Jelle De Loecker <jelle@elevenways.be>
562
644
  * @since 0.1.0
563
- * @version 0.1.0
645
+ * @version 0.1.12
564
646
  */
565
647
  Widget.setMethod(async function showConfig(fields) {
566
648
 
@@ -590,9 +672,11 @@ Widget.setMethod(async function showConfig(fields) {
590
672
  }
591
673
  }
592
674
 
675
+ let widget_settings = Object.assign({}, this.syncConfig());
676
+
593
677
  let variables = {
594
678
  schema : this.schema,
595
- widget_settings : this.syncConfig(),
679
+ widget_settings,
596
680
  fields : fields
597
681
  };
598
682
 
@@ -607,6 +691,9 @@ Widget.setMethod(async function showConfig(fields) {
607
691
  let dialog = dialog_contents.queryParents('he-dialog'),
608
692
  button = dialog_contents.querySelector('.btn-apply');
609
693
 
694
+ dialog_contents.classList.add('default-form-editor');
695
+ hawkejs.scene.enableStyle('chimera/chimera');
696
+
610
697
  button.addEventListener('click', e => {
611
698
  e.preventDefault();
612
699
 
@@ -11,6 +11,11 @@
11
11
  */
12
12
  const Container = Function.inherits('Alchemy.Widget', 'Container');
13
13
 
14
+ /**
15
+ * Make this an abstract class
16
+ */
17
+ Container.makeAbstractClass();
18
+
14
19
  /**
15
20
  * Prepare the schema
16
21
  *
@@ -33,7 +33,7 @@ Header.constitute(function prepareSchema() {
33
33
  *
34
34
  * @author Jelle De Loecker <jelle@elevenways.be>
35
35
  * @since 0.1.0
36
- * @version 0.1.0
36
+ * @version 0.1.6
37
37
  */
38
38
  Header.constitute(function addActions() {
39
39
 
@@ -43,7 +43,7 @@ Header.constitute(function addActions() {
43
43
  for (let level of levels) {
44
44
  let level_action = this.createAction('make-level-' + level, 'Level ' + level);
45
45
 
46
- level_action.setHandler(function setLevelAction(widget, toolbar) {
46
+ level_action.setHandler(function setLevelAction(widget, handle, toolbar) {
47
47
 
48
48
  let content = widget.querySelector(query);
49
49
 
@@ -0,0 +1,215 @@
1
+ /**
2
+ * The Partial Widget class
3
+ *
4
+ * @constructor
5
+ *
6
+ * @author Jelle De Loecker <jelle@elevenways.be>
7
+ * @since 0.1.6
8
+ * @version 0.1.6
9
+ *
10
+ * @param {Object} data
11
+ */
12
+ const Partial = Function.inherits('Alchemy.Widget', 'Partial');
13
+
14
+ /**
15
+ * Make this an abstract class
16
+ */
17
+ Partial.makeAbstractClass();
18
+
19
+ /**
20
+ * Get the type name
21
+ *
22
+ * @author Jelle De Loecker <jelle@elevenways.be>
23
+ * @since 0.1.6
24
+ * @version 0.1.6
25
+ */
26
+ Partial.setStatic(function createClassTypeName() {
27
+ return 'partial_' + this.name.underscore();
28
+ });
29
+
30
+ /**
31
+ * Set the template to use
32
+ *
33
+ * @author Jelle De Loecker <jelle@elevenways.be>
34
+ * @since 0.1.6
35
+ * @version 0.1.6
36
+ */
37
+ Partial.setStatic(function setTemplateFile(name) {
38
+ this.constitute(function _setTemplateFile() {
39
+ this.template_file = name;
40
+ });
41
+ });
42
+
43
+ /**
44
+ * Prepare the schema
45
+ *
46
+ * @author Jelle De Loecker <jelle@elevenways.be>
47
+ * @since 0.1.6
48
+ * @version 0.1.6
49
+ */
50
+ Partial.constitute(function prepareSchema() {
51
+
52
+ this.schema.addField('view', 'String', {
53
+ title : 'Partial View',
54
+ description : 'The actual HWK template to use for rendering this widget',
55
+ });
56
+
57
+ let contents = this.createSchema();
58
+
59
+ contents.addField('name', 'String');
60
+ contents.addField('content', 'Widgets');
61
+
62
+ this.schema.addField('contents', contents, {array: true});
63
+ });
64
+
65
+ /**
66
+ * Populate the widget
67
+ *
68
+ * @author Jelle De Loecker <jelle@elevenways.be>
69
+ * @since 0.1.6
70
+ * @version 0.1.6
71
+ *
72
+ * @param {HTMLElement} widget
73
+ */
74
+ Partial.setMethod(async function populateWidget() {
75
+
76
+ let view = this.constructor.template_file || this.config.view;
77
+
78
+ if (view) {
79
+
80
+ let options = {
81
+ print : false,
82
+ };
83
+
84
+ let variables = {};
85
+
86
+ await this.addVariablesForRender(variables);
87
+
88
+ if (this.config?.contents?.length) {
89
+ for (let entry of this.config.contents) {
90
+ variables[entry.name] = entry.contents;
91
+ }
92
+ }
93
+
94
+ variables.config = this.config;
95
+
96
+ let placeholder = this.hawkejs_renderer.addSubtemplate(view, options, variables);
97
+
98
+ // If the widget is already part of the DOM,
99
+ // it's being edited and we need to manually kickstart the renderer
100
+ if (Blast.isBrowser && document.body.contains(this.widget)) {
101
+ await placeholder.getContent();
102
+ }
103
+
104
+ Hawkejs.removeChildren(this.widget);
105
+
106
+ this.widget.append(placeholder);
107
+ }
108
+
109
+ populateWidget.super.call(this);
110
+ });
111
+
112
+ /**
113
+ * Allow adding variables for rendering the partial
114
+ *
115
+ * @author Jelle De Loecker <jelle@elevenways.be>
116
+ * @since 0.1.
117
+ * @version 0.1.
118
+ */
119
+ Partial.setMethod(function addVariablesForRender(variables) {
120
+ // To be optionally implemented by child widgets
121
+ });
122
+
123
+ /**
124
+ * Get all the sub widgets by their name
125
+ *
126
+ * @author Jelle De Loecker <jelle@elevenways.be>
127
+ * @since 0.1.6
128
+ * @version 0.1.6
129
+ *
130
+ * @return {Object}
131
+ */
132
+ Partial.setMethod(function getSubWidgets() {
133
+
134
+ let elements = this.widget.queryAllNotNested('[data-section-name]'),
135
+ result = {};
136
+
137
+ for (let element of elements) {
138
+ result[element.dataset.sectionName] = element;
139
+ }
140
+
141
+ return result;
142
+ });
143
+
144
+ /**
145
+ * Start the editor
146
+ *
147
+ * @author Jelle De Loecker <jelle@elevenways.be>
148
+ * @since 0.1.6
149
+ * @version 0.1.6
150
+ */
151
+ Partial.setMethod(function _startEditor() {
152
+
153
+ let sub_widgets = this.getSubWidgets();
154
+
155
+ for (let name in sub_widgets) {
156
+ let sub_widget = sub_widgets[name];
157
+ sub_widget.startEditor();
158
+ }
159
+ });
160
+
161
+ /**
162
+ * Stop the editor
163
+ *
164
+ * @author Jelle De Loecker <jelle@elevenways.be>
165
+ * @since 0.1.6
166
+ * @version 0.1.6
167
+ */
168
+ Partial.setMethod(function _stopEditor() {
169
+
170
+ let sub_widgets = this.getSubWidgets();
171
+
172
+ for (let name in sub_widgets) {
173
+ let sub_widget = sub_widgets[name];
174
+ sub_widget.stopEditor();
175
+ }
176
+
177
+ this.populateWidget();
178
+ });
179
+
180
+ /**
181
+ * Update the config values
182
+ *
183
+ * @author Jelle De Loecker <jelle@elevenways.be>
184
+ * @since 0.1.6
185
+ * @version 0.1.6
186
+ *
187
+ * @return {Object}
188
+ */
189
+ Partial.setMethod(function syncConfig() {
190
+
191
+ let sub_widgets = this.getSubWidgets(),
192
+ contents = this.config.contents;
193
+
194
+ if (!contents) {
195
+ contents = [];
196
+ this.config.contents = contents;
197
+ }
198
+
199
+ for (let name in sub_widgets) {
200
+ let sub_widget = sub_widgets[name];
201
+ let widget_config = contents.findByPath('name', name);
202
+
203
+ if (!widget_config) {
204
+ widget_config = {
205
+ name,
206
+ };
207
+
208
+ contents.push(widget_config);
209
+ }
210
+
211
+ widget_config.contents = sub_widget.value;
212
+ }
213
+
214
+ return this.config;
215
+ });
@@ -28,18 +28,28 @@ Text.constitute(function prepareSchema() {
28
28
  *
29
29
  * @author Jelle De Loecker <jelle@elevenways.be>
30
30
  * @since 0.1.0
31
- * @version 0.1.0
31
+ * @version 0.1.6
32
32
  *
33
33
  * @param {HTMLElement} widget
34
34
  */
35
35
  Text.setMethod(function populateWidget() {
36
36
 
37
+ let tag_name;
38
+
37
39
  populateWidget.super.call(this);
38
40
 
39
- let paragraph = this.createElement('p');
40
- paragraph.textContent = this.config.content || '';
41
+ if (this.widget.dataset.textElementTag) {
42
+ tag_name = this.widget.dataset.textElementTag;
43
+ }
44
+
45
+ if (!tag_name) {
46
+ tag_name = 'p';
47
+ }
48
+
49
+ let text_element = this.createElement(tag_name);
50
+ text_element.textContent = this.config.content || '';
41
51
 
42
- this.widget.append(paragraph);
52
+ this.widget.append(text_element);
43
53
  });
44
54
 
45
55
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "alchemy-widget",
3
3
  "description": "The widget plugin for the AlchemyMVC",
4
- "version": "0.1.5",
4
+ "version": "0.1.6",
5
5
  "author": "Jelle De Loecker <jelle@elevenways.be>",
6
6
  "keywords": [
7
7
  "alchemy",
@@ -15,6 +15,6 @@
15
15
  "repository": "11ways/alchemy-widget",
16
16
  "license": "MIT",
17
17
  "engines": {
18
- "node" : ">=12.0.0"
18
+ "node" : ">=14.0.0"
19
19
  }
20
20
  }