apostrophe 4.1.1 → 4.2.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 +34 -1
- package/lib/mongodb-connect.js +1 -1
- package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposAdminBarMenu.vue +1 -0
- package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextBar.vue +22 -16
- package/modules/@apostrophecms/area/index.js +1 -1
- package/modules/@apostrophecms/area/ui/apos/components/AposAreaEditor.vue +6 -1
- package/modules/@apostrophecms/db/index.js +0 -6
- package/modules/@apostrophecms/doc/index.js +19 -31
- package/modules/@apostrophecms/doc/lib/migrations.js +59 -0
- package/modules/@apostrophecms/doc-type/index.js +5 -2
- package/modules/@apostrophecms/express/index.js +3 -2
- package/modules/@apostrophecms/i18n/i18n/de.json +5 -1
- package/modules/@apostrophecms/i18n/i18n/en.json +5 -1
- package/modules/@apostrophecms/i18n/i18n/es.json +5 -1
- package/modules/@apostrophecms/i18n/i18n/fr.json +5 -1
- package/modules/@apostrophecms/i18n/i18n/it.json +5 -1
- package/modules/@apostrophecms/i18n/i18n/pt-BR.json +5 -1
- package/modules/@apostrophecms/i18n/i18n/sk.json +5 -1
- package/modules/@apostrophecms/i18n/index.js +1 -1
- package/modules/@apostrophecms/migration/index.js +5 -0
- package/modules/@apostrophecms/modal/ui/apos/apps/AposModals.js +2 -2
- package/modules/@apostrophecms/modal/ui/apos/components/AposModal.vue +188 -187
- package/modules/@apostrophecms/modal/ui/apos/composables/AposFocus.js +2 -2
- package/modules/@apostrophecms/notification/index.js +2 -0
- package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManager.vue +2 -2
- package/modules/@apostrophecms/rich-text-widget/index.js +19 -8
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposRichTextWidgetEditor.vue +44 -15
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapMarks.vue +226 -0
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapStyles.vue +90 -18
- package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Classes.js +70 -6
- package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Default.js +2 -1
- package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Document.js +1 -1
- package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Heading.js +1 -1
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputColor.vue +1 -1
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputWrapper.vue +1 -1
- package/modules/@apostrophecms/schema/ui/apos/logic/AposInputColor.js +4 -0
- package/modules/@apostrophecms/schema/ui/apos/logic/AposInputSlug.js +3 -0
- package/modules/@apostrophecms/ui/ui/apos/components/AposButtonSplit.vue +2 -2
- package/modules/@apostrophecms/ui/ui/apos/lib/i18next.js +4 -8
- package/modules/@apostrophecms/user/index.js +40 -14
- package/modules/@apostrophecms/user/lib/password-hash.js +122 -0
- package/modules/@apostrophecms/util/ui/src/http.js +7 -0
- package/package.json +3 -5
- package/test/docs.js +151 -0
- package/test/password-hash.js +56 -0
- package/test/users.js +19 -3
- package/.github/workflows/outdated-dependencies.yml +0 -43
- package/modules/@apostrophecms/modal/ui/apos/mixins/AposFocusMixin.js +0 -91
|
@@ -11,10 +11,10 @@
|
|
|
11
11
|
:class="classes"
|
|
12
12
|
role="dialog"
|
|
13
13
|
aria-modal="true"
|
|
14
|
-
:aria-labelledby="id"
|
|
14
|
+
:aria-labelledby="state.id"
|
|
15
15
|
data-apos-modal
|
|
16
|
-
@keydown="cycleElementsToFocus"
|
|
17
16
|
@focus.capture="storeFocusedElement"
|
|
17
|
+
@keydown="onKeydown"
|
|
18
18
|
>
|
|
19
19
|
<transition :name="transitionType">
|
|
20
20
|
<div
|
|
@@ -41,16 +41,16 @@
|
|
|
41
41
|
<template v-else>
|
|
42
42
|
<header v-if="!modal.disableHeader" class="apos-modal__header">
|
|
43
43
|
<div class="apos-modal__header__main">
|
|
44
|
-
<div v-if="
|
|
44
|
+
<div v-if="hasSlot('secondaryControls')" class="apos-modal__controls--secondary">
|
|
45
45
|
<slot name="secondaryControls" />
|
|
46
46
|
</div>
|
|
47
|
-
<h2 :id="id" class="apos-modal__heading">
|
|
47
|
+
<h2 :id="state.id" class="apos-modal__heading">
|
|
48
48
|
<span v-if="modal.a11yTitle" class="apos-sr-only">
|
|
49
49
|
{{ $t(modal.a11yTitle) }}
|
|
50
50
|
</span>
|
|
51
51
|
{{ $t(modalTitle) }}
|
|
52
52
|
</h2>
|
|
53
|
-
<div v-if="hasBeenLocalized ||
|
|
53
|
+
<div v-if="hasBeenLocalized || hasSlot('primaryControls')" class="apos-modal__controls--header">
|
|
54
54
|
<div v-if="hasBeenLocalized" class="apos-modal__locale">
|
|
55
55
|
<span class="apos-modal__locale-label">
|
|
56
56
|
{{ $t('apostrophe:locale') }}:
|
|
@@ -58,21 +58,21 @@
|
|
|
58
58
|
{{ currentLocale }}
|
|
59
59
|
</span>
|
|
60
60
|
</div>
|
|
61
|
-
<div v-if="
|
|
61
|
+
<div v-if="hasSlot('primaryControls')" class="apos-modal__controls--primary">
|
|
62
62
|
<slot name="primaryControls" />
|
|
63
63
|
</div>
|
|
64
64
|
</div>
|
|
65
65
|
</div>
|
|
66
|
-
<div v-if="
|
|
66
|
+
<div v-if="hasSlot('breadcrumbs')" class="apos-modal__breadcrumbs">
|
|
67
67
|
<slot class="apos-modal__breadcrumbs" name="breadcrumbs" />
|
|
68
68
|
</div>
|
|
69
69
|
</header>
|
|
70
70
|
<div class="apos-modal__main" :class="gridModifier">
|
|
71
|
-
<slot
|
|
71
|
+
<slot name="leftRail" />
|
|
72
72
|
<slot name="main" />
|
|
73
73
|
<slot name="rightRail" />
|
|
74
74
|
</div>
|
|
75
|
-
<footer v-if="
|
|
75
|
+
<footer v-if="hasSlot('footer')" class="apos-modal__footer">
|
|
76
76
|
<div class="apos-modal__footer__inner">
|
|
77
77
|
<slot name="footer" />
|
|
78
78
|
</div>
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
</transition>
|
|
85
85
|
</template>
|
|
86
86
|
|
|
87
|
-
<script>
|
|
87
|
+
<script setup>
|
|
88
88
|
// NOTE:
|
|
89
89
|
// To get the desired transition effect, modal props have two properties,
|
|
90
90
|
// `active` and `showModal`, which control their visibility. Basically,
|
|
@@ -94,186 +94,187 @@
|
|
|
94
94
|
// So as the modal exits, they should change in reverse. `showModal` becomes
|
|
95
95
|
// `false`, then `active` is set to `false` once the modal has finished its
|
|
96
96
|
// transition.
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
id: 'modal:' + Math.random().toString().replace('.', '')
|
|
119
|
-
};
|
|
120
|
-
},
|
|
121
|
-
computed: {
|
|
122
|
-
transitionType: function () {
|
|
123
|
-
if (this.modal.type === 'slide') {
|
|
124
|
-
if (this.modal.origin === 'left') {
|
|
125
|
-
return 'slide-right';
|
|
126
|
-
} else {
|
|
127
|
-
return 'slide-left';
|
|
128
|
-
}
|
|
129
|
-
} else {
|
|
130
|
-
return 'fade';
|
|
131
|
-
}
|
|
132
|
-
},
|
|
133
|
-
shouldTrapFocus() {
|
|
134
|
-
return this.modal.trapFocus || this.modal.trapFocus === undefined;
|
|
135
|
-
},
|
|
136
|
-
triggerFocusRefresh () {
|
|
137
|
-
return this.modal.triggerFocusRefresh;
|
|
138
|
-
},
|
|
139
|
-
hasBeenLocalized: function() {
|
|
140
|
-
return Object.keys(apos.i18n.locales).length > 1;
|
|
141
|
-
},
|
|
142
|
-
currentLocale: function() {
|
|
143
|
-
return apos.i18n.locale;
|
|
144
|
-
},
|
|
145
|
-
hasPrimaryControls: function () {
|
|
146
|
-
return !!this.$slots.primaryControls;
|
|
147
|
-
},
|
|
148
|
-
hasSecondaryControls: function () {
|
|
149
|
-
return !!this.$slots.secondaryControls;
|
|
150
|
-
},
|
|
151
|
-
hasBreadcrumbs: function () {
|
|
152
|
-
return !!this.$slots.breadcrumbs;
|
|
153
|
-
},
|
|
154
|
-
hasLeftRail: function () {
|
|
155
|
-
return !!this.$slots.leftRail;
|
|
156
|
-
},
|
|
157
|
-
hasRightRail: function () {
|
|
158
|
-
return !!this.$slots.rightRail;
|
|
159
|
-
},
|
|
160
|
-
hasFooter: function () {
|
|
161
|
-
return !!this.$slots.footer;
|
|
162
|
-
},
|
|
163
|
-
classes() {
|
|
164
|
-
const classes = [ 'apos-modal' ];
|
|
165
|
-
classes.push(`apos-modal--${this.modal.type}`);
|
|
166
|
-
if (this.modal.type === 'slide') {
|
|
167
|
-
if (this.modal.origin) {
|
|
168
|
-
classes.push(`apos-modal--origin-${this.modal.origin}`);
|
|
169
|
-
} else {
|
|
170
|
-
classes.push('apos-modal--origin-right');
|
|
171
|
-
}
|
|
172
|
-
classes.push('apos-modal--full-height');
|
|
173
|
-
}
|
|
174
|
-
if (this.modal.busy) {
|
|
175
|
-
classes.push('apos-modal--busy');
|
|
176
|
-
}
|
|
177
|
-
return classes.join(' ');
|
|
178
|
-
},
|
|
179
|
-
innerClasses() {
|
|
180
|
-
const classes = [];
|
|
181
|
-
if (this.modal.width) {
|
|
182
|
-
classes.push(`apos-modal__inner--${this.modal.width}`);
|
|
183
|
-
};
|
|
184
|
-
return classes;
|
|
185
|
-
},
|
|
186
|
-
gridModifier() {
|
|
187
|
-
if (this.hasLeftRail && this.hasRightRail) {
|
|
188
|
-
return 'apos-modal__main--with-rails';
|
|
189
|
-
}
|
|
190
|
-
if (this.hasLeftRail && !this.hasRightRail) {
|
|
191
|
-
return 'apos-modal__main--with-left-rail';
|
|
192
|
-
}
|
|
193
|
-
if (!this.hasLeftRail && this.hasRightRail) {
|
|
194
|
-
return 'apos-modal__main--with-right-rail';
|
|
195
|
-
}
|
|
196
|
-
return false;
|
|
197
|
-
}
|
|
198
|
-
},
|
|
199
|
-
watch: {
|
|
200
|
-
// Simple way to re-trigger focusable elements
|
|
201
|
-
// that might have been created or removed
|
|
202
|
-
// after an update, like an XHR call to get the
|
|
203
|
-
// pieces list in the AposDocsManager modal, for instance.
|
|
204
|
-
triggerFocusRefresh (newVal) {
|
|
205
|
-
if (this.shouldTrapFocus) {
|
|
206
|
-
this.$nextTick(this.trapFocus);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
},
|
|
210
|
-
mounted() {
|
|
211
|
-
if (this.shouldTrapFocus) {
|
|
212
|
-
this.$nextTick(this.trapFocus);
|
|
213
|
-
}
|
|
97
|
+
|
|
98
|
+
import {
|
|
99
|
+
ref, reactive, onMounted, computed, watch, nextTick, useSlots
|
|
100
|
+
} from 'vue';
|
|
101
|
+
import { useAposFocus } from 'Modules/@apostrophecms/modal/composables/AposFocus';
|
|
102
|
+
import cuid from 'cuid';
|
|
103
|
+
|
|
104
|
+
const {
|
|
105
|
+
cycleElementsToFocus,
|
|
106
|
+
elementsToFocus,
|
|
107
|
+
focusElement,
|
|
108
|
+
focusLastModalFocusedElement,
|
|
109
|
+
focusedElement,
|
|
110
|
+
isElementVisible,
|
|
111
|
+
storeFocusedElement
|
|
112
|
+
} = useAposFocus();
|
|
113
|
+
|
|
114
|
+
const props = defineProps({
|
|
115
|
+
modal: {
|
|
116
|
+
type: Object,
|
|
117
|
+
required: true
|
|
214
118
|
},
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
function addExcludingAttributes(element) {
|
|
272
|
-
return `${element}:not([tabindex="-1"]):not([disabled]):not([type="hidden"]):not([aria-hidden])`;
|
|
273
|
-
}
|
|
119
|
+
modalTitle: {
|
|
120
|
+
type: [ String, Object ],
|
|
121
|
+
default: ''
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
const slots = useSlots();
|
|
126
|
+
const emit = defineEmits([ 'inactive', 'esc', 'show-modal', 'no-modal', 'ready' ]);
|
|
127
|
+
const modalEl = ref(null);
|
|
128
|
+
const state = reactive({
|
|
129
|
+
id: `modal:${cuid()}`,
|
|
130
|
+
elementsToFocus,
|
|
131
|
+
focusedElement,
|
|
132
|
+
modalEl
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const transitionType = computed(() => {
|
|
136
|
+
if (props.modal.type !== 'slide') {
|
|
137
|
+
return 'fade';
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (props.modal.origin === 'left') {
|
|
141
|
+
return 'slide-right';
|
|
142
|
+
} else {
|
|
143
|
+
return 'slide-left';
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
const shouldTrapFocus = computed(() => {
|
|
148
|
+
return props.modal.trapFocus || props.modal.trapFocus === undefined;
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
const triggerFocusRefresh = computed(() => {
|
|
152
|
+
return props.modal.triggerFocusRefresh;
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
const hasBeenLocalized = computed(() => {
|
|
156
|
+
return Object.keys(apos.i18n.locales).length > 1;
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
const currentLocale = computed(() => {
|
|
160
|
+
return apos.i18n.locale;
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
function hasSlot(slotName) {
|
|
164
|
+
return Boolean(slots[slotName]);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const classes = computed(() => {
|
|
168
|
+
const classes = [ 'apos-modal' ];
|
|
169
|
+
classes.push(`apos-modal--${props.modal.type}`);
|
|
170
|
+
if (props.modal.type === 'slide') {
|
|
171
|
+
if (props.modal.origin) {
|
|
172
|
+
classes.push(`apos-modal--origin-${props.modal.origin}`);
|
|
173
|
+
} else {
|
|
174
|
+
classes.push('apos-modal--origin-right');
|
|
274
175
|
}
|
|
176
|
+
classes.push('apos-modal--full-height');
|
|
177
|
+
}
|
|
178
|
+
if (props.modal.busy) {
|
|
179
|
+
classes.push('apos-modal--busy');
|
|
180
|
+
}
|
|
181
|
+
return classes.join(' ');
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const innerClasses = computed(() => {
|
|
185
|
+
const classes = [];
|
|
186
|
+
if (props.modal.width) {
|
|
187
|
+
classes.push(`apos-modal__inner--${props.modal.width}`);
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
return classes;
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
const gridModifier = computed(() => {
|
|
194
|
+
const hasLeftRail = hasSlot('leftRail');
|
|
195
|
+
const hasRightRail = hasSlot('rightRail');
|
|
196
|
+
|
|
197
|
+
if (hasLeftRail && hasRightRail) {
|
|
198
|
+
return 'apos-modal__main--with-rails';
|
|
199
|
+
}
|
|
200
|
+
if (hasLeftRail && !hasRightRail) {
|
|
201
|
+
return 'apos-modal__main--with-left-rail';
|
|
202
|
+
}
|
|
203
|
+
if (!hasLeftRail && hasRightRail) {
|
|
204
|
+
return 'apos-modal__main--with-right-rail';
|
|
205
|
+
}
|
|
206
|
+
return false;
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
watch(triggerFocusRefresh, (newVal) => {
|
|
210
|
+
if (shouldTrapFocus.value) {
|
|
211
|
+
nextTick(trapFocus);
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
onMounted(() => {
|
|
216
|
+
if (shouldTrapFocus.value) {
|
|
217
|
+
nextTick(trapFocus);
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
function onKeydown(e) {
|
|
222
|
+
const hasPressedEsc = e.keyCode === 27;
|
|
223
|
+
if (hasPressedEsc) {
|
|
224
|
+
close(e);
|
|
225
|
+
}
|
|
226
|
+
cycleElementsToFocus(e);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function onEnter() {
|
|
230
|
+
emit('show-modal');
|
|
231
|
+
apos.modal.stack = apos.modal.stack || [];
|
|
232
|
+
|
|
233
|
+
apos.modal.stack.push(state);
|
|
234
|
+
nextTick(() => {
|
|
235
|
+
emit('ready');
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function onLeave() {
|
|
240
|
+
emit('no-modal');
|
|
241
|
+
|
|
242
|
+
apos.modal.stack = apos.modal.stack
|
|
243
|
+
.filter(modal => modal.id !== state.id);
|
|
244
|
+
|
|
245
|
+
focusLastModalFocusedElement();
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function trapFocus() {
|
|
249
|
+
const elementSelectors = [
|
|
250
|
+
'[tabindex]',
|
|
251
|
+
'[href]',
|
|
252
|
+
'input',
|
|
253
|
+
'select',
|
|
254
|
+
'textarea',
|
|
255
|
+
'button'
|
|
256
|
+
];
|
|
257
|
+
|
|
258
|
+
const selector = elementSelectors
|
|
259
|
+
.map(addExcludingAttributes)
|
|
260
|
+
.join(', ');
|
|
261
|
+
|
|
262
|
+
elementsToFocus.value = [ ...modalEl.value.querySelectorAll(selector) ]
|
|
263
|
+
.filter(isElementVisible);
|
|
264
|
+
|
|
265
|
+
focusElement(focusedElement.value, elementsToFocus.value[0]);
|
|
266
|
+
|
|
267
|
+
function addExcludingAttributes(element) {
|
|
268
|
+
return `${element}:not([tabindex="-1"]):not([disabled]):not([type="hidden"]):not([aria-hidden])`;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function close() {
|
|
273
|
+
if (apos.modal.stack.at(-1)?.id !== state.id) {
|
|
274
|
+
return;
|
|
275
275
|
}
|
|
276
|
-
|
|
276
|
+
emit('esc');
|
|
277
|
+
}
|
|
277
278
|
</script>
|
|
278
279
|
|
|
279
280
|
<style lang="scss" scoped>
|
|
@@ -8,7 +8,7 @@ export function useAposFocus() {
|
|
|
8
8
|
elementsToFocus,
|
|
9
9
|
focusedElement,
|
|
10
10
|
cycleElementsToFocus,
|
|
11
|
-
|
|
11
|
+
focusLastModalFocusedElement,
|
|
12
12
|
storeFocusedElement,
|
|
13
13
|
focusElement,
|
|
14
14
|
isElementVisible
|
|
@@ -56,7 +56,7 @@ export function useAposFocus() {
|
|
|
56
56
|
// Focus the last focused element from the last modal.
|
|
57
57
|
// If it is not focusable (not visible/not in the DOM),
|
|
58
58
|
// fallbacks to the first focusable element from the last modal.
|
|
59
|
-
function
|
|
59
|
+
function focusLastModalFocusedElement() {
|
|
60
60
|
const lastModal = apos.modal.stack.at(-1);
|
|
61
61
|
|
|
62
62
|
if (!lastModal) {
|
|
@@ -405,11 +405,11 @@ export default {
|
|
|
405
405
|
},
|
|
406
406
|
shortcutNew(event) {
|
|
407
407
|
const interesting = event.keyCode === 78; // N(ew)
|
|
408
|
-
const
|
|
408
|
+
const topModalId = apos.modal.stack.at(-1)?.id;
|
|
409
409
|
if (
|
|
410
410
|
interesting &&
|
|
411
411
|
document.activeElement.tagName !== 'INPUT' &&
|
|
412
|
-
this.$refs.modal.id ===
|
|
412
|
+
this.$refs.modal.id === topModalId
|
|
413
413
|
) {
|
|
414
414
|
this.create();
|
|
415
415
|
}
|
|
@@ -142,9 +142,15 @@ module.exports = {
|
|
|
142
142
|
widgetEditor: 'AposRichTextWidgetEditor'
|
|
143
143
|
},
|
|
144
144
|
editorTools: {
|
|
145
|
-
|
|
145
|
+
nodes: {
|
|
146
146
|
component: 'AposTiptapStyles',
|
|
147
|
-
label: 'apostrophe:
|
|
147
|
+
label: 'apostrophe:richTextNodeStyles',
|
|
148
|
+
icon: 'format-text-icon'
|
|
149
|
+
},
|
|
150
|
+
marks: {
|
|
151
|
+
component: 'AposTiptapMarks',
|
|
152
|
+
label: 'apostrophe:richTextMarkStyles',
|
|
153
|
+
icon: 'palette-swatch-icon'
|
|
148
154
|
},
|
|
149
155
|
table: {
|
|
150
156
|
component: 'AposTiptapTable',
|
|
@@ -311,9 +317,10 @@ module.exports = {
|
|
|
311
317
|
setNode: [ 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'pre', 'div' ],
|
|
312
318
|
toggleMark: [
|
|
313
319
|
'b', 'strong', 'code', 'mark', 'em', 'i',
|
|
314
|
-
'a', 's', 'del', 'strike', '
|
|
320
|
+
'a', 's', 'del', 'strike', 'u', 'anchor',
|
|
315
321
|
'superscript', 'subscript'
|
|
316
322
|
],
|
|
323
|
+
toggleClassOrToggleMark: [ 'span' ],
|
|
317
324
|
wrapIn: [ 'blockquote' ]
|
|
318
325
|
},
|
|
319
326
|
tiptapTypes: {
|
|
@@ -347,7 +354,8 @@ module.exports = {
|
|
|
347
354
|
icons: {
|
|
348
355
|
'format-text-icon': 'FormatText',
|
|
349
356
|
'format-color-highlight-icon': 'FormatColorHighlight',
|
|
350
|
-
'table-icon': 'Table'
|
|
357
|
+
'table-icon': 'Table',
|
|
358
|
+
'palette-swatch-icon': 'PaletteSwatch'
|
|
351
359
|
},
|
|
352
360
|
handlers(self) {
|
|
353
361
|
return {
|
|
@@ -609,9 +617,13 @@ module.exports = {
|
|
|
609
617
|
for (const style of options.styles || []) {
|
|
610
618
|
const tag = style.tag;
|
|
611
619
|
const classes = self.getStyleClasses(style);
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
620
|
+
|
|
621
|
+
// Add classes to THIS tag's allowList
|
|
622
|
+
if (tag) {
|
|
623
|
+
allowedClasses[tag] = allowedClasses[tag] || {};
|
|
624
|
+
for (const c of classes) {
|
|
625
|
+
allowedClasses[tag][c] = true;
|
|
626
|
+
}
|
|
615
627
|
}
|
|
616
628
|
}
|
|
617
629
|
}
|
|
@@ -867,7 +879,6 @@ module.exports = {
|
|
|
867
879
|
// Add on the core default options to use, if needed.
|
|
868
880
|
getBrowserData(_super, req) {
|
|
869
881
|
const initialData = _super(req);
|
|
870
|
-
|
|
871
882
|
const finalData = {
|
|
872
883
|
...initialData,
|
|
873
884
|
tools: self.options.editorTools,
|
package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposRichTextWidgetEditor.vue
CHANGED
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
duration: 300,
|
|
9
9
|
zIndex: 2000,
|
|
10
10
|
animation: 'fade',
|
|
11
|
-
inertia: true
|
|
11
|
+
inertia: true,
|
|
12
|
+
placement: 'bottom'
|
|
12
13
|
}"
|
|
13
14
|
:editor="editor"
|
|
14
15
|
>
|
|
@@ -213,14 +214,18 @@ export default {
|
|
|
213
214
|
editorOptions() {
|
|
214
215
|
// Deep clone to prevent runaway recursive rendering
|
|
215
216
|
// as the subproperties are mutated in several places
|
|
216
|
-
// by this code and its dependencies
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
activeOptions
|
|
220
|
-
activeOptions
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
217
|
+
// by this code and its dependencies
|
|
218
|
+
let activeOptions = klona(this.options);
|
|
219
|
+
|
|
220
|
+
activeOptions = {
|
|
221
|
+
...activeOptions,
|
|
222
|
+
...this.enhanceStyles(
|
|
223
|
+
activeOptions.styles?.length
|
|
224
|
+
? activeOptions.styles
|
|
225
|
+
: this.defaultOptions.styles
|
|
226
|
+
)
|
|
227
|
+
};
|
|
228
|
+
delete activeOptions.styles;
|
|
224
229
|
|
|
225
230
|
// Allow default options to pass through if `false`
|
|
226
231
|
Object.keys(this.defaultOptions).forEach((option) => {
|
|
@@ -233,6 +238,15 @@ export default {
|
|
|
233
238
|
activeOptions.className = (activeOptions.className !== undefined)
|
|
234
239
|
? activeOptions.className : this.moduleOptions.className;
|
|
235
240
|
|
|
241
|
+
if (activeOptions.toolbar.includes('styles')) {
|
|
242
|
+
activeOptions.toolbar = activeOptions.toolbar.filter(t => t !== 'styles');
|
|
243
|
+
if (activeOptions.marks.length) {
|
|
244
|
+
activeOptions.toolbar = [ 'marks', ...activeOptions.toolbar ];
|
|
245
|
+
}
|
|
246
|
+
if (activeOptions.nodes.length) {
|
|
247
|
+
activeOptions.toolbar = [ 'nodes', ...activeOptions.toolbar ];
|
|
248
|
+
}
|
|
249
|
+
}
|
|
236
250
|
return activeOptions;
|
|
237
251
|
},
|
|
238
252
|
autofocus() {
|
|
@@ -248,7 +262,12 @@ export default {
|
|
|
248
262
|
// If we don't supply a valid instance of the first style, then
|
|
249
263
|
// the text align control will not work until the user manually
|
|
250
264
|
// applies a style or refreshes the page
|
|
251
|
-
const defaultStyle =
|
|
265
|
+
const defaultStyle =
|
|
266
|
+
this.editorOptions.nodes.length
|
|
267
|
+
? this.editorOptions.nodes.find(style => style.def)
|
|
268
|
+
: this.editorOptions.marks.length
|
|
269
|
+
? this.editorOptions.marks.find(style => style.def)
|
|
270
|
+
: null;
|
|
252
271
|
|
|
253
272
|
const _class = defaultStyle.class ? ` class="${defaultStyle.class}"` : '';
|
|
254
273
|
return `<${defaultStyle.tag}${_class}></${defaultStyle.tag}>`;
|
|
@@ -547,7 +566,13 @@ export default {
|
|
|
547
566
|
styles[0].def = true;
|
|
548
567
|
}
|
|
549
568
|
}
|
|
550
|
-
|
|
569
|
+
|
|
570
|
+
// Split styles into node and mark selects
|
|
571
|
+
const result = {
|
|
572
|
+
nodes: styles.filter(style => style.command === 'setNode'),
|
|
573
|
+
marks: styles.filter(style => style.command !== 'setNode')
|
|
574
|
+
};
|
|
575
|
+
return result;
|
|
551
576
|
},
|
|
552
577
|
localizeStyle(style) {
|
|
553
578
|
return {
|
|
@@ -559,7 +584,8 @@ export default {
|
|
|
559
584
|
return (apos.tiptapExtensions || [])
|
|
560
585
|
.map(extension => extension({
|
|
561
586
|
...this.editorOptions,
|
|
562
|
-
|
|
587
|
+
nodes: this.editorOptions.nodes.map(this.localizeStyle),
|
|
588
|
+
marks: this.editorOptions.marks.map(this.localizeStyle),
|
|
563
589
|
types: this.tiptapTypes
|
|
564
590
|
}));
|
|
565
591
|
},
|
|
@@ -717,13 +743,16 @@ function traverseNextNode(node) {
|
|
|
717
743
|
|
|
718
744
|
.apos-button--rich-text {
|
|
719
745
|
position: relative;
|
|
720
|
-
width: 24px;
|
|
721
746
|
height: 24px;
|
|
722
|
-
padding: 0;
|
|
747
|
+
padding: 0 8px;
|
|
723
748
|
border: none;
|
|
724
749
|
border-radius: var(--a-border-radius);
|
|
725
750
|
background-color: transparent;
|
|
726
751
|
color: var(--a-base-1);
|
|
752
|
+
&.apos-button--icon-only {
|
|
753
|
+
width: 24px;
|
|
754
|
+
padding: 0;
|
|
755
|
+
}
|
|
727
756
|
&:hover {
|
|
728
757
|
background-color: transparent;
|
|
729
758
|
}
|
|
@@ -779,7 +808,7 @@ function traverseNextNode(node) {
|
|
|
779
808
|
align-items: stretch;
|
|
780
809
|
max-width: 100%;
|
|
781
810
|
height: auto;
|
|
782
|
-
gap:
|
|
811
|
+
gap: 6px;
|
|
783
812
|
}
|
|
784
813
|
|
|
785
814
|
.apos-rich-text-editor__editor :deep(.ProseMirror) {
|