Skip to content

Commit 6780667

Browse files
committed
PRO-8256: allow createWidgetOperations to be filtered based on a list of widget types
1 parent 338ca26 commit 6780667

7 files changed

Lines changed: 64 additions & 25 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## UNRELEASED
4+
5+
### Adds
6+
7+
* Custom operations registered with `addCreateWidgetOperation` can now specify an `ifTypesIntersect` property containing an array of widget type names. If the area in question allows at least one, the operation is offered.
8+
39
## 4.21.0 (2025-09-03)
410

511
### Adds

modules/@apostrophecms/area/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ module.exports = {
191191
// interface regardless of whether `options.widgets` or `options.groups`
192192
// was used.
193193
getWidgets(options) {
194+
// Keep in sync with client-side implementation
194195
let widgets = options.widgets || {};
195196

196197
if (options.groups) {

modules/@apostrophecms/area/ui/apos/components/AposAreaContextualMenu.vue

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@
9595

9696
<script>
9797
import { createId } from '@paralleldrive/cuid2';
98+
import filterCreateWidgetOperations from '../lib/filter-create-widget-operations.js';
9899
99100
export default {
100101
name: 'AposAreaContextualMenu',
@@ -201,7 +202,8 @@ export default {
201202
return [];
202203
}
203204
const menu = [ ...this.contextMenuOptions.menu ];
204-
for (const createWidgetOperation of this.moduleOptions.createWidgetOperations) {
205+
const createWidgetOperations = filterCreateWidgetOperations(this.moduleOptions, this.options);
206+
for (const createWidgetOperation of createWidgetOperations) {
205207
menu.unshift({
206208
type: 'operation',
207209
...createWidgetOperation
@@ -329,6 +331,7 @@ export default {
329331
this.$refs[name][0].querySelector('button').focus();
330332
}
331333
}
334+
332335
}
333336
};
334337
</script>

modules/@apostrophecms/area/ui/apos/components/AposAreaExpandedMenu.vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@
100100
</template>
101101

102102
<script>
103+
import filterCreateWidgetOperations from '../lib/filter-create-widget-operations.js';
104+
103105
export default {
104106
name: 'AposAreaExpandedMenu',
105107
props: {
@@ -177,7 +179,7 @@ export default {
177179
return count ? +count > 1 && +count < 4 : true;
178180
},
179181
getCreateWidgetOperationsGroups() {
180-
const operations = this.moduleOptions.createWidgetOperations;
182+
const operations = filterCreateWidgetOperations(this.moduleOptions, this.options);
181183
if (operations.length === 0) {
182184
return [];
183185
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Requires the module options and an area field's .options property
2+
3+
import getWidgets from './get-widgets.js';
4+
5+
export default function filterCreateWidgetOperations(moduleOptions, areaOptions) {
6+
const widgets = getWidgets(areaOptions);
7+
return moduleOptions.createWidgetOperations.filter(createWidgetOperation => {
8+
if (createWidgetOperation.ifTypesIntersect) {
9+
const types = Object.keys(widgets);
10+
if (!types.some(type => createWidgetOperation.ifTypesIntersect.includes(type))) {
11+
return false;
12+
}
13+
}
14+
return true;
15+
});
16+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Requires an area field's .options property
2+
3+
export default function getWidgets(options) {
4+
let widgets = options.widgets || {};
5+
if (options.groups) {
6+
for (const group of Object.keys(options.groups)) {
7+
widgets = {
8+
...widgets,
9+
...options.groups[group].widgets
10+
};
11+
}
12+
}
13+
return widgets;
14+
}

modules/@apostrophecms/widget-type/index.js

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -123,29 +123,6 @@ module.exports = {
123123
origin: null,
124124
preview: true
125125
},
126-
handlers(self) {
127-
return {
128-
'apostrophe:modulesRegistered': {
129-
composeWidgetOperations() {
130-
self.widgetOperations = Object.entries(self.widgetOperations)
131-
.map(([ name, operation ]) => {
132-
if (!operation.label || !operation.modal) {
133-
throw self.apos.error('invalid', 'widgetOperations requires label and modal properties.');
134-
}
135-
136-
if (operation.secondaryLevel !== true && !operation.icon) {
137-
throw self.apos.error('invalid', 'widgetOperations requires the icon property at primary level.');
138-
}
139-
140-
return {
141-
name,
142-
...operation
143-
};
144-
});
145-
}
146-
}
147-
};
148-
},
149126
init(self) {
150127
self.isExplicitOrigin = self.options.origin !== null;
151128
self.options.origin = self.options.origin || 'right';
@@ -170,6 +147,7 @@ module.exports = {
170147
self.label = self.options.label;
171148

172149
self.composeSchema();
150+
self.composeWidgetOperations();
173151

174152
self.apos.area.setWidgetManager(self.name, self);
175153

@@ -187,6 +165,7 @@ module.exports = {
187165
},
188166
methods(self) {
189167
return {
168+
190169
composeSchema() {
191170
self.schema = self.apos.schema.compose({
192171
addFields: self.apos.schema.fieldsToArray(`Module ${self.__meta.name}`, self.fields),
@@ -203,6 +182,24 @@ module.exports = {
203182
});
204183
},
205184

185+
composeWidgetOperations() {
186+
self.widgetOperations = Object.entries(self.widgetOperations)
187+
.map(([ name, operation ]) => {
188+
if (!operation.label || !operation.modal) {
189+
throw self.apos.error('invalid', 'widgetOperations requires label and modal properties.');
190+
}
191+
192+
if (operation.secondaryLevel !== true && !operation.icon) {
193+
throw self.apos.error('invalid', 'widgetOperations requires the icon property at primary level.');
194+
}
195+
196+
return {
197+
name,
198+
...operation
199+
};
200+
});
201+
},
202+
206203
// Returns markup for the widget. Invoked via `{% widget ... %}` in the
207204
// `@apostrophecms/area` module as it iterates over widgets in
208205
// an area. The default behavior is to render the template for the widget,

0 commit comments

Comments
 (0)