apostrophe 3.28.0 → 3.29.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 +26 -6
- package/modules/@apostrophecms/area/index.js +25 -4
- package/modules/@apostrophecms/area/ui/apos/components/AposAreaContextualMenu.vue +382 -0
- package/modules/@apostrophecms/area/ui/apos/components/AposAreaEditor.vue +28 -4
- package/modules/@apostrophecms/area/ui/apos/components/AposAreaExpandedMenu.vue +264 -0
- package/modules/@apostrophecms/area/ui/apos/components/AposAreaMenu.vue +40 -233
- package/modules/@apostrophecms/area/ui/apos/components/AposAreaWidget.vue +22 -7
- package/modules/@apostrophecms/attachment/index.js +2 -2
- package/modules/@apostrophecms/login/ui/apos/components/TheAposLogin.vue +6 -6
- package/modules/@apostrophecms/modal/ui/apos/components/AposModal.vue +28 -5
- package/modules/@apostrophecms/module/index.js +1 -1
- package/modules/@apostrophecms/piece-page-type/index.js +8 -1
- package/modules/@apostrophecms/schema/lib/addFieldTypes.js +27 -14
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputArea.vue +12 -1
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputBoolean.vue +1 -1
- package/modules/@apostrophecms/widget-type/index.js +4 -0
- package/package.json +1 -1
- package/test/areas.js +76 -3
- package/test/schemaBuilders.js +24 -0
- package/test/schemas.js +62 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.29.0 (2022-10-03)
|
|
4
|
+
|
|
5
|
+
### Adds
|
|
6
|
+
|
|
7
|
+
* Areas now support an `expanded: true` option to display previews for widgets. The Expanded Widget Preview Menu also supports grouping and display columns for each group.
|
|
8
|
+
* Add "showQuery" in piece-page-type in order to override the query for the "show" page as "indexQuery" does it for the index page
|
|
9
|
+
|
|
10
|
+
### Fixes
|
|
11
|
+
|
|
12
|
+
* Resolved a bug in which users making a password error in the presence of pre-login checks such as a CAPTCHA were unable to try again until they refreshed the page.
|
|
13
|
+
|
|
14
|
+
## 3.28.1 (2022-09-15)
|
|
15
|
+
|
|
16
|
+
### Fixes
|
|
17
|
+
|
|
18
|
+
* `AposInputBoolean` can now be `required` and have the value `false`.
|
|
19
|
+
* Schema fields containing boolean filters can now list both `yes` and `no` choices according to available values in the database.
|
|
20
|
+
* Fix attachment `getHeight()` and `getWidth()` template helpers by changing the assignment of the `attachment._crop` property.
|
|
21
|
+
* Change assignment of `attachment._focalPoint` for consistency.
|
|
22
|
+
|
|
3
23
|
## 3.28.0 (2022-08-31)
|
|
4
24
|
|
|
5
25
|
### Fixes
|
|
@@ -99,7 +119,7 @@ Hotfix: always waits for the DOM to be ready before initializing the Apostrophe
|
|
|
99
119
|
|
|
100
120
|
### Fixes
|
|
101
121
|
|
|
102
|
-
* Fix a Webpack cache issue leading to modules symlinked in `node_modules` not being rebuilt.
|
|
122
|
+
* Fix a Webpack cache issue leading to modules symlinked in `node_modules` not being rebuilt.
|
|
103
123
|
* Fixes login maximum attempts error message that wasn't showing the plural when lockoutMinutes is more than 1.
|
|
104
124
|
* Fixes the text color of the current array item's slat label in the array editor modal.
|
|
105
125
|
* Fixes the maximum width of an array item's slat label so as to not obscure the Remove button in narrow viewports.
|
|
@@ -113,13 +133,13 @@ Hotfix: always waits for the DOM to be ready before initializing the Apostrophe
|
|
|
113
133
|
### Fixes
|
|
114
134
|
|
|
115
135
|
* Work around backwards compatibility break in `sass` module by pinning to `sass` `1.50.x` while we investigate. If you saw the error `RangeError: Invalid value: Not in inclusive range 0..145: -1` you can now fix that by upgrading with `npm update`. If it does not immediately clear up the issue in development, try `node app @apostrophecms/asset:clear-cache`.
|
|
116
|
-
|
|
136
|
+
|
|
117
137
|
## 3.21.0 (2022-05-25)
|
|
118
138
|
|
|
119
139
|
### Adds
|
|
120
140
|
|
|
121
141
|
* Trigger only the relevant build when in a watch mode (development). The build paths should not contain comma (`,`).
|
|
122
|
-
* Adds an `unpublish` method, available for any doc-type.
|
|
142
|
+
* Adds an `unpublish` method, available for any doc-type.
|
|
123
143
|
An _Unpublish_ option has also been added to the context menu of the modal when editing a piece or a page.
|
|
124
144
|
* Allows developers to group fields in relationships the same way it's done for normal schemas.
|
|
125
145
|
|
|
@@ -196,9 +216,9 @@ An _Unpublish_ option has also been added to the context menu of the modal when
|
|
|
196
216
|
* Adds possibility for modules to [add extra frontend bundles for scss and js](https://v3.docs.apostrophecms.org/guide/webpack.html). This is useful when the `ui/src` build would otherwise be very large due to code used on rarely accessed pages.
|
|
197
217
|
* Loads the right bundles on the right pages depending on the page template and the loaded widgets. Logged-in users have all the bundles on every page, because they might introduce widgets at any time.
|
|
198
218
|
* Fixes deprecation warnings displayed after running `npm install`, for dependencies that are directly included by this package.
|
|
199
|
-
* Implement custom ETags emission when `etags` cache option is enabled. [See the documentation for more information](https://v3.docs.apostrophecms.org/guide/caching.html).
|
|
200
|
-
It allows caching of pages and pieces, using a cache invalidation mechanism that takes into account related (and reverse related) document updates, thanks to backlinks mentioned above.
|
|
201
|
-
Note that for now, only single pages and pieces benefit from the ETags caching system (pages' and pieces' `getOne` REST API route, and regular served pages).
|
|
219
|
+
* Implement custom ETags emission when `etags` cache option is enabled. [See the documentation for more information](https://v3.docs.apostrophecms.org/guide/caching.html).
|
|
220
|
+
It allows caching of pages and pieces, using a cache invalidation mechanism that takes into account related (and reverse related) document updates, thanks to backlinks mentioned above.
|
|
221
|
+
Note that for now, only single pages and pieces benefit from the ETags caching system (pages' and pieces' `getOne` REST API route, and regular served pages).
|
|
202
222
|
The cache of an index page corresponding to the type of a piece that was just saved will automatically be invalidated. However, please consider that it won't be effective when a related piece is saved, therefore the cache will automatically be invalidated _after_ the cache lifetime set in `maxAge` cache option.
|
|
203
223
|
|
|
204
224
|
### Fixes
|
|
@@ -43,9 +43,10 @@ module.exports = {
|
|
|
43
43
|
throw self.apos.error('invalid');
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
const widgets = self.getWidgets(field.options);
|
|
47
|
+
|
|
48
|
+
const options = widgets[type] || {};
|
|
47
49
|
|
|
48
|
-
options = options || {};
|
|
49
50
|
const manager = self.getWidgetManager(type);
|
|
50
51
|
if (!manager) {
|
|
51
52
|
self.warnMissingWidgetType(type);
|
|
@@ -96,6 +97,20 @@ module.exports = {
|
|
|
96
97
|
setWidgetManager(name, manager) {
|
|
97
98
|
self.widgetManagers[name] = manager;
|
|
98
99
|
},
|
|
100
|
+
getWidgets(options) {
|
|
101
|
+
let widgets = options.widgets || {};
|
|
102
|
+
|
|
103
|
+
if (options.groups) {
|
|
104
|
+
for (const group of Object.keys(options.groups)) {
|
|
105
|
+
widgets = {
|
|
106
|
+
...widgets,
|
|
107
|
+
...options.groups[group].widgets
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return widgets;
|
|
113
|
+
},
|
|
99
114
|
// Get the manager object for the given widget type name.
|
|
100
115
|
getWidgetManager(name) {
|
|
101
116
|
return self.widgetManagers[name];
|
|
@@ -140,7 +155,12 @@ module.exports = {
|
|
|
140
155
|
in an options property.
|
|
141
156
|
`);
|
|
142
157
|
}
|
|
143
|
-
|
|
158
|
+
|
|
159
|
+
const widgets = self.getWidgets(options);
|
|
160
|
+
|
|
161
|
+
options.widgets = widgets;
|
|
162
|
+
|
|
163
|
+
_.each(widgets, function (options, name) {
|
|
144
164
|
const manager = self.widgetManagers[name];
|
|
145
165
|
if (manager) {
|
|
146
166
|
choices.push({
|
|
@@ -254,7 +274,8 @@ module.exports = {
|
|
|
254
274
|
options = options || {};
|
|
255
275
|
const result = [];
|
|
256
276
|
const errors = [];
|
|
257
|
-
const widgetsOptions = options
|
|
277
|
+
const widgetsOptions = self.getWidgets(options);
|
|
278
|
+
|
|
258
279
|
for (let i = 0; i < items.length; i++) {
|
|
259
280
|
const item = items[i];
|
|
260
281
|
if ((item == null) || typeof item !== 'object' || typeof item.type !== 'string') {
|
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="apos-area-menu" :class="{'apos-is-focused': groupIsFocused}">
|
|
3
|
+
<AposContextMenu
|
|
4
|
+
:disabled="isDisabled"
|
|
5
|
+
:button="buttonOptions"
|
|
6
|
+
v-bind="extendedContextMenuOptions"
|
|
7
|
+
@open="menuOpen"
|
|
8
|
+
@close="menuClose"
|
|
9
|
+
ref="contextMenu"
|
|
10
|
+
:popover-modifiers="inContext ? ['z-index-in-context'] : []"
|
|
11
|
+
>
|
|
12
|
+
<ul class="apos-area-menu__wrapper">
|
|
13
|
+
<li
|
|
14
|
+
class="apos-area-menu__item"
|
|
15
|
+
v-for="(item, itemIndex) in myMenu"
|
|
16
|
+
:key="item.type ? `${item.type}_${item.label}` : item.label"
|
|
17
|
+
:class="{'apos-has-group': item.items}"
|
|
18
|
+
:ref="`item-${itemIndex}`"
|
|
19
|
+
>
|
|
20
|
+
<dl v-if="item.items" class="apos-area-menu__group">
|
|
21
|
+
<dt>
|
|
22
|
+
<button
|
|
23
|
+
:for="item.label" class="apos-area-menu__group-label"
|
|
24
|
+
v-if="item.items" tabindex="0"
|
|
25
|
+
:id="`${menuId}-trigger-${itemIndex}`"
|
|
26
|
+
:aria-controls="`${menuId}-group-${itemIndex}`"
|
|
27
|
+
@focus="groupFocused"
|
|
28
|
+
@blur="groupBlurred"
|
|
29
|
+
@click="toggleGroup(itemIndex)"
|
|
30
|
+
@keydown.prevent.space="toggleGroup(itemIndex)"
|
|
31
|
+
@keydown.prevent.enter="toggleGroup(itemIndex)"
|
|
32
|
+
@keydown.prevent.arrow-down="switchGroup(itemIndex, 1)"
|
|
33
|
+
@keydown.prevent.arrow-up="switchGroup(itemIndex, -1)"
|
|
34
|
+
@keydown.prevent.home="switchGroup(itemIndex, 0)"
|
|
35
|
+
@keydown.prevent.end="switchGroup(itemIndex, null)"
|
|
36
|
+
ref="groupButton"
|
|
37
|
+
>
|
|
38
|
+
<span>{{ item.label }}</span>
|
|
39
|
+
<chevron-up-icon
|
|
40
|
+
class="apos-area-menu__group-chevron"
|
|
41
|
+
:class="{'apos-is-active': itemIndex === active}" :size="13"
|
|
42
|
+
/>
|
|
43
|
+
</button>
|
|
44
|
+
</dt>
|
|
45
|
+
<dd class="apos-area-menu__group-list" role="region">
|
|
46
|
+
<ul
|
|
47
|
+
class="apos-area-menu__items apos-area-menu__items--accordion"
|
|
48
|
+
:class="{'apos-is-active': active === itemIndex}"
|
|
49
|
+
:id="`${menuId}-group-${itemIndex}`"
|
|
50
|
+
:aria-labelledby="`${menuId}-trigger-${itemIndex}`"
|
|
51
|
+
:aria-expanded="active === itemIndex ? 'true' : 'false'"
|
|
52
|
+
>
|
|
53
|
+
<li
|
|
54
|
+
class="apos-area-menu__item"
|
|
55
|
+
v-for="(child, childIndex) in item.items"
|
|
56
|
+
:key="child.name"
|
|
57
|
+
:ref="`child-${index}-${childIndex}`"
|
|
58
|
+
>
|
|
59
|
+
<AposAreaMenuItem
|
|
60
|
+
@click="add(child)"
|
|
61
|
+
:item="child"
|
|
62
|
+
:tabbable="itemIndex === active"
|
|
63
|
+
@up="switchItem(`child-${itemIndex}-${childIndex - 1}`, -1)"
|
|
64
|
+
@down="switchItem(`child-${itemIndex}-${childIndex + 1}`, 1)"
|
|
65
|
+
/>
|
|
66
|
+
</li>
|
|
67
|
+
</ul>
|
|
68
|
+
</dd>
|
|
69
|
+
</dl>
|
|
70
|
+
<AposAreaMenuItem
|
|
71
|
+
v-else
|
|
72
|
+
@click="add(item)"
|
|
73
|
+
:item="item"
|
|
74
|
+
@up="switchItem(`item-${itemIndex - 1}`, -1)"
|
|
75
|
+
@down="switchItem(`item-${itemIndex + 1}`, 1)"
|
|
76
|
+
/>
|
|
77
|
+
</li>
|
|
78
|
+
</ul>
|
|
79
|
+
</AposContextMenu>
|
|
80
|
+
</div>
|
|
81
|
+
</template>
|
|
82
|
+
|
|
83
|
+
<script>
|
|
84
|
+
import cuid from 'cuid';
|
|
85
|
+
|
|
86
|
+
export default {
|
|
87
|
+
name: 'AposAreaContextualMenu',
|
|
88
|
+
props: {
|
|
89
|
+
buttonOptions: {
|
|
90
|
+
type: Object,
|
|
91
|
+
required: true
|
|
92
|
+
},
|
|
93
|
+
contextMenuOptions: {
|
|
94
|
+
type: Object,
|
|
95
|
+
required: true
|
|
96
|
+
},
|
|
97
|
+
index: {
|
|
98
|
+
type: Number,
|
|
99
|
+
default: 0
|
|
100
|
+
},
|
|
101
|
+
options: {
|
|
102
|
+
type: Object,
|
|
103
|
+
required: true
|
|
104
|
+
},
|
|
105
|
+
maxReached: {
|
|
106
|
+
type: Boolean
|
|
107
|
+
},
|
|
108
|
+
disabled: {
|
|
109
|
+
type: Boolean,
|
|
110
|
+
default: false
|
|
111
|
+
},
|
|
112
|
+
// NOTE: Left for backwards compatibility.
|
|
113
|
+
// Should use options now instead.
|
|
114
|
+
widgetOptions: {
|
|
115
|
+
type: Object,
|
|
116
|
+
default: function() {
|
|
117
|
+
return {};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
emits: [ 'menu-close', 'menu-open', 'add' ],
|
|
122
|
+
data() {
|
|
123
|
+
return {
|
|
124
|
+
active: 0,
|
|
125
|
+
groupIsFocused: false,
|
|
126
|
+
inContext: true
|
|
127
|
+
};
|
|
128
|
+
},
|
|
129
|
+
computed: {
|
|
130
|
+
moduleOptions() {
|
|
131
|
+
return window.apos.area;
|
|
132
|
+
},
|
|
133
|
+
isDisabled() {
|
|
134
|
+
let flag = this.disabled;
|
|
135
|
+
if (this.maxReached) {
|
|
136
|
+
flag = true;
|
|
137
|
+
}
|
|
138
|
+
return flag;
|
|
139
|
+
},
|
|
140
|
+
extendedContextMenuOptions() {
|
|
141
|
+
const modifiers = [ 'unpadded' ];
|
|
142
|
+
if (!this.groupedMenus) {
|
|
143
|
+
modifiers.push('tb-padded');
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
menuPlacement: 'bottom',
|
|
147
|
+
menuOffset: 15,
|
|
148
|
+
...this.contextMenuOptions,
|
|
149
|
+
modifiers
|
|
150
|
+
};
|
|
151
|
+
},
|
|
152
|
+
groupedMenus() {
|
|
153
|
+
let flag = false;
|
|
154
|
+
this.contextMenuOptions.menu.forEach((e) => {
|
|
155
|
+
if (e.items) {
|
|
156
|
+
flag = true;
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
return flag;
|
|
160
|
+
},
|
|
161
|
+
myMenu() {
|
|
162
|
+
const clipboard = apos.area.widgetClipboard.get();
|
|
163
|
+
const menu = [ ...this.contextMenuOptions.menu ];
|
|
164
|
+
if (clipboard) {
|
|
165
|
+
const widget = clipboard;
|
|
166
|
+
const matchingChoice = menu.find(option => option.name === widget.type);
|
|
167
|
+
if (matchingChoice) {
|
|
168
|
+
menu.unshift({
|
|
169
|
+
type: 'clipboard',
|
|
170
|
+
...matchingChoice,
|
|
171
|
+
label: {
|
|
172
|
+
key: 'apostrophe:pasteWidget',
|
|
173
|
+
widget: this.$t(matchingChoice.label)
|
|
174
|
+
},
|
|
175
|
+
clipboard: widget
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (this.groupedMenus) {
|
|
180
|
+
return this.composeGroups(menu);
|
|
181
|
+
} else {
|
|
182
|
+
return menu;
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
menuId() {
|
|
186
|
+
return `areaMenu-${cuid()}`;
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
mounted() {
|
|
190
|
+
// if this area is not in-context then it is assumed in a schema's modal and we need to bump
|
|
191
|
+
// the z-index of menus above them
|
|
192
|
+
this.inContext = !apos.util.closest(this.$el, '[data-apos-schema-area]');
|
|
193
|
+
},
|
|
194
|
+
methods: {
|
|
195
|
+
menuClose(e) {
|
|
196
|
+
this.$emit('menu-close', e);
|
|
197
|
+
},
|
|
198
|
+
menuOpen(e) {
|
|
199
|
+
this.$emit('menu-open', e);
|
|
200
|
+
},
|
|
201
|
+
async add(item) {
|
|
202
|
+
// Potential TODO: If we find ourselves manually flipping these bits in other AposContextMenu overrides
|
|
203
|
+
// we should consider refactoring contextmenus to be able to self close when any click takes place within their el
|
|
204
|
+
// as it is often the logical experience (not always, see tag menus and filters)
|
|
205
|
+
this.$refs.contextMenu.isOpen = false;
|
|
206
|
+
this.$emit('add', {
|
|
207
|
+
...item,
|
|
208
|
+
index: this.index
|
|
209
|
+
});
|
|
210
|
+
},
|
|
211
|
+
groupFocused() {
|
|
212
|
+
this.groupIsFocused = true;
|
|
213
|
+
},
|
|
214
|
+
groupBlurred() {
|
|
215
|
+
this.groupIsFocused = false;
|
|
216
|
+
},
|
|
217
|
+
composeGroups(menu) {
|
|
218
|
+
const ungrouped = {
|
|
219
|
+
label: 'apostrophe:ungroupedWidgets',
|
|
220
|
+
items: []
|
|
221
|
+
};
|
|
222
|
+
const myMenu = [];
|
|
223
|
+
|
|
224
|
+
menu.forEach((item) => {
|
|
225
|
+
if (!item.items) {
|
|
226
|
+
ungrouped.items.push(item);
|
|
227
|
+
} else {
|
|
228
|
+
myMenu.push(item);
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
if (ungrouped.items.length) {
|
|
233
|
+
myMenu.push(ungrouped);
|
|
234
|
+
}
|
|
235
|
+
return myMenu;
|
|
236
|
+
},
|
|
237
|
+
|
|
238
|
+
toggleGroup(index) {
|
|
239
|
+
if (this.active !== index) {
|
|
240
|
+
this.active = index;
|
|
241
|
+
} else {
|
|
242
|
+
this.active = null;
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
|
|
246
|
+
switchGroup(index, dir) {
|
|
247
|
+
let target;
|
|
248
|
+
|
|
249
|
+
if (dir > 0) {
|
|
250
|
+
target = index < this.$refs.groupButton.length - 1 ? index + 1 : 0;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (dir < 0) {
|
|
254
|
+
target = index === 0 ? this.$refs.groupButton.length - 1 : index - 1;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (dir === 0) {
|
|
258
|
+
target = 0;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (!dir) {
|
|
262
|
+
target = this.$refs.groupButton.length - 1;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
this.$nextTick(() => {
|
|
266
|
+
this.$refs.groupButton[target].focus();
|
|
267
|
+
});
|
|
268
|
+
},
|
|
269
|
+
|
|
270
|
+
switchItem(name, dir) {
|
|
271
|
+
if (this.$refs[name]) {
|
|
272
|
+
this.$refs[name][0].querySelector('button').focus();
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
</script>
|
|
278
|
+
|
|
279
|
+
<style lang="scss" scoped>
|
|
280
|
+
|
|
281
|
+
.apos-area-menu.apos-is-focused ::v-deep .apos-context-menu__inner {
|
|
282
|
+
border: 1px solid var(--a-base-4);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.apos-area-menu.apos-is-focused ::v-deep .apos-context-menu__tip-outline {
|
|
286
|
+
stroke: var(--a-base-4);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
.apos-area-menu__wrapper,
|
|
290
|
+
.apos-area-menu__items,
|
|
291
|
+
.apos-area-menu__group-list {
|
|
292
|
+
@include apos-list-reset();
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
.apos-area-menu__wrapper {
|
|
296
|
+
min-width: 250px;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
.apos-area-menu__button {
|
|
300
|
+
@include apos-button-reset();
|
|
301
|
+
@include type-base;
|
|
302
|
+
box-sizing: border-box;
|
|
303
|
+
width: 100%;
|
|
304
|
+
padding: 5px 20px;
|
|
305
|
+
color: var(--a-base-1);
|
|
306
|
+
|
|
307
|
+
&:hover,
|
|
308
|
+
&:focus {
|
|
309
|
+
& ::v-deep .apos-area-menu__item-icon {
|
|
310
|
+
color: var(--a-primary);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
&:hover {
|
|
315
|
+
cursor: pointer;
|
|
316
|
+
color: var(--a-text-primary);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
&:focus {
|
|
320
|
+
outline: none;
|
|
321
|
+
color: var(--a-text-primary);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
&:active {
|
|
325
|
+
color: var(--a-base-1);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
.apos-area-menu__accordion-trigger {
|
|
330
|
+
z-index: $z-index-under;
|
|
331
|
+
opacity: 0;
|
|
332
|
+
position: absolute;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.apos-area-menu__group-label {
|
|
336
|
+
@include apos-button-reset();
|
|
337
|
+
box-sizing: border-box;
|
|
338
|
+
display: flex;
|
|
339
|
+
width: 100%;
|
|
340
|
+
justify-content: space-between;
|
|
341
|
+
padding: 10px 20px;
|
|
342
|
+
&:hover {
|
|
343
|
+
cursor: pointer;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
&:focus {
|
|
347
|
+
background-color: var(--a-base-10);
|
|
348
|
+
outline: 1px solid var(--a-base-4);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
.apos-area-menu__group-chevron {
|
|
353
|
+
@include apos-transition();
|
|
354
|
+
transform: rotate(90deg);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
.apos-area-menu__group-chevron.apos-is-active {
|
|
358
|
+
transform: rotate(180deg);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
.apos-area-menu__group {
|
|
362
|
+
border-bottom: 1px solid var(--a-base-8);
|
|
363
|
+
padding-bottom: 10px;
|
|
364
|
+
margin: 10px 0;
|
|
365
|
+
}
|
|
366
|
+
.apos-area-menu__item:last-child.apos-has-group .apos-area-menu__group {
|
|
367
|
+
border-bottom: none;
|
|
368
|
+
margin-bottom: 0;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
.apos-area-menu__items--accordion {
|
|
372
|
+
overflow: hidden;
|
|
373
|
+
max-height: 0;
|
|
374
|
+
@include apos-transition($duration:0.3s);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
.apos-area-menu__items--accordion.apos-is-active {
|
|
378
|
+
transition-delay: 0.25s;
|
|
379
|
+
max-height: 20rem;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
</style>
|
|
@@ -26,9 +26,10 @@
|
|
|
26
26
|
:context-menu-options="contextMenuOptions"
|
|
27
27
|
:empty="true"
|
|
28
28
|
:index="0"
|
|
29
|
-
:
|
|
29
|
+
:options="options"
|
|
30
30
|
:max-reached="maxReached"
|
|
31
31
|
:disabled="field && field.readOnly"
|
|
32
|
+
:widget-options="options.widgets"
|
|
32
33
|
/>
|
|
33
34
|
</template>
|
|
34
35
|
</div>
|
|
@@ -138,7 +139,8 @@ export default {
|
|
|
138
139
|
contextMenuOptions: {
|
|
139
140
|
menu: this.choices
|
|
140
141
|
},
|
|
141
|
-
edited: {}
|
|
142
|
+
edited: {},
|
|
143
|
+
widgets: {}
|
|
142
144
|
};
|
|
143
145
|
},
|
|
144
146
|
computed: {
|
|
@@ -164,7 +166,7 @@ export default {
|
|
|
164
166
|
return window.apos.area;
|
|
165
167
|
},
|
|
166
168
|
types() {
|
|
167
|
-
return Object.keys(this.
|
|
169
|
+
return Object.keys(this.widgets);
|
|
168
170
|
},
|
|
169
171
|
maxReached() {
|
|
170
172
|
return this.options.max && this.next.length >= this.options.max;
|
|
@@ -195,6 +197,16 @@ export default {
|
|
|
195
197
|
this.next = this.getValidItems();
|
|
196
198
|
}
|
|
197
199
|
},
|
|
200
|
+
created() {
|
|
201
|
+
if (this.options.groups) {
|
|
202
|
+
for (const group of Object.keys(this.options.groups)) {
|
|
203
|
+
this.widgets = {
|
|
204
|
+
...this.options.groups[group].widgets,
|
|
205
|
+
...this.widgets
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
},
|
|
198
210
|
mounted() {
|
|
199
211
|
apos.bus.$on('area-updated', this.areaUpdatedHandler);
|
|
200
212
|
apos.bus.$on('widget-hover', this.updateWidgetHovered);
|
|
@@ -433,7 +445,7 @@ export default {
|
|
|
433
445
|
apos.area.activeEditor = this;
|
|
434
446
|
const widget = await apos.modal.execute(componentName, {
|
|
435
447
|
value: null,
|
|
436
|
-
options: this.
|
|
448
|
+
options: this.widgetOptionsByType(name),
|
|
437
449
|
type: name,
|
|
438
450
|
docId: this.docId
|
|
439
451
|
});
|
|
@@ -446,6 +458,18 @@ export default {
|
|
|
446
458
|
}
|
|
447
459
|
}
|
|
448
460
|
},
|
|
461
|
+
widgetOptionsByType(name) {
|
|
462
|
+
if (this.options.widgets) {
|
|
463
|
+
return this.options.widgets[name];
|
|
464
|
+
} else if (this.options.expanded) {
|
|
465
|
+
for (const info of Object.values(this.options.groups || {})) {
|
|
466
|
+
if (info?.widgets?.[name]) {
|
|
467
|
+
return info.widgets[name];
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
return null;
|
|
472
|
+
},
|
|
449
473
|
contextualWidgetDefaultData(type) {
|
|
450
474
|
return this.moduleOptions.contextualWidgetDefaultData[type];
|
|
451
475
|
},
|