alchemy-widget 0.1.1 → 0.1.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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,16 @@
1
+ ## 0.1.4 (2022-06-23)
2
+
3
+ * Use `he-context-menu` element to show widgets to add
4
+ * Add widget actions to move across container boundaries
5
+
6
+ ## 0.1.3 (2022-03-21)
7
+
8
+ * Catch and print errors when appending a widget to a container
9
+
10
+ ## 0.1.2 (2022-02-20)
11
+
12
+ * Add `table-of-contents` element and widget
13
+
1
14
  ## 0.1.1 (2022-01-28)
2
15
 
3
16
  * Hide context button when clicking out of widget
@@ -11,7 +11,7 @@ alchemy-widgets-column {
11
11
  }
12
12
 
13
13
  > * {
14
- flex: 1;
14
+ flex: 10;
15
15
  }
16
16
  }
17
17
 
@@ -53,7 +53,7 @@ alchemy-widgets {
53
53
 
54
54
  alchemy-widgets-row {
55
55
  flex-flow: row;
56
- flex: 1 1 auto;
56
+ flex: 10 10 auto;
57
57
 
58
58
  &.aw-editing {
59
59
  padding-right: 5rem;
@@ -91,7 +91,7 @@ alchemy-widgets-column > alchemy-widgets-column,
91
91
  }
92
92
 
93
93
  alchemy-widgets-column {
94
- flex: 1 1 auto;
94
+ flex: 10 10 auto;
95
95
  }
96
96
 
97
97
  alchemy-widgets-column,
@@ -149,8 +149,8 @@ alchemy-widget-add-area {
149
149
  display: flex;
150
150
  align-content: center;
151
151
  min-height: 26px;
152
- min-width: 26px;
153
152
  align-items: center;
153
+ text-transform: uppercase;
154
154
 
155
155
  &:hover {
156
156
  color: #388ae5;
@@ -251,4 +251,8 @@ alchemy-widget[type="header"] {
251
251
  font-weight: bold;
252
252
  height: 1.3em;
253
253
  }
254
+ }
255
+
256
+ table-of-contents {
257
+ display: block;
254
258
  }
@@ -42,9 +42,89 @@ Base.setAssignedProperty('instance');
42
42
  *
43
43
  * @author Jelle De Loecker <jelle@elevenways.be>
44
44
  * @since 0.1.0
45
- * @version 0.1.0
45
+ * @version 0.1.4
46
+ */
47
+ Base.setProperty(function parent_container() {
48
+
49
+ let container,
50
+ current = this.parentElement;
51
+
52
+ while (current) {
53
+ if (current.is_container) {
54
+ container = current;
55
+ break;
56
+ }
57
+
58
+ current = current.parentElement;
59
+ }
60
+
61
+ return container;
62
+ });
63
+
64
+ /**
65
+ * The next container across container boundaries
66
+ *
67
+ * @author Jelle De Loecker <jelle@elevenways.be>
68
+ * @since 0.1.4
69
+ * @version 0.1.4
70
+ */
71
+ Base.setProperty(function next_container() {
72
+ return this.getSiblingContainer('next');
73
+ });
74
+
75
+ /**
76
+ * The previous container across container boundaries
77
+ *
78
+ * @author Jelle De Loecker <jelle@elevenways.be>
79
+ * @since 0.1.4
80
+ * @version 0.1.4
46
81
  */
47
- Base.setProperty('parent_container');
82
+ Base.setProperty(function previous_container() {
83
+ return this.getSiblingContainer('previous');
84
+ });
85
+
86
+ /**
87
+ * Get a sibling container
88
+ *
89
+ * @author Jelle De Loecker <jelle@elevenways.be>
90
+ * @since 0.1.4
91
+ * @version 0.1.4
92
+ */
93
+ Base.setMethod(function getSiblingContainer(type) {
94
+ let property;
95
+
96
+ if (type == 'next') {
97
+ property = 'nextElementSibling';
98
+ } else if (type == 'previous') {
99
+ property = 'previousElementSibling';
100
+ } else {
101
+ return false;
102
+ }
103
+
104
+ if (!this[property] && !this.parent_container) {
105
+ return;
106
+ }
107
+
108
+ let next = this[property];
109
+
110
+ if (next) {
111
+ if (next.is_container) {
112
+ return next;
113
+ }
114
+
115
+ if (next.tagName != 'ALCHEMY-WIDGET-ADD-AREA') {
116
+ return false;
117
+ }
118
+ }
119
+
120
+ next = this.parent_container[property];
121
+
122
+ if (next && next.is_container) {
123
+ return next;
124
+ }
125
+
126
+ return false;
127
+ });
48
128
 
49
129
  /**
50
130
  * Look for a context variable
@@ -32,6 +32,15 @@ AlchemyWidgets.setStatic('custom_element_prefix', 'alchemy-widgets');
32
32
  */
33
33
  AlchemyWidgets.setProperty('add_edit_event_listeners', false);
34
34
 
35
+ /**
36
+ * Indicate this is a container
37
+ *
38
+ * @author Jelle De Loecker <jelle@elevenways.be>
39
+ * @since 0.1.4
40
+ * @version 0.1.4
41
+ */
42
+ AlchemyWidgets.setProperty('is_container', true);
43
+
35
44
  /**
36
45
  * Context variables
37
46
  *
@@ -179,15 +188,26 @@ AlchemyWidgets.setMethod(function clear() {
179
188
  *
180
189
  * @author Jelle De Loecker <jelle@elevenways.be>
181
190
  * @since 0.1.0
182
- * @version 0.1.0
191
+ * @version 0.1.3
183
192
  *
184
193
  * @param {String} type
185
194
  * @param {Object} config
186
195
  */
187
196
  AlchemyWidgets.setMethod(function addWidget(type, config) {
188
197
 
198
+ let instance;
199
+
189
200
  // Create the instance of the widget
190
- let instance = this.instance.createChildWidget(type, config);
201
+ try {
202
+ instance = this.instance.createChildWidget(type, config);
203
+ } catch (err) {
204
+ config = {
205
+ original_config : config,
206
+ html : '<pre>' + err.message + '\n' + err.stack + '</pre>',
207
+ };
208
+
209
+ instance = this.instance.createChildWidget('html', config);
210
+ }
191
211
 
192
212
  // Attach the renderer
193
213
  instance.hawkejs_renderer = this.hawkejs_renderer;
@@ -195,9 +215,6 @@ AlchemyWidgets.setMethod(function addWidget(type, config) {
195
215
  // Get the actual widget HTML element
196
216
  let element = instance.element;
197
217
 
198
- // Make the element know what its parent container is
199
- element.parent_container = this;
200
-
201
218
  this._appendWidgetElement(element);
202
219
 
203
220
  element.value = config;
@@ -26,11 +26,9 @@ let AddArea = Function.inherits('Alchemy.Element.Widget.Base', function AlchemyW
26
26
  * @since 0.1.0
27
27
  * @version 0.1.0
28
28
  */
29
- AddArea.setMethod(function showTypes() {
29
+ AddArea.setMethod(function showTypes(event) {
30
30
 
31
- let that = this,
32
- types_element = this.querySelector('.widget-types'),
33
- widgets = alchemy.getClassGroup('widgets');
31
+ let that = this;
34
32
 
35
33
  let context_button = document.querySelector('alchemy-widget-context');
36
34
 
@@ -38,31 +36,25 @@ AddArea.setMethod(function showTypes() {
38
36
  context_button.unselectedWidget();
39
37
  }
40
38
 
41
- this.classList.add('show-types');
39
+ let context = this.createElement('he-context-menu');
42
40
 
43
- Hawkejs.removeChildren(types_element);
41
+ let widgets = Object.values(alchemy.getClassGroup('widgets')).sortByPath(1, 'title');
44
42
 
45
- for (let key in widgets) {
43
+ for (let widget of widgets) {
46
44
 
47
- if (key == 'container') {
45
+ if (widget.type_name == 'container') {
48
46
  continue;
49
47
  }
50
48
 
51
- let widget = widgets[key];
52
-
53
- let button = this.createElement('button');
54
- button.setAttribute('data-type', key);
55
- button.textContent = widget.title;
56
-
57
- button.addEventListener('click', function onClick(e) {
58
- e.preventDefault();
59
- that.classList.remove('show-types');
60
-
61
- that.parentElement.addWidget(key);
49
+ context.addEntry({
50
+ title : widget.title,
51
+ icon : null,
52
+ }, e => {
53
+ that.parentElement.addWidget(widget.type_name);
62
54
  });
63
-
64
- types_element.append(button);
65
55
  }
56
+
57
+ context.show(event);
66
58
  });
67
59
 
68
60
  /**
@@ -107,7 +99,7 @@ AddArea.setMethod(function introduced() {
107
99
 
108
100
  add_button.addEventListener('click', function onClick(e) {
109
101
  e.preventDefault();
110
- that.showTypes();
102
+ that.showTypes(e);
111
103
  });
112
104
 
113
105
  let context_button = this.querySelector('.menu-button');
@@ -0,0 +1,193 @@
1
+ /**
2
+ * The table-of-contents element
3
+ *
4
+ * @author Jelle De Loecker <jelle@elevenways.be>
5
+ * @since 0.1.2
6
+ * @version 0.1.2
7
+ */
8
+ const TableOfContents = Function.inherits('Alchemy.Element.App', 'TableOfContents');
9
+
10
+ /**
11
+ * The template to use for the content of this element
12
+ *
13
+ * @author Jelle De Loecker <jelle@elevenways.be>
14
+ * @since 0.1.2
15
+ * @version 0.1.2
16
+ */
17
+ TableOfContents.setTemplateFile('elements/table_of_contents');
18
+
19
+ /**
20
+ * Set the content
21
+ *
22
+ * @author Jelle De Loecker <jelle@elevenways.be>
23
+ * @since 0.1.2
24
+ * @version 0.1.2
25
+ */
26
+ TableOfContents.setAssignedProperty('content');
27
+
28
+ /**
29
+ * The role of this element
30
+ *
31
+ * @author Jelle De Loecker <jelle@elevenways.be>
32
+ * @since 0.1.2
33
+ * @version 0.1.2
34
+ */
35
+ TableOfContents.setRole('navigation');
36
+
37
+ /**
38
+ * The parent query
39
+ *
40
+ * @author Jelle De Loecker <jelle@elevenways.be>
41
+ * @since 0.1.2
42
+ * @version 0.1.2
43
+ */
44
+ TableOfContents.setAttribute('parent-selector');
45
+
46
+ /**
47
+ * The children query
48
+ *
49
+ * @author Jelle De Loecker <jelle@elevenways.be>
50
+ * @since 0.1.2
51
+ * @version 0.1.2
52
+ */
53
+ TableOfContents.setAttribute('children-selector');
54
+
55
+ /**
56
+ * The elements query
57
+ *
58
+ * @author Jelle De Loecker <jelle@elevenways.be>
59
+ * @since 0.1.2
60
+ * @version 0.1.2
61
+ */
62
+ TableOfContents.setAttribute('elements-selector');
63
+
64
+ /**
65
+ * The elements query
66
+ *
67
+ * @author Jelle De Loecker <jelle@elevenways.be>
68
+ * @since 0.1.2
69
+ * @version 0.1.2
70
+ */
71
+ TableOfContents.setAttribute('title-selector');
72
+
73
+ /**
74
+ * The class to add when intersecting (visible)
75
+ *
76
+ * @author Jelle De Loecker <jelle@elevenways.be>
77
+ * @since 0.1.2
78
+ * @version 0.1.2
79
+ */
80
+ TableOfContents.setAttribute('intersection-class');
81
+
82
+ /**
83
+ * Get the entries
84
+ *
85
+ * @author Jelle De Loecker <jelle@elevenways.be>
86
+ * @since 0.1.2
87
+ * @version 0.1.2
88
+ */
89
+ TableOfContents.setProperty(function entries() {
90
+
91
+ let result = [],
92
+ parent = this.parentElement,
93
+ wrapper;
94
+
95
+ if (this.parent_selector) {
96
+ parent = this.queryParents(this.parent_selector);
97
+ }
98
+
99
+ if (parent) {
100
+ wrapper = parent;
101
+ }
102
+
103
+ if (wrapper && this.children_selector) {
104
+ wrapper = wrapper.querySelector(this.children_selector);
105
+ }
106
+
107
+ if (wrapper) {
108
+ let elements = wrapper.querySelectorAll(this.elements_selector || 'h1,h2'),
109
+ element,
110
+ title,
111
+ i;
112
+
113
+ for (i = 0; i < elements.length; i++) {
114
+ element = elements[i];
115
+
116
+ if (!element.id) {
117
+
118
+ if (element.hawkejs_id) {
119
+ element.id = element.hawkejs_id;
120
+ }
121
+
122
+ if (!element.id) {
123
+ continue;
124
+ }
125
+ }
126
+
127
+ let title_element;
128
+
129
+ if (this.title_selector) {
130
+ title_element = element.querySelector(this.title_selector);
131
+ }
132
+
133
+ if (!title_element) {
134
+ title_element = element;
135
+ }
136
+
137
+ title = (title_element.toc_title || title_element.textContent || '').trim();
138
+
139
+ title = title.truncate(30);
140
+
141
+ // Don't add empty titles
142
+ if (!title) {
143
+ continue;
144
+ }
145
+
146
+ result.push({
147
+ id : element.id,
148
+ title : title,
149
+ element : element,
150
+ });
151
+ }
152
+ }
153
+
154
+ return result;
155
+ });
156
+
157
+ /**
158
+ * Added to the dom for the first time
159
+ *
160
+ * @author Jelle De Loecker <jelle@elevenways.be>
161
+ * @since 0.1.2
162
+ * @version 0.1.2
163
+ */
164
+ TableOfContents.setMethod(async function introduced() {
165
+
166
+ await this.rerender();
167
+
168
+ const observer = new IntersectionObserver(entries => {
169
+
170
+ let class_name = this.intersection_class || 'visible';
171
+
172
+ for (let entry of entries) {
173
+ const id = entry.target.getAttribute('id');
174
+
175
+ let query = `a[href="#${id}"]`,
176
+ element = this.querySelector(query);
177
+
178
+ if (!element) {
179
+ return;
180
+ }
181
+
182
+ if (entry.intersectionRatio > 0) {
183
+ element.classList.add(class_name);
184
+ } else {
185
+ element.classList.remove(class_name);
186
+ }
187
+ };
188
+ });
189
+
190
+ for (let entry of this.entries) {
191
+ observer.observe(entry.element);
192
+ }
193
+ });
@@ -38,6 +38,7 @@ Toolbar.setMethod(function showWidgetActions(widget) {
38
38
  button.classList.add('aw-toolbar-button');
39
39
 
40
40
  button.innerHTML = action.getButtonHTML();
41
+ button.setAttribute('title', action.title);
41
42
 
42
43
  let is_selected = action.isAlreadySelected(widget);
43
44
 
@@ -107,12 +107,16 @@ Widget.constitute(function prepareSchema() {
107
107
 
108
108
  // Extra classnames for the wrapper
109
109
  this.schema.addField('wrapper_class_names', 'String', {
110
+ title : 'Wrapper CSS classes',
111
+ description : 'Configure extra CSS classes to the wrapper `alchemy-widget` element',
110
112
  array: true,
111
113
  widget_config_editable: true,
112
114
  });
113
115
 
114
116
  // Classnames for the inserted element (if any)
115
117
  this.schema.addField('main_class_names', 'String', {
118
+ title : 'Main CSS classes',
119
+ description : 'Configure extra CSS classes for the main inserted element',
116
120
  array: true,
117
121
  });
118
122
 
@@ -179,6 +183,47 @@ Widget.constitute(function prepareSchema() {
179
183
 
180
184
  move_right.setIcon('gg-arrow-right');
181
185
 
186
+ // The move-in-left action
187
+ let move_in_left = this.createAction('move-in-left', 'Move in left');
188
+
189
+ move_in_left.close_toolbar = true;
190
+
191
+ move_in_left.setHandler(function moveLeftAction(widget_el, handle) {
192
+ // Hawkejs custom element method!
193
+ let container = handle.previous_container;
194
+
195
+ if (container) {
196
+ container.append(handle);
197
+ }
198
+ });
199
+
200
+ move_in_left.setTester(function moveLeftTest(widget_el, handle) {
201
+ return !!handle.previous_container;
202
+ });
203
+
204
+ move_in_left.setIcon('gg-arrow-left');
205
+
206
+ // The move-in-right action
207
+ let move_in_right = this.createAction('move-in-right', 'Move in right');
208
+
209
+ move_in_right.close_toolbar = true;
210
+
211
+ move_in_right.setHandler(function moveRightAction(widget_el, handle) {
212
+ // Hawkejs custom element method!
213
+ let container = handle.next_container;
214
+
215
+ if (container) {
216
+ container.prepend(handle);
217
+ }
218
+ });
219
+
220
+ move_in_right.setTester(function moveRightTest(widget_el, handle) {
221
+ console.log('Right test of:', handle)
222
+ return !!handle.next_container;
223
+ });
224
+
225
+ move_in_right.setIcon('gg-arrow-right');
226
+
182
227
  let css_class = this.createAction('css-class', 'CSS Class');
183
228
 
184
229
  css_class.setHandler(function setCssClass(widget_el, handle) {
@@ -212,11 +257,11 @@ Widget.setStatic(function createSchema() {
212
257
  *
213
258
  * @author Jelle De Loecker <jelle@elevenways.be>
214
259
  * @since 0.1.0
215
- * @version 0.1.0
260
+ * @version 0.1.4
216
261
  */
217
- Widget.setStatic(function createAction(name) {
262
+ Widget.setStatic(function createAction(name, title) {
218
263
 
219
- let action = new Classes.Alchemy.Widget.Action(name);
264
+ let action = new Classes.Alchemy.Widget.Action(name, title || name.titleize());
220
265
 
221
266
  this.actions.set(name, action);
222
267
 
@@ -408,7 +453,7 @@ Widget.setMethod(function _createPopulatedWidgetElement() {
408
453
  *
409
454
  * @author Jelle De Loecker <jelle@elevenways.be>
410
455
  * @since 0.1.0
411
- * @version 0.1.0
456
+ * @version 0.1.2
412
457
  */
413
458
  Widget.setMethod(function populateWidget() {
414
459
 
@@ -416,8 +461,10 @@ Widget.setMethod(function populateWidget() {
416
461
  let name,
417
462
  i;
418
463
 
419
- for (i = 0; i < this.config.wrapper_class_names.length; i++) {
420
- name = this.config.wrapper_class_names[i];
464
+ let class_names = Array.cast(this.config.wrapper_class_names);
465
+
466
+ for (i = 0; i < class_names.length; i++) {
467
+ name = class_names[i];
421
468
  this.widget.classList.add(name);
422
469
  }
423
470
  }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * The TOC Widget class
3
+ *
4
+ * @constructor
5
+ *
6
+ * @author Jelle De Loecker <jelle@elevenways.be>
7
+ * @since 0.1.2
8
+ * @version 0.1.2
9
+ *
10
+ * @param {Object} data
11
+ */
12
+ const Toc = Function.inherits('Alchemy.Widget', 'TableOfContents');
13
+
14
+ /**
15
+ * Populate the widget
16
+ *
17
+ * @author Jelle De Loecker <jelle@elevenways.be>
18
+ * @since 0.1.2
19
+ * @version 0.1.2
20
+ */
21
+ Toc.setMethod(function populateWidget() {
22
+
23
+ populateWidget.super.call(this);
24
+
25
+ let toc = this.createElement('table-of-contents');
26
+
27
+ if (this.config.parent_selector) {
28
+ toc.parent_selector = this.config.parent_selector;
29
+ }
30
+
31
+ if (this.config.elements_selector) {
32
+ toc.elements_selector = this.config.elements_selector;
33
+ }
34
+
35
+ if (this.config.child_selector) {
36
+ toc.child_selector = this.config.child_selector;
37
+ }
38
+
39
+ if (this.config.title_selector) {
40
+ toc.title_selector = this.config.title_selector;
41
+ }
42
+
43
+ this.widget.append(toc);
44
+ });
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.1",
4
+ "version": "0.1.4",
5
5
  "author": "Jelle De Loecker <jelle@elevenways.be>",
6
6
  "keywords": [
7
7
  "alchemy",
@@ -10,7 +10,7 @@
10
10
  "widget"
11
11
  ],
12
12
  "peerDependencies": {
13
- "alchemymvc" : "~1.1.0"
13
+ "alchemymvc" : ">=1.1.0"
14
14
  },
15
15
  "repository": "11ways/alchemy-widget",
16
16
  "license": "MIT",
@@ -0,0 +1,9 @@
1
+ <ol>
2
+ {% each self.entries as entry %}
3
+ <li>
4
+ <a href="#{% entry.id %}">
5
+ {{ entry.title }}
6
+ </a>
7
+ </li>
8
+ {% /each %}
9
+ </ol>