alchemy-widget 0.1.5 → 0.2.0

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 (36) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/assets/stylesheets/alchemy_widgets.scss +426 -0
  3. package/bootstrap.js +6 -0
  4. package/controller/alchemy_widgets_controller.js +85 -9
  5. package/element/00-widget_base_element.js +154 -6
  6. package/element/05-widget_element.js +112 -19
  7. package/element/10-container_elements.js +15 -15
  8. package/element/11-alchemy_widgets_list_element.js +2 -2
  9. package/element/20-add_area_element.js +23 -23
  10. package/element/table_of_contents_element.js +104 -8
  11. package/element/widget_actionbar_element.js +92 -0
  12. package/element/widget_context_element.js +39 -23
  13. package/element/widget_toolbar_element.js +300 -38
  14. package/helper/widget_action.js +19 -18
  15. package/helper/widgets/00-widget.js +160 -37
  16. package/helper/widgets/01-container.js +12 -7
  17. package/helper/widgets/05-list.js +1 -1
  18. package/helper/widgets/alchemy_field_widget.js +112 -0
  19. package/helper/widgets/alchemy_form_widget.js +183 -0
  20. package/helper/widgets/alchemy_table_widget.js +71 -0
  21. package/helper/widgets/alchemy_tabs_widget.js +195 -0
  22. package/helper/widgets/header.js +4 -4
  23. package/helper/widgets/markdown.js +17 -10
  24. package/helper/widgets/partial.js +215 -0
  25. package/helper/widgets/sourcecode.js +4 -4
  26. package/helper/widgets/table_of_contents.js +2 -2
  27. package/helper/widgets/text.js +14 -4
  28. package/helper/widgets_helper.js +26 -0
  29. package/package.json +4 -3
  30. package/view/elements/table_of_contents.hwk +23 -9
  31. package/view/form/inputs/edit/widget.hwk +2 -2
  32. package/view/form/inputs/edit/widgets.hwk +2 -2
  33. package/view/widget/elements/al_widget_toolbar.hwk +49 -0
  34. package/view/widget/widget_config.hwk +7 -4
  35. package/assets/stylesheets/alchemy-widget-symbols.scss +0 -191
  36. package/assets/stylesheets/alchemy-widgets.scss +0 -258
package/CHANGELOG.md CHANGED
@@ -1,3 +1,32 @@
1
+ ## 0.2.0 (2022-11-02)
2
+
3
+ * Use `al-` prefix for all custom elements
4
+ * Update to `alchemy-form` v0.2.0
5
+ * Add copy & paste actions
6
+ * Add nesting levels to `al-toc`
7
+ * Add `al-widget-toolbar` element
8
+ * Use `easymde` markdown editor for markdown widgets
9
+
10
+ ## 0.1.6 (2022-10-12)
11
+
12
+ * Allow hiding widgets from the add-menu
13
+ * Let actions return their button contents as elements instead of only html
14
+ * Cancel clicks on widgets when editing them
15
+ * Fix getting the `hawkejs_renderer` instance in a widget
16
+ * Use `alchemy-chimera` style for the widget configuration dialog
17
+ * Make renders wait for widgets that have to render their content asynchronously
18
+ * Allow setting the element to use in a Text widget
19
+ * Fix Header-widget level actions
20
+ * Load the icon fonts as soon as the editor starts
21
+ * Make the `rerender` method async
22
+ * Use `child_class` property in the populate method
23
+ * Add filter logic to widgets for getting specific values
24
+ * Add abstract `Partial` widget class, to easily create a new widget with a pre-defined layout
25
+ * Wait for widgets to render their contents before starting editor
26
+ * Add `can_be_removed` property to widget elements
27
+ * Add `can_be_moved` property to widget elements
28
+ * Throw an error if `alchemy-form` is loaded before this plugin
29
+
1
30
  ## 0.1.5 (2022-07-14)
2
31
 
3
32
  * Unselect widgets when stopping the editor
@@ -0,0 +1,426 @@
1
+ :root {
2
+ al-widget-toolbar {
3
+ display: none;
4
+ }
5
+ }
6
+
7
+ html.logged-in {
8
+ al-widget-toolbar {
9
+ font-size: 15px;
10
+
11
+ display: flex;
12
+ z-index: 9999;
13
+ background: rgba(200, 200, 200, 0.5);
14
+ box-shadow: 0 5px 10px 0 rgba(0, 0, 0, 0.15);
15
+ bottom: 3em; left: 3em;
16
+ border-radius: 24px;
17
+ height: 5.25em;
18
+ padding: 1em;
19
+ border: 1px solid #aaa;
20
+ display: flex;
21
+ gap: 1em;
22
+ backdrop-filter: blur(10px);
23
+
24
+ position: fixed;
25
+ bottom: 16px;
26
+ left: 16px;
27
+ right: 16px;
28
+ }
29
+ }
30
+
31
+ al-widget-toolbar {
32
+
33
+ &[state="editing"] {
34
+ .start-edit {
35
+ display: none;
36
+ }
37
+ }
38
+
39
+ &[state="default"],
40
+ &[state="ready"] {
41
+ .stop-and-save,
42
+ .stop-edit,
43
+ .save-all {
44
+ display: none;
45
+ }
46
+ }
47
+
48
+ &[state="saving"],
49
+ &[state="saving-before-stop"] {
50
+ .start-edit,
51
+ .stop-edit {
52
+ display: none;
53
+ }
54
+ }
55
+
56
+ &[state="saving"] {
57
+ .stop-and-save {
58
+ display: none;
59
+ }
60
+ }
61
+
62
+ &[state="saving-before-stop"] {
63
+ .save-all {
64
+ display: none;
65
+ }
66
+ }
67
+
68
+ .stop-and-save,
69
+ .save-all {
70
+ --al-button-bg-color: green;
71
+ --al-button-bg-color-hover: rgb(55, 155, 55);
72
+ }
73
+
74
+ a,
75
+ al-button {
76
+ --al-button-font-size: 1.3em;
77
+ border-radius: 1em;
78
+ }
79
+
80
+ a {
81
+ background-color: black;
82
+ color: white;
83
+ padding: 0.5em 1em;
84
+ display: inline-flex;
85
+ align-items: center;
86
+ gap: 0.5em;
87
+
88
+ al-icon {
89
+ font-size: 2em;
90
+ }
91
+
92
+ &:hover {
93
+ background-color: rgb(53, 53, 53);
94
+ }
95
+ }
96
+ }
97
+
98
+ al-widgets,
99
+ al-widgets-row,
100
+ al-widgets-column,
101
+ al-widget {
102
+
103
+ }
104
+
105
+ al-widgets,
106
+ al-widgets-row,
107
+ al-widgets-column {
108
+ display: flex;
109
+
110
+ &.aw-editing {
111
+ position: relative;
112
+ min-height: 3rem;
113
+ }
114
+
115
+ > * {
116
+ flex: 10 10 auto;
117
+ }
118
+ }
119
+
120
+ al-widgets,
121
+ al-widget {
122
+ &.aw-editing {
123
+ &:before {
124
+ content: "";
125
+ position: absolute;
126
+ inset: -2px;
127
+ background: white;
128
+ border: 2px dashed rgba(0, 0, 0, 0.4);
129
+ pointer-events: none;
130
+ backdrop-filter: invert(80%);
131
+ clip-path: polygon(0% 0%, 0% 100%, 2px 100%, 2px 2px, calc(100% - 2px) 2px, calc(100% - 2px) calc(100% - 2px), 0 calc(100% - 2px), 0 100%, 100% 100%, 100% 0)
132
+ }
133
+ }
134
+ }
135
+
136
+ al-widgets-row,
137
+ al-widgets-column {
138
+
139
+ &.aw-editing {
140
+ &:hover {
141
+ background: rgba(60, 60, 60, 0.1);
142
+ }
143
+ }
144
+ }
145
+
146
+ al-widgets,
147
+ al-widgets-column {
148
+ flex-flow: column;
149
+
150
+ &.aw-editing {
151
+ padding-bottom: 5rem;
152
+ }
153
+
154
+ > al-widget-add-area {
155
+ position: absolute;
156
+ bottom: 0.2rem;
157
+ left: 50%;
158
+ transform: translateX(-50%);
159
+ }
160
+ }
161
+
162
+ al-widgets {
163
+ min-width: 10rem;
164
+ min-height: 10rem;
165
+ }
166
+
167
+ al-widgets-row {
168
+ flex-flow: row;
169
+ flex: 10 10 auto;
170
+
171
+ &.aw-editing {
172
+ padding-right: 5rem;
173
+ }
174
+
175
+ > al-widget-add-area {
176
+ position: absolute;
177
+ right: 0.5rem;
178
+ top: 50%;
179
+ transform: translateY(-50%);
180
+ }
181
+ }
182
+
183
+ al-widget-add-area {
184
+ background: rgba(255,255,255,0.5);
185
+ padding: 0.5rem 2rem;
186
+ border-radius: 2rem;
187
+ z-index: 99999999;
188
+
189
+ .widget-button {
190
+ min-height: 2.5rem;
191
+ padding: 1rem;
192
+ }
193
+ }
194
+
195
+ al-widgets-row,
196
+ al-widgets > al-widgets-column,
197
+ al-widgets-column > al-widgets-column,
198
+ .alchemy-widgets-container > al-widgets-column {
199
+ &.aw-editing {
200
+
201
+ &::after {
202
+ content: "";
203
+ position: absolute;
204
+ height: 1px;
205
+ left: 15px;
206
+ right: 15px;
207
+ bottom: 0;
208
+ border-bottom: 2px dashed rgba(0, 0, 0, 0.3);
209
+
210
+ // Reset some things (in case of a nested column)
211
+ width: initial;
212
+ border-right: initial;
213
+ top: initial;
214
+ }
215
+ }
216
+ }
217
+
218
+ al-widgets-column {
219
+ flex: 10 10 auto;
220
+ }
221
+
222
+ al-widgets-column,
223
+ al-widgets-row > al-widgets-row,
224
+ .alchemy-widgets-container > al-widgets-row {
225
+ &.aw-editing {
226
+ &::after {
227
+ content: "";
228
+ position: absolute;
229
+ width: 1px;
230
+ top: 10px;
231
+ bottom: 10px;
232
+ right: 0;
233
+ border-right: 2px dashed rgba(0, 0, 0, 0.3);
234
+
235
+ // Reset some things (in case of a nested row)
236
+ left: initial;
237
+ border-bottom: initial;
238
+ height: initial;
239
+ }
240
+ }
241
+ }
242
+
243
+ al-widget-add-area {
244
+ display: flex;
245
+ justify-content: center;
246
+ align-items: center;
247
+
248
+ .widget-types {
249
+ display: none;
250
+ }
251
+
252
+ &.show-types {
253
+ .main-button {
254
+ display: none;
255
+ }
256
+
257
+ .widget-types {
258
+ display: initial;
259
+ }
260
+ }
261
+
262
+ .main-button {
263
+ display: flex;
264
+ }
265
+ }
266
+
267
+ .aw-actionbar-button,
268
+ .widget-button {
269
+ color: #707684;
270
+ cursor: pointer;
271
+ border: none;
272
+ background: none;
273
+
274
+ display: flex;
275
+ align-content: center;
276
+ min-height: 26px;
277
+ align-items: center;
278
+ text-transform: uppercase;
279
+
280
+ &:hover {
281
+ color: #388ae5;
282
+ }
283
+ }
284
+
285
+ al-widget {
286
+ display: block;
287
+
288
+ &.aw-editing {
289
+ min-height: 2rem;
290
+
291
+ &:hover {
292
+ background: rgba(60, 60, 120, 0.2);
293
+
294
+ // This actually causes some glitches on Firefox :/
295
+ backdrop-filter: blur(4px);
296
+ }
297
+ }
298
+
299
+ &.aw-selected {
300
+ background-color: #7979f347;
301
+ position: relative;
302
+ outline: none;
303
+
304
+ &:before {
305
+ background-color: yellow;
306
+ }
307
+ }
308
+ }
309
+
310
+ al-widget-context,
311
+ al-widget-actionbar {
312
+ display: block;
313
+ padding: 0.4rem;
314
+ background-color: white;
315
+ border-radius: 4px;
316
+ border: 1px solid #dadada;
317
+ font-size: 2rem;
318
+
319
+ &[hidden] {
320
+ display: none;
321
+ }
322
+ }
323
+
324
+ al-widget-context {
325
+ position: fixed;
326
+ z-index: 99999;
327
+
328
+ al-widget-actionbar {
329
+ position: absolute;
330
+ top: calc(100% + 5px);
331
+ right: 0;
332
+ z-index: 999999;
333
+ }
334
+ }
335
+
336
+ al-widget-actionbar {
337
+ min-height: 2.5rem;
338
+ min-width: 2.5rem;
339
+ display: flex;
340
+
341
+ > * {
342
+ margin-right: 5px;
343
+ }
344
+
345
+ .aw-actionbar-button {
346
+ font-size: 2.5rem;
347
+ border-radius: 4px;
348
+ padding: 0.6rem;
349
+
350
+ &:hover {
351
+ color: rgb(112, 118, 132);
352
+ background-color: #f0f0f0;
353
+ }
354
+
355
+ &.aw-button-selected {
356
+ color: #388ae5;
357
+ }
358
+ }
359
+ }
360
+
361
+ al-widget[type="header"] {
362
+
363
+ h1, h2, h3, h4, h5, h6 {
364
+ padding: 1em 0;
365
+ margin: 0;
366
+ line-height: 1.5em;
367
+ outline: none;
368
+ }
369
+
370
+ h1 {
371
+ font-size: 2em;
372
+ }
373
+
374
+ h2 {
375
+ font-size: 1.5em;
376
+ }
377
+ }
378
+
379
+ al-widget[type="text"] {
380
+
381
+ &.aw-editing {
382
+ &,
383
+ & > * {
384
+ min-width: 5rem;
385
+ min-height: 3rem;
386
+ }
387
+
388
+ & > * {
389
+ display: inline-block;
390
+ width: 100%;
391
+ }
392
+ }
393
+ }
394
+
395
+ .aw-actionbar-button {
396
+
397
+ .aw-header-h {
398
+ font-weight: bold;
399
+ font-size: 1.2em;
400
+ }
401
+
402
+ .aw-header-level {
403
+ font-weight: bold;
404
+ height: 1.3em;
405
+ }
406
+ }
407
+
408
+ al-toc {
409
+ display: block;
410
+ }
411
+
412
+ [data-he-template="widget/widget_config"] {
413
+
414
+ .widget-config-title {
415
+ margin-bottom: 1rem;
416
+ color: black;
417
+ }
418
+
419
+ al-label {
420
+ padding: 0.5rem;
421
+
422
+ [data-he-name="field-title"] {
423
+ display: block;
424
+ }
425
+ }
426
+ }
package/bootstrap.js CHANGED
@@ -1,3 +1,9 @@
1
+ alchemy.requirePlugin('form', false);
2
+
3
+ if (!alchemy.plugins.form) {
4
+ throw new Error('The alchemy-form plugin has to be loaded BEFORE alchemy-widget');
5
+ }
6
+
1
7
  Router.add({
2
8
  name : 'AlchemyWidgets#save',
3
9
  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);