alchemy-widget 0.3.0-alpha.1 → 0.3.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.
- package/CHANGELOG.md +16 -0
- package/CLAUDE.md +160 -0
- package/assets/stylesheets/alchemy_widgets.scss +0 -0
- package/assets/stylesheets/widget_picker.scss +283 -0
- package/element/20-add_area_element.js +19 -21
- package/element/30-base_toolbar_element.js +189 -2
- package/element/editor_toolbar_element.js +121 -1
- package/element/user_avatar_group_element.js +5 -0
- package/element/widget_picker_element.js +333 -0
- package/element/widget_toolbar_element.js +22 -137
- package/helper/document_watcher.js +41 -6
- package/helper/editor_toolbar_manager.js +112 -20
- package/helper/widgets/00-widget.js +256 -1
- package/helper/widgets/05-column.js +4 -0
- package/helper/widgets/05-list.js +55 -0
- package/helper/widgets/05-row.js +4 -0
- package/helper/widgets/alchemy_field_widget.js +5 -0
- package/helper/widgets/alchemy_form_widget.js +5 -0
- package/helper/widgets/alchemy_table_widget.js +4 -0
- package/helper/widgets/alchemy_tabs_widget.js +5 -0
- package/helper/widgets/hawkejs_template.js +4 -0
- package/helper/widgets/header.js +4 -0
- package/helper/widgets/html.js +4 -0
- package/helper/widgets/markdown.js +4 -0
- package/helper/widgets/sourcecode.js +4 -0
- package/helper/widgets/table_of_contents.js +4 -0
- package/helper/widgets/text.js +4 -0
- package/lib/conduit_extras.js +15 -4
- package/package.json +2 -2
- package/view/widget/elements/al_editor_toolbar.hwk +49 -1
- package/view/widget/elements/al_widget_toolbar.hwk +1 -1
- package/view/widget/elements/widget_picker.hwk +55 -0
|
@@ -35,6 +35,245 @@ Widget.makeAbstractClass();
|
|
|
35
35
|
*/
|
|
36
36
|
Widget.startNewGroup('widgets');
|
|
37
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Standard widget categories.
|
|
40
|
+
* These are the universal categories available in all projects.
|
|
41
|
+
* Projects can register their own categories using Widget.registerCategory()
|
|
42
|
+
*
|
|
43
|
+
* Translation keys are derived automatically from the category name:
|
|
44
|
+
* - Title: category.name (e.g., "layout")
|
|
45
|
+
* - Description: "widget-category-{name}-description" (e.g., "widget-category-layout-description")
|
|
46
|
+
*
|
|
47
|
+
* Templates should pass filter parameters: widget=true category=true
|
|
48
|
+
*/
|
|
49
|
+
Widget.CATEGORIES = {
|
|
50
|
+
LAYOUT: {
|
|
51
|
+
name: 'layout',
|
|
52
|
+
icon: 'table-columns',
|
|
53
|
+
order: 10
|
|
54
|
+
},
|
|
55
|
+
TEXT: {
|
|
56
|
+
name: 'text',
|
|
57
|
+
icon: 'font',
|
|
58
|
+
order: 20
|
|
59
|
+
},
|
|
60
|
+
MEDIA: {
|
|
61
|
+
name: 'media',
|
|
62
|
+
icon: 'image',
|
|
63
|
+
order: 30
|
|
64
|
+
},
|
|
65
|
+
DATA: {
|
|
66
|
+
name: 'data',
|
|
67
|
+
icon: 'database',
|
|
68
|
+
order: 40
|
|
69
|
+
},
|
|
70
|
+
NAVIGATION: {
|
|
71
|
+
name: 'navigation',
|
|
72
|
+
icon: 'compass',
|
|
73
|
+
order: 50
|
|
74
|
+
},
|
|
75
|
+
INTERACTIVE: {
|
|
76
|
+
name: 'interactive',
|
|
77
|
+
icon: 'hand-pointer',
|
|
78
|
+
order: 60
|
|
79
|
+
},
|
|
80
|
+
ADVANCED: {
|
|
81
|
+
name: 'advanced',
|
|
82
|
+
icon: 'code',
|
|
83
|
+
order: 100
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Storage for dynamically registered categories.
|
|
89
|
+
* Use Widget.registerCategory() to add new categories.
|
|
90
|
+
*/
|
|
91
|
+
Widget.REGISTERED_CATEGORIES = {};
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Register a new widget category.
|
|
95
|
+
* This allows projects to add their own categories without modifying the core.
|
|
96
|
+
*
|
|
97
|
+
* Translation keys are derived automatically from the category name:
|
|
98
|
+
* - Title: category.name (e.g., "monitoring")
|
|
99
|
+
* - Description: "widget-category-{name}-description" (e.g., "widget-category-monitoring-description")
|
|
100
|
+
*
|
|
101
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
102
|
+
* @since 0.3.0
|
|
103
|
+
* @version 0.3.0
|
|
104
|
+
*
|
|
105
|
+
* @param {string} key The category key (e.g., 'MONITORING')
|
|
106
|
+
* @param {Object} config Category configuration
|
|
107
|
+
* @param {string} config.name Internal name (e.g., 'monitoring')
|
|
108
|
+
* @param {string} config.icon FontAwesome icon name
|
|
109
|
+
* @param {number} [config.order] Sort order (lower = earlier, default: 90)
|
|
110
|
+
*/
|
|
111
|
+
Widget.setStatic(function registerCategory(key, config) {
|
|
112
|
+
|
|
113
|
+
if (!key || typeof key !== 'string') {
|
|
114
|
+
throw new Error('Category key must be a non-empty string');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (!config || typeof config !== 'object') {
|
|
118
|
+
throw new Error('Category config must be an object');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (!config.name || typeof config.name !== 'string') {
|
|
122
|
+
throw new Error('Category config.name must be a non-empty string');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (!config.icon || typeof config.icon !== 'string') {
|
|
126
|
+
throw new Error('Category config.icon must be a non-empty string');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Normalize the key to uppercase
|
|
130
|
+
key = key.toUpperCase();
|
|
131
|
+
|
|
132
|
+
// Create the category config with defaults
|
|
133
|
+
let category = {
|
|
134
|
+
name : config.name,
|
|
135
|
+
icon : config.icon,
|
|
136
|
+
order : config.order ?? 90
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
// Store in registered categories
|
|
140
|
+
this.REGISTERED_CATEGORIES[key] = category;
|
|
141
|
+
|
|
142
|
+
// Also add to CATEGORIES for backwards compatibility and easy access
|
|
143
|
+
this.CATEGORIES[key] = category;
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Set the widget category
|
|
148
|
+
*
|
|
149
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
150
|
+
* @since 0.3.0
|
|
151
|
+
* @version 0.3.0
|
|
152
|
+
*
|
|
153
|
+
* @param {String} category The category name (use CATEGORIES.*.name or custom string)
|
|
154
|
+
*/
|
|
155
|
+
Widget.setStatic(function setCategory(category) {
|
|
156
|
+
this.category = category;
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Set the widget description
|
|
161
|
+
*
|
|
162
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
163
|
+
* @since 0.3.0
|
|
164
|
+
* @version 0.3.0
|
|
165
|
+
*
|
|
166
|
+
* @param {String} description A short description of what the widget does
|
|
167
|
+
*/
|
|
168
|
+
Widget.setStatic(function setDescription(description) {
|
|
169
|
+
this.description = description;
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Set the widget icon (FontAwesome icon name)
|
|
174
|
+
*
|
|
175
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
176
|
+
* @since 0.3.0
|
|
177
|
+
* @version 0.3.0
|
|
178
|
+
*
|
|
179
|
+
* @param {String} icon The icon name (e.g., 'table', 'font', 'image')
|
|
180
|
+
*/
|
|
181
|
+
Widget.setStatic(function setIcon(icon) {
|
|
182
|
+
this.icon = icon;
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Override the auto-generated title
|
|
187
|
+
*
|
|
188
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
189
|
+
* @since 0.3.0
|
|
190
|
+
* @version 0.3.0
|
|
191
|
+
*
|
|
192
|
+
* @param {String} title The display title for the widget
|
|
193
|
+
*/
|
|
194
|
+
Widget.setStatic(function setTitle(title) {
|
|
195
|
+
this.title = title;
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Get category info for a given category name.
|
|
200
|
+
* Supports built-in categories, registered categories, and custom strings.
|
|
201
|
+
* Falls back to ADVANCED category if no category is specified.
|
|
202
|
+
*
|
|
203
|
+
* Translation keys are derived from the category name at render time:
|
|
204
|
+
* - Title: category.name (e.g., "layout")
|
|
205
|
+
* - Description: "widget-category-{name}-description"
|
|
206
|
+
*
|
|
207
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
208
|
+
* @since 0.3.0
|
|
209
|
+
* @version 0.3.0
|
|
210
|
+
*
|
|
211
|
+
* @param {String} category The category name
|
|
212
|
+
*
|
|
213
|
+
* @return {Object} Category info with name, icon, order
|
|
214
|
+
*/
|
|
215
|
+
Widget.setStatic(function getCategoryInfo(category) {
|
|
216
|
+
|
|
217
|
+
// Default to 'advanced' if no category specified
|
|
218
|
+
if (!category) {
|
|
219
|
+
return this.CATEGORIES.ADVANCED;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Check built-in categories (includes registered ones added via registerCategory)
|
|
223
|
+
for (let key in this.CATEGORIES) {
|
|
224
|
+
if (this.CATEGORIES[key].name === category) {
|
|
225
|
+
return this.CATEGORIES[key];
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Return a default structure for unknown/custom categories
|
|
230
|
+
// These are categories set via widget.category = 'custom' without registerCategory()
|
|
231
|
+
return {
|
|
232
|
+
name : category,
|
|
233
|
+
icon : 'puzzle-piece',
|
|
234
|
+
order : 90
|
|
235
|
+
};
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Get all available categories.
|
|
240
|
+
* Returns built-in categories, registered categories, and any custom categories
|
|
241
|
+
* discovered from widgets that set their category to a custom string.
|
|
242
|
+
*
|
|
243
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
244
|
+
* @since 0.3.0
|
|
245
|
+
* @version 0.3.0
|
|
246
|
+
*
|
|
247
|
+
* @return {Array} Array of category info objects, sorted by order
|
|
248
|
+
*/
|
|
249
|
+
Widget.setStatic(function getAllCategories() {
|
|
250
|
+
|
|
251
|
+
let categories = new Map();
|
|
252
|
+
|
|
253
|
+
// Add built-in categories (includes registered ones added via registerCategory)
|
|
254
|
+
for (let key in this.CATEGORIES) {
|
|
255
|
+
let cat = this.CATEGORIES[key];
|
|
256
|
+
categories.set(cat.name, cat);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Check all registered widgets for custom categories
|
|
260
|
+
// (widgets that set category to a custom string without using registerCategory)
|
|
261
|
+
let widgets = alchemy.getClassGroup('widgets');
|
|
262
|
+
|
|
263
|
+
if (widgets) {
|
|
264
|
+
for (let widget of Object.values(widgets)) {
|
|
265
|
+
let category = widget.category;
|
|
266
|
+
|
|
267
|
+
if (category && !categories.has(category)) {
|
|
268
|
+
categories.set(category, this.getCategoryInfo(category));
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Convert to array and sort by order
|
|
274
|
+
return Array.from(categories.values()).sortByPath(1, 'order');
|
|
275
|
+
});
|
|
276
|
+
|
|
38
277
|
/**
|
|
39
278
|
* Return the class-wide schema
|
|
40
279
|
*
|
|
@@ -740,7 +979,7 @@ Widget.setMethod(function populateWidget() {
|
|
|
740
979
|
*
|
|
741
980
|
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
742
981
|
* @since 0.1.0
|
|
743
|
-
* @version 0.
|
|
982
|
+
* @version 0.3.0
|
|
744
983
|
*/
|
|
745
984
|
Widget.setMethod(function finalizePopulatedWidget() {
|
|
746
985
|
|
|
@@ -758,6 +997,22 @@ Widget.setMethod(function finalizePopulatedWidget() {
|
|
|
758
997
|
}
|
|
759
998
|
}
|
|
760
999
|
|
|
1000
|
+
if (config.main_class_names) {
|
|
1001
|
+
let main = this.widget?.children?.[0];
|
|
1002
|
+
|
|
1003
|
+
if (main) {
|
|
1004
|
+
let name,
|
|
1005
|
+
i;
|
|
1006
|
+
|
|
1007
|
+
let class_names = Array.cast(config.main_class_names);
|
|
1008
|
+
|
|
1009
|
+
for (i = 0; i < class_names.length; i++) {
|
|
1010
|
+
name = class_names[i];
|
|
1011
|
+
main.classList.add(name);
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
|
|
761
1016
|
if (config.language) {
|
|
762
1017
|
this.widget.setAttribute('lang', config.language);
|
|
763
1018
|
} else {
|
|
@@ -11,6 +11,27 @@
|
|
|
11
11
|
*/
|
|
12
12
|
const List = Function.inherits('Alchemy.Widget.Container', 'List');
|
|
13
13
|
|
|
14
|
+
// Widget metadata
|
|
15
|
+
List.setCategory('layout');
|
|
16
|
+
List.setIcon('list');
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Prepare the schema
|
|
20
|
+
*
|
|
21
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
22
|
+
* @since 0.3.0
|
|
23
|
+
* @version 0.3.0
|
|
24
|
+
*/
|
|
25
|
+
List.constitute(function prepareSchema() {
|
|
26
|
+
|
|
27
|
+
// Classnames for the li elements
|
|
28
|
+
this.schema.addField('li_class_names', 'String', {
|
|
29
|
+
title : 'Li-element CSS classes',
|
|
30
|
+
description : 'Configure extra CSS classes for the list items',
|
|
31
|
+
array: true,
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
14
35
|
/**
|
|
15
36
|
* Get a list of elements that could be child widgets
|
|
16
37
|
*
|
|
@@ -39,4 +60,38 @@ List.setMethod(function initContainer() {
|
|
|
39
60
|
this.widget.list_element = ul;
|
|
40
61
|
|
|
41
62
|
initContainer.super.call(this);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Populate the contents of the widget
|
|
67
|
+
*
|
|
68
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
69
|
+
* @since 0.3.0
|
|
70
|
+
* @version 0.3.0
|
|
71
|
+
*/
|
|
72
|
+
List.setMethod(function finalizePopulatedWidget() {
|
|
73
|
+
|
|
74
|
+
const config = this.config;
|
|
75
|
+
|
|
76
|
+
if (config?.li_class_names) {
|
|
77
|
+
let li_elements = this.widget.list_element.querySelectorAll(':scope > li');
|
|
78
|
+
|
|
79
|
+
if (li_elements.length) {
|
|
80
|
+
let name,
|
|
81
|
+
i;
|
|
82
|
+
|
|
83
|
+
let class_names = Array.cast(config.li_class_names);
|
|
84
|
+
|
|
85
|
+
for (i = 0; i < class_names.length; i++) {
|
|
86
|
+
name = class_names[i];
|
|
87
|
+
|
|
88
|
+
for (let li_element of li_elements) {
|
|
89
|
+
li_element.classList.add(name);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
return finalizePopulatedWidget.super.call(this);
|
|
42
97
|
});
|
package/helper/widgets/05-row.js
CHANGED
|
@@ -11,6 +11,11 @@
|
|
|
11
11
|
*/
|
|
12
12
|
const AlchemyField = Function.inherits('Alchemy.Widget', 'AlchemyField');
|
|
13
13
|
|
|
14
|
+
// Widget metadata
|
|
15
|
+
AlchemyField.setTitle('Form Field');
|
|
16
|
+
AlchemyField.setCategory('data');
|
|
17
|
+
AlchemyField.setIcon('i-cursor');
|
|
18
|
+
|
|
14
19
|
/**
|
|
15
20
|
* Prepare the schema
|
|
16
21
|
*
|
|
@@ -11,6 +11,11 @@
|
|
|
11
11
|
*/
|
|
12
12
|
const AlchemyForm = Function.inherits('Alchemy.Widget', 'AlchemyForm');
|
|
13
13
|
|
|
14
|
+
// Widget metadata
|
|
15
|
+
AlchemyForm.setTitle('Form');
|
|
16
|
+
AlchemyForm.setCategory('data');
|
|
17
|
+
AlchemyForm.setIcon('rectangle-list');
|
|
18
|
+
|
|
14
19
|
/**
|
|
15
20
|
* Prepare the schema
|
|
16
21
|
*
|
package/helper/widgets/header.js
CHANGED
package/helper/widgets/html.js
CHANGED
package/helper/widgets/text.js
CHANGED
package/lib/conduit_extras.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const MANAGER = Symbol('toolbar_manager');
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Add a method to the conduit class to set the toolbar manager
|
|
3
5
|
*
|
|
@@ -13,6 +15,8 @@ Classes.Alchemy.Conduit.Conduit.setMethod(function setToolbarInfo(document_or_mo
|
|
|
13
15
|
} catch (err) {
|
|
14
16
|
console.error('Error setting toolbar info', err);
|
|
15
17
|
}
|
|
18
|
+
|
|
19
|
+
return this[MANAGER];
|
|
16
20
|
});
|
|
17
21
|
|
|
18
22
|
/**
|
|
@@ -30,11 +34,18 @@ function _setToolbarInfo(document_or_model, scenario = 'frontend') {
|
|
|
30
34
|
return;
|
|
31
35
|
}
|
|
32
36
|
|
|
33
|
-
let manager =
|
|
34
|
-
|
|
37
|
+
let manager = this[MANAGER] || this.set('toolbar_manager');
|
|
38
|
+
|
|
39
|
+
if (!manager) {
|
|
40
|
+
manager = Classes.Alchemy.Widget.EditorToolbarManager.create(this);
|
|
41
|
+
this[MANAGER] = manager;
|
|
42
|
+
this.set('toolbar_manager', manager);
|
|
43
|
+
this.expose('toolbar_manager', manager);
|
|
44
|
+
}
|
|
35
45
|
|
|
36
|
-
|
|
37
|
-
|
|
46
|
+
if (scenario !== undefined) {
|
|
47
|
+
manager.scenario = scenario;
|
|
48
|
+
}
|
|
38
49
|
|
|
39
50
|
let document_watcher,
|
|
40
51
|
document,
|
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.3.0
|
|
4
|
+
"version": "0.3.0",
|
|
5
5
|
"author": "Jelle De Loecker <jelle@elevenways.be>",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"alchemy",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
],
|
|
12
12
|
"peerDependencies": {
|
|
13
13
|
"alchemymvc" : ">=1.4.0||>=1.4.0-alpha",
|
|
14
|
-
"alchemy-form": "
|
|
14
|
+
"alchemy-form": ">=0.3.0||>=0.3.0-alpha"
|
|
15
15
|
},
|
|
16
16
|
"repository": "11ways/alchemy-widget",
|
|
17
17
|
"license": "MIT",
|
|
@@ -1,3 +1,51 @@
|
|
|
1
1
|
<div data-he-slot="left"></div>
|
|
2
|
+
|
|
2
3
|
<div data-he-slot="center"></div>
|
|
3
|
-
|
|
4
|
+
|
|
5
|
+
<div data-he-slot="right">
|
|
6
|
+
<al-button class="start-edit" state="ready">
|
|
7
|
+
<al-icon icon-name="pencil"></al-icon>
|
|
8
|
+
{%t "start-editing" %}
|
|
9
|
+
</al-button>
|
|
10
|
+
|
|
11
|
+
<al-button class="stop-and-save" state="ready">
|
|
12
|
+
<al-state state-name="saving">
|
|
13
|
+
<al-icon icon-name="spinner" icon-flags="spin"></al-icon>
|
|
14
|
+
{%t "saving" %}
|
|
15
|
+
</al-state>
|
|
16
|
+
<al-state state-name="saving-before-stop">
|
|
17
|
+
<al-icon icon-name="spinner" icon-flags="spin"></al-icon>
|
|
18
|
+
{%t "saving" %}
|
|
19
|
+
</al-state>
|
|
20
|
+
<al-state state-name="saved">
|
|
21
|
+
<al-icon icon-name="badge-check" icon-flags="beat"></al-icon>
|
|
22
|
+
{%t "saved" %}
|
|
23
|
+
</al-state>
|
|
24
|
+
<al-state state-name="ready">
|
|
25
|
+
<al-icon icon-name="floppy-disk-circle-arrow-right"></al-icon>
|
|
26
|
+
{%t "save-and-stop-editing" %}
|
|
27
|
+
</al-state>
|
|
28
|
+
</al-button>
|
|
29
|
+
|
|
30
|
+
<al-button class="stop-edit" state="ready">
|
|
31
|
+
<al-icon icon-name="pencil-slash"></al-icon>
|
|
32
|
+
{%t "stop-editing" %}
|
|
33
|
+
</al-button>
|
|
34
|
+
|
|
35
|
+
<al-button class="save-all" state="ready">
|
|
36
|
+
<al-state state-name="saving">
|
|
37
|
+
<al-icon icon-name="spinner" icon-flags="spin"></al-icon>
|
|
38
|
+
{%t "saving" %}
|
|
39
|
+
</al-state>
|
|
40
|
+
<al-state state-name="saved">
|
|
41
|
+
<al-icon icon-name="badge-check" icon-flags="beat"></al-icon>
|
|
42
|
+
{%t "saved" %}
|
|
43
|
+
</al-state>
|
|
44
|
+
<al-state state-name="ready">
|
|
45
|
+
<al-icon icon-name="floppy-disk"></al-icon>
|
|
46
|
+
{%t "save-all" %}
|
|
47
|
+
</al-state>
|
|
48
|
+
</al-button>
|
|
49
|
+
|
|
50
|
+
<div data-area="buttons"></div>
|
|
51
|
+
</div>
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
</al-state>
|
|
42
42
|
</al-button>
|
|
43
43
|
|
|
44
|
-
{% if Router.routeConfig('Chimera.Editor#index') %}
|
|
44
|
+
{% if Router.routeConfig('Chimera.Editor#index') && (not toolbar_manager or toolbar_manager.scenario neq 'chimera') %}
|
|
45
45
|
<a href="/chimera/" data-he-link="false">
|
|
46
46
|
<al-icon icon-style="duotone" icon-name="display-code"></al-icon>
|
|
47
47
|
{%t "go-to-backend" %}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{# Widget picker dialog - using flat widgets list to avoid nested loop issues #}
|
|
2
|
+
<div class="widget-picker">
|
|
3
|
+
<div class="widget-picker-header">
|
|
4
|
+
<input
|
|
5
|
+
type="search"
|
|
6
|
+
class="widget-picker-search"
|
|
7
|
+
placeholder={% __d('widget-picker-search-placeholder') %}
|
|
8
|
+
autocomplete="off"
|
|
9
|
+
>
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
<div class="widget-picker-body">
|
|
13
|
+
<aside class="widget-picker-categories">
|
|
14
|
+
<button class="category-btn active" data-category="">
|
|
15
|
+
<al-icon icon-name="grid-2"></al-icon>
|
|
16
|
+
<span>{%t 'widget-picker-all-categories' %}</span>
|
|
17
|
+
</button>
|
|
18
|
+
{% each categories as category %}
|
|
19
|
+
<button
|
|
20
|
+
class="category-btn"
|
|
21
|
+
data-category={% category.name %}
|
|
22
|
+
>
|
|
23
|
+
<al-icon icon-name={% category.icon %}></al-icon>
|
|
24
|
+
<span>{%t category.name widget=true category=true %}</span>
|
|
25
|
+
</button>
|
|
26
|
+
{% /each %}
|
|
27
|
+
</aside>
|
|
28
|
+
|
|
29
|
+
<main class="widget-picker-list">
|
|
30
|
+
{% each widgets as widget %}
|
|
31
|
+
<button class="widget-item"
|
|
32
|
+
data-type={% widget.type_name %}
|
|
33
|
+
data-category={% widget.category %}
|
|
34
|
+
tabindex="0">
|
|
35
|
+
<div class="widget-item-icon">
|
|
36
|
+
<al-icon icon-name={% widget.icon %}></al-icon>
|
|
37
|
+
</div>
|
|
38
|
+
<div class="widget-item-content">
|
|
39
|
+
<span class="widget-item-title">{{ widget.title }}</span>
|
|
40
|
+
{% if widget.description %}
|
|
41
|
+
<span class="widget-item-description">{{ widget.description }}</span>
|
|
42
|
+
{% else %}
|
|
43
|
+
<span class="widget-item-description">{%t widget.description_key widget=true %}</span>
|
|
44
|
+
{% /if %}
|
|
45
|
+
</div>
|
|
46
|
+
</button>
|
|
47
|
+
{% /each %}
|
|
48
|
+
|
|
49
|
+
<div class="widget-picker-empty" hidden>
|
|
50
|
+
<al-icon icon-name="search"></al-icon>
|
|
51
|
+
<p>{%t 'widget-picker-no-results' %}</p>
|
|
52
|
+
</div>
|
|
53
|
+
</main>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|