Skip to content

Commit d4e121f

Browse files
committed
Breadcrumb widget operations for layout and column, remove operation for columns
1 parent 7cba5c6 commit d4e121f

9 files changed

Lines changed: 384 additions & 152 deletions

File tree

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

Lines changed: 155 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -76,28 +76,34 @@
7676
/>
7777
</li>
7878
</ol>
79-
<!-- FIXME Bring widget controls data from the widget config -->
8079
<ol
80+
v-if="widgetBreadcrumbActions.length > 0"
8181
class="apos-area-widget__breadcrumbs apos-area-widget__breadcrumbs--switch"
8282
style="margin-left: 10px;"
8383
>
8484
<li class="apos-area-widget__breadcrumb">
85-
<AposBreadcrumbSwitch
86-
:choices="[
87-
{ label: 'Content', value: 'content' },
88-
{ label: 'Layout', value: 'layout' }
89-
]"
90-
name="switch"
91-
value="layout"
92-
@update="foo"
85+
<component
86+
:is="operation.component"
87+
v-for="operation in widgetBreadcrumbActions"
88+
:key="operation.key"
89+
v-bind="operation.props"
90+
v-on="operation.listeners"
91+
/>
92+
</li>
93+
</ol>
94+
<ol
95+
v-if="widgetBreadcrumbInfos.length > 0"
96+
class="apos-area-widget__breadcrumbs apos-area-widget__breadcrumbs--info"
97+
>
98+
<li>
99+
<component
100+
:is="operation.component"
101+
v-for="operation in widgetBreadcrumbInfos"
102+
:key="`info-${operation.key}`"
103+
v-bind="operation.props"
104+
v-on="operation.listeners"
93105
/>
94106
</li>
95-
<AposIndicator
96-
icon="information-outline-icon"
97-
fill-color="var(--a-primary)"
98-
tooltip="Use Content mode to edit your widgets and Layout mode to modify your columns"
99-
class="apos-area-widget__breadcrumbs-switch__info"
100-
/>
101107
</ol>
102108
</div>
103109
<div
@@ -345,6 +351,18 @@ export default {
345351
};
346352
},
347353
computed: {
354+
operationButtonDefault() {
355+
return {
356+
iconOnly: true,
357+
icon: 'plus-icon',
358+
type: 'group',
359+
modifiers: [ 'small', 'inline' ],
360+
role: 'menuitem',
361+
class: 'apos-area-modify-controls__button',
362+
iconSize: 16,
363+
disableFocus: !this.isFocused
364+
};
365+
},
348366
// Passed only to the preview layer (custom preview components).
349367
followingValuesWithParent() {
350368
return Object.entries(this.followingValues || {})
@@ -383,12 +401,32 @@ export default {
383401
moduleOptions() {
384402
return window.apos.area;
385403
},
404+
widgetModuleOptions() {
405+
return apos.modules[this.moduleOptions?.widgetManagers[this.widget?.type]] ?? {};
406+
},
407+
widgetBreadcrumbOperations() {
408+
return (this.widgetModuleOptions.widgetBreadcrumbOperations || [])
409+
.map((operation) => ({
410+
component: this.getOperationComponent(operation),
411+
props: this.getOperationProps(operation),
412+
listeners: this.getOperationListeners(operation),
413+
key: operation.action || operation.name,
414+
type: operation.type
415+
}));
416+
},
417+
widgetBreadcrumbActions() {
418+
return this.widgetBreadcrumbOperations.filter(op => op.type !== 'info');
419+
},
420+
widgetBreadcrumbInfos() {
421+
return this.widgetBreadcrumbOperations.filter(op => op.type === 'info');
422+
},
386423
isFocused() {
387424
if (this.isSuppressed) {
388425
return false;
389426
}
390427
391428
const isWidgetFocused = this.widgetFocused === this.widget._id;
429+
// FIXME: move to the watcher?
392430
if (isWidgetFocused) {
393431
document.addEventListener('click', this.unfocus);
394432
}
@@ -481,8 +519,86 @@ export default {
481519
apos.bus.$off('widget-focus-parent', this.focusParent);
482520
},
483521
methods: {
484-
foo(update) {
485-
console.log(`name: ${update.name}, value: ${update.value}`);
522+
getOperationComponent(operation) {
523+
if (operation.type === 'info') {
524+
return 'AposIndicator';
525+
}
526+
if (operation.type === 'switch') {
527+
return 'AposBreadcrumbSwitch';
528+
}
529+
return 'AposButton';
530+
},
531+
getOperationProps(operation) {
532+
if (operation.type === 'info') {
533+
return {
534+
// class: 'apos-area-widget__breadcrumbs-switch__info',
535+
fillColor: 'var(--a-primary)',
536+
icon: operation.icon,
537+
tooltip: operation.tooltip
538+
};
539+
}
540+
541+
if (operation.type === 'switch') {
542+
return {
543+
name: operation.name,
544+
choices: operation.choices,
545+
value: operation.def,
546+
class: 'apos-area-modify-controls__button--switch'
547+
};
548+
}
549+
550+
// Button by default
551+
return {
552+
...this.operationButtonDefault,
553+
icon: operation.icon,
554+
action: operation.action,
555+
tooltip: operation.tooltip || null
556+
};
557+
},
558+
getOperationListeners(operation) {
559+
const listeners = {};
560+
if (operation.type === 'info') {
561+
return listeners;
562+
}
563+
if (operation.type === 'switch') {
564+
listeners.update = (payload) => {
565+
this.emitOperation(operation, payload);
566+
};
567+
} else {
568+
listeners.click = () => {
569+
this.handleOperationClick(operation);
570+
};
571+
}
572+
return listeners;
573+
},
574+
async handleOperationClick(operation) {
575+
const { modal } = operation;
576+
if (modal) {
577+
const result = await apos.modal.execute(modal, {
578+
widget: this.widget,
579+
widgetSchema: this.widgetModuleOptions.schema
580+
});
581+
if (result) {
582+
// TODO: make sure the update method from
583+
// modules/@apostrophecms/area/ui/apos/components/AposAreaEditor.vue
584+
// does the job and does not mess with the widget type and _id:
585+
this.$emit('update', result);
586+
}
587+
return;
588+
}
589+
590+
this.emitOperation(operation);
591+
},
592+
emitOperation(operation, payload = {}) {
593+
if (operation.action) {
594+
this.$emit(operation.action, this.i);
595+
} else {
596+
apos.bus.$emit('widget-breadcrumb-operation', {
597+
...payload,
598+
...operation,
599+
_id: this.widget._id
600+
});
601+
}
486602
},
487603
getFocusForMenu({ menuId, isOpen }) {
488604
if (
@@ -639,12 +755,28 @@ export default {
639755
padding: 0;
640756
}
641757
758+
> li {
759+
display: flex;
760+
align-items: center;
761+
margin: 0;
762+
padding: 0;
763+
764+
}
642765
}
643766
644-
.apos-area-widget__breadcrumbs-switch__info {
645-
position: absolute;
646-
transform: translateX(50%);
647-
right: -10px;
767+
.apos-area-widget__breadcrumbs.apos-area-widget__breadcrumbs--info {
768+
display: flex;
769+
border: none;
770+
background-color: transparent;
771+
772+
> li {
773+
display: flex;
774+
align-items: center;
775+
gap: 5px;
776+
margin: 0;
777+
padding: 0;
778+
779+
}
648780
}
649781
650782
@mixin showButton() {
@@ -901,13 +1033,13 @@ export default {
9011033
9021034
& {
9031035
display: flex;
904-
height: 32px;
9051036
box-sizing: border-box;
9061037
align-items: center;
1038+
height: 32px;
9071039
margin: 0 0 8px;
9081040
padding: 4px 6px;
909-
background-color: var(--a-background-primary);
9101041
border: 1px solid var(--a-primary-transparent-50);
1042+
background-color: var(--a-background-primary);
9111043
border-radius: 8px;
9121044
}
9131045
}

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

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
<label
55
v-for="choice in choices"
66
:key="choice.value"
7-
:for="choice.value"
87
>
98
<input
109
:id="choice.value"
@@ -54,9 +53,10 @@ export default {
5453
methods: {
5554
update(event) {
5655
this.next = event.target.value;
56+
const payload = this.choices.find(choice => choice.value === this.next);
5757
this.$emit('update', {
58-
name: this.name,
59-
value: this.next
58+
...payload,
59+
name: this.name
6060
});
6161
}
6262
}
@@ -66,8 +66,8 @@ export default {
6666
<style lang="scss" scoped>
6767
.apos-breadcrumb-switch {
6868
position: relative;
69-
padding: 0;
7069
margin: 0;
70+
padding: 0;
7171
border: none;
7272
7373
:focus {
@@ -79,26 +79,29 @@ export default {
7979
display: flex;
8080
flex-wrap: wrap;
8181
justify-content: center;
82+
8283
label:first-child span {
8384
left: -1px;
8485
}
86+
8587
label:last-child span {
8688
right: -1px;
8789
}
8890
}
8991
9092
input[type="radio"] {
93+
position: absolute;
94+
overflow: hidden;
95+
width: 1px;
96+
height: 1px;
9197
clip: rect(0 0 0 0);
9298
clip-path: inset(100%);
93-
height: 1px;
94-
overflow: hidden;
95-
position: absolute;
9699
white-space: nowrap;
97-
width: 1px;
100+
98101
&:checked + span {
99102
box-shadow: 0 0 0 1px var(--a-primary-dark-15);
100103
background-color: var(--a-primary-transparent-90);
101-
z-index: 1;
104+
z-index: $z-index-default;
102105
color: var(--a-text-inverted);
103106
}
104107
}
@@ -112,19 +115,19 @@ export default {
112115
}
113116
114117
span {
115-
height: 100%;
118+
position: relative;
116119
display: inline-flex;
120+
box-sizing: border-box;
117121
align-items: center;
118-
cursor: pointer;
119-
position: relative;
120-
margin-left: .0625em;
121-
letter-spacing: 0.5px;
122+
height: 100%;
123+
margin-left: .063em;
124+
padding: 0 8px;
122125
color: var(--a-base-1);
123126
text-align: center;
124-
transition: background-color .5s ease;
125-
box-sizing: border-box;
127+
transition: background-color 500ms ease;
128+
cursor: pointer;
129+
letter-spacing: 0.5px;
126130
border-radius: 4px;
127-
padding: 0 8px;
128131
}
129132
}
130133
}

modules/@apostrophecms/layout-column-widget/index.js

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,22 @@ module.exports = {
5151
label: 'apostrophe:layoutColumn',
5252
operationsInBreadcrumb: true
5353
},
54-
// widgetOperations: {
55-
// add: {
56-
// move: {
57-
// icon: 'move',
58-
// tooltip: 'Move',
59-
// rawEvents: [ 'pointerdown', 'pointermove', 'pointerup' ]
60-
// }
61-
// }
62-
// },
54+
widgetOperations(self, options) {
55+
return {
56+
add: {
57+
layoutColConfig: {
58+
placement: 'breadcrumb',
59+
icon: 'cog-icon',
60+
tooltip: 'Use Content mode to edit your widgets and Layout mode to modify your columns'
61+
},
62+
layoutColDelete: {
63+
placement: 'breadcrumb',
64+
icon: 'delete-icon',
65+
tooltip: 'Use Content mode to edit your widgets and Layout mode to modify your columns'
66+
}
67+
}
68+
};
69+
},
6370
fields: {
6471
add: {
6572
desktop: {

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,33 @@ module.exports = {
1717
defaultCellHorizontalAlignment: null,
1818
defaultCellVerticalAlignment: null
1919
},
20+
widgetOperations(self, options) {
21+
return {
22+
add: {
23+
layout: {
24+
placement: 'breadcrumb',
25+
type: 'switch',
26+
choices: [
27+
{
28+
label: 'Content',
29+
value: 'content'
30+
},
31+
{
32+
label: 'Layout',
33+
value: 'layout'
34+
}
35+
],
36+
def: 'content'
37+
},
38+
layoutHelp: {
39+
placement: 'breadcrumb',
40+
type: 'info',
41+
icon: 'information-outline-icon',
42+
tooltip: 'Use Content mode to edit your widgets and Layout mode to modify your columns'
43+
}
44+
}
45+
};
46+
},
2047
fields(self, options) {
2148
return {
2249
add: {

0 commit comments

Comments
 (0)