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
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="apos-marks-control">
|
|
3
|
+
<AposButton
|
|
4
|
+
type="rich-text"
|
|
5
|
+
class="apos-marks-control__button"
|
|
6
|
+
:label="buttonLabel"
|
|
7
|
+
:icon="tool.icon"
|
|
8
|
+
:icon-size="16"
|
|
9
|
+
:modifiers="['no-border', 'no-motion']"
|
|
10
|
+
:tooltip="{
|
|
11
|
+
content: $t(tool.label),
|
|
12
|
+
placement: 'top',
|
|
13
|
+
delay: 650
|
|
14
|
+
}"
|
|
15
|
+
@click="click"
|
|
16
|
+
/>
|
|
17
|
+
<div
|
|
18
|
+
v-if="open"
|
|
19
|
+
v-click-outside-element="close"
|
|
20
|
+
class="apos-popover apos-marks-control__dialog"
|
|
21
|
+
x-placement="bottom"
|
|
22
|
+
>
|
|
23
|
+
<AposContextMenuDialog
|
|
24
|
+
menu-placement="bottom-start"
|
|
25
|
+
class-list="apos-context-menu__dialog--unpadded"
|
|
26
|
+
>
|
|
27
|
+
<div class="apos-marks-control__content-wrapper">
|
|
28
|
+
<ul class="apos-marks-control__items">
|
|
29
|
+
<li
|
|
30
|
+
v-for="mark in options.marks"
|
|
31
|
+
:key="mark.class"
|
|
32
|
+
class="apos-marks-control__item"
|
|
33
|
+
:class="{ 'apos-marks-control__item--is-active': activeClasses.includes(mark.class) }"
|
|
34
|
+
>
|
|
35
|
+
<button class="apos-marks-control__button" @click="toggleStyle(mark)">
|
|
36
|
+
<span class="apos-marks-control__label" :class="mark.class">
|
|
37
|
+
{{ $t(mark.label) }}
|
|
38
|
+
</span>
|
|
39
|
+
</button>
|
|
40
|
+
</li>
|
|
41
|
+
</ul>
|
|
42
|
+
</div>
|
|
43
|
+
</AposContextMenuDialog>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
</template>
|
|
47
|
+
|
|
48
|
+
<script>
|
|
49
|
+
import AposEditorMixin from 'Modules/@apostrophecms/modal/mixins/AposEditorMixin';
|
|
50
|
+
|
|
51
|
+
export default {
|
|
52
|
+
name: 'AposTiptapMarks',
|
|
53
|
+
mixins: [ AposEditorMixin ],
|
|
54
|
+
props: {
|
|
55
|
+
name: {
|
|
56
|
+
type: String,
|
|
57
|
+
required: true
|
|
58
|
+
},
|
|
59
|
+
tool: {
|
|
60
|
+
type: Object,
|
|
61
|
+
required: true
|
|
62
|
+
},
|
|
63
|
+
editor: {
|
|
64
|
+
type: Object,
|
|
65
|
+
required: true
|
|
66
|
+
},
|
|
67
|
+
options: {
|
|
68
|
+
type: Object,
|
|
69
|
+
default() {
|
|
70
|
+
return {};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
data() {
|
|
75
|
+
return {
|
|
76
|
+
active: false,
|
|
77
|
+
open: false,
|
|
78
|
+
classes: this.options.marks.map(m => m.class)
|
|
79
|
+
};
|
|
80
|
+
},
|
|
81
|
+
computed: {
|
|
82
|
+
activeClasses() {
|
|
83
|
+
let activeClasses = [];
|
|
84
|
+
const { selection } = this.editor.state;
|
|
85
|
+
const content = selection.content();
|
|
86
|
+
|
|
87
|
+
traverseContent(content.content);
|
|
88
|
+
|
|
89
|
+
function traverseContent(content) {
|
|
90
|
+
content.forEach(item => {
|
|
91
|
+
if (item.attrs.class) {
|
|
92
|
+
activeClasses = activeClasses.concat(item.attrs.class.split(' '));
|
|
93
|
+
}
|
|
94
|
+
if (item?.marks?.length) {
|
|
95
|
+
traverseContent(item.marks);
|
|
96
|
+
}
|
|
97
|
+
if (item?.content?.content) {
|
|
98
|
+
traverseContent(item.content.content);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
// Filter out classes that are not in the list of available classes
|
|
103
|
+
return activeClasses.filter(value => this.classes.includes(value));
|
|
104
|
+
},
|
|
105
|
+
buttonLabel() {
|
|
106
|
+
let label;
|
|
107
|
+
if (this.activeClasses.length > 1) {
|
|
108
|
+
label = this.$t('apostrophe:richTextMarkMultipleStyles');
|
|
109
|
+
}
|
|
110
|
+
if (this.activeClasses.length === 1) {
|
|
111
|
+
label = this.options.marks.find(m => m.class === this.activeClasses[0])?.label;
|
|
112
|
+
}
|
|
113
|
+
return label || this.$t('apostrophe:richTextMarkApplyStyles');
|
|
114
|
+
},
|
|
115
|
+
hasSelection() {
|
|
116
|
+
const { state } = this.editor;
|
|
117
|
+
const { selection } = this.editor.state;
|
|
118
|
+
const { from, to } = selection;
|
|
119
|
+
const text = state.doc.textBetween(from, to, '');
|
|
120
|
+
return text !== '';
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
watch: {
|
|
124
|
+
hasSelection(newVal) {
|
|
125
|
+
if (!newVal) {
|
|
126
|
+
this.close();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
methods: {
|
|
131
|
+
toggleStyle(mark) {
|
|
132
|
+
this.editor.commands.focus();
|
|
133
|
+
this.editor.commands[mark.command](mark.type, mark.options || {});
|
|
134
|
+
this.close();
|
|
135
|
+
},
|
|
136
|
+
click() {
|
|
137
|
+
this.toggleOpen();
|
|
138
|
+
},
|
|
139
|
+
toggleOpen() {
|
|
140
|
+
this.open = !this.open;
|
|
141
|
+
},
|
|
142
|
+
close() {
|
|
143
|
+
this.open = false;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
</script>
|
|
148
|
+
|
|
149
|
+
<style lang="scss" scoped>
|
|
150
|
+
.apos-marks-control {
|
|
151
|
+
position: relative;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.apos-marks-control__button:deep(.apos-button--rich-text) {
|
|
155
|
+
&:active:after, &:focus:after {
|
|
156
|
+
background-color: var(--a-base-8);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.apos-button__label {
|
|
160
|
+
max-width: 200px;
|
|
161
|
+
overflow: hidden;
|
|
162
|
+
text-overflow: ellipsis;
|
|
163
|
+
white-space: nowrap;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.apos-marks-control__content-wrapper {
|
|
168
|
+
max-height: 200px;
|
|
169
|
+
overflow-y: scroll;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.apos-marks-control__dialog {
|
|
173
|
+
position: absolute;
|
|
174
|
+
top: calc(100% + $spacing-base);
|
|
175
|
+
left: 0;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.apos-marks-control__items {
|
|
179
|
+
display: flex;
|
|
180
|
+
flex-direction: column;
|
|
181
|
+
gap: 3px;
|
|
182
|
+
margin: 0;
|
|
183
|
+
padding: $spacing-base;
|
|
184
|
+
list-style: none;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.apos-marks-control__item {
|
|
188
|
+
white-space: nowrap;
|
|
189
|
+
max-width: 230px;
|
|
190
|
+
text-overflow: ellipsis;
|
|
191
|
+
overflow: hidden;
|
|
192
|
+
border-radius: var(--a-border-radius);
|
|
193
|
+
|
|
194
|
+
// We are adding dev-defined styles into the Apostrophe admin UI,
|
|
195
|
+
// attempt clamp down the dimensions of the label to prevent broken UI
|
|
196
|
+
// stylelint-disable declaration-no-important
|
|
197
|
+
.apos-marks-control__label {
|
|
198
|
+
position: static !important;
|
|
199
|
+
height: auto !important;
|
|
200
|
+
margin: 0 !important;
|
|
201
|
+
padding: 0 !important;
|
|
202
|
+
font-size: var(--a-type-large) !important;
|
|
203
|
+
}
|
|
204
|
+
// stylelint-enable declaration-no-important
|
|
205
|
+
|
|
206
|
+
&:hover {
|
|
207
|
+
background-color: var(--a-base-10);
|
|
208
|
+
cursor: pointer;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
&--is-active {
|
|
212
|
+
background-color: var(--a-base-10);
|
|
213
|
+
&:hover {
|
|
214
|
+
background-color: var(--a-base-9);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.apos-marks-control__button {
|
|
219
|
+
@include apos-button-reset();
|
|
220
|
+
display: block;
|
|
221
|
+
width: 100%;
|
|
222
|
+
padding: $spacing-base;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
</style>
|
|
@@ -1,27 +1,29 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="apos-tiptap-select">
|
|
3
|
-
<
|
|
3
|
+
<component
|
|
4
|
+
:is="tool.icon"
|
|
4
5
|
:size="16"
|
|
5
6
|
class="apos-tiptap-select__type-icon"
|
|
6
7
|
fill-color="currentColor"
|
|
7
8
|
/>
|
|
8
9
|
<select
|
|
9
10
|
v-apos-tooltip="{
|
|
10
|
-
content:
|
|
11
|
+
content: $t(tool.label),
|
|
11
12
|
placement: 'top',
|
|
12
13
|
delay: 650
|
|
13
14
|
}"
|
|
14
|
-
:
|
|
15
|
+
:value="active"
|
|
15
16
|
class="apos-tiptap-control apos-tiptap-control--select"
|
|
16
|
-
:style="`width:${
|
|
17
|
+
:style="`width:${$t(nodeOptions[active].label).length * 6.5}px`"
|
|
17
18
|
@change="setStyle"
|
|
18
19
|
>
|
|
19
20
|
<option
|
|
20
|
-
v-for="(style, i) in
|
|
21
|
+
v-for="(style, i) in nodeOptions"
|
|
21
22
|
:key="style.label"
|
|
22
23
|
:value="i"
|
|
24
|
+
:hidden="style.attr === 'hidden'"
|
|
23
25
|
>
|
|
24
|
-
{{ style.label }}
|
|
26
|
+
{{ $t(style.label) }}
|
|
25
27
|
</option>
|
|
26
28
|
</select>
|
|
27
29
|
<chevron-down-icon
|
|
@@ -56,27 +58,92 @@ export default {
|
|
|
56
58
|
}
|
|
57
59
|
}
|
|
58
60
|
},
|
|
61
|
+
data() {
|
|
62
|
+
return {
|
|
63
|
+
multipleSelected: false
|
|
64
|
+
};
|
|
65
|
+
},
|
|
59
66
|
computed: {
|
|
67
|
+
nodeOptions() {
|
|
68
|
+
return [ {
|
|
69
|
+
label: 'apostrophe:richTextNodeMultipleStyles',
|
|
70
|
+
attr: this.multipleSelected ? '' : 'hidden'
|
|
71
|
+
},
|
|
72
|
+
...this.options.nodes ];
|
|
73
|
+
},
|
|
60
74
|
active() {
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
75
|
+
const { selection } = this.editor.state;
|
|
76
|
+
const content = selection.content();
|
|
77
|
+
let activeEls = [];
|
|
78
|
+
const nodes = this.options.nodes.map(n => {
|
|
79
|
+
return {
|
|
80
|
+
type: n.type,
|
|
81
|
+
class: n.options.class || null,
|
|
82
|
+
level: n.options.level || null
|
|
83
|
+
};
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
if (content?.content?.content?.length) {
|
|
87
|
+
activeEls = content.content.content.map(n => {
|
|
88
|
+
return {
|
|
89
|
+
name: n.type.name,
|
|
90
|
+
class: n.attrs.class || null,
|
|
91
|
+
level: n.attrs.level || null
|
|
92
|
+
};
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Remove duplicates
|
|
97
|
+
activeEls = activeEls.filter((item, index, self) => {
|
|
98
|
+
// Find the index of the first occurrence of the current item
|
|
99
|
+
const firstIndex = self.findIndex(t =>
|
|
100
|
+
t.name === item.name &&
|
|
101
|
+
t.class === item.class &&
|
|
102
|
+
t.level === item.level
|
|
103
|
+
);
|
|
104
|
+
// If the index of the current item is the same as the first index, keep it
|
|
105
|
+
return index === firstIndex;
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
if (activeEls.length) {
|
|
109
|
+
if (activeEls.length > 1) {
|
|
110
|
+
// More than one node, show 'multiple styles' label
|
|
111
|
+
return 0;
|
|
112
|
+
} else {
|
|
113
|
+
// Only one node, show the style label
|
|
114
|
+
// the default style will look different, detect it specifically
|
|
115
|
+
if (activeEls[0].name === 'defaultNode') {
|
|
116
|
+
return 1;
|
|
117
|
+
} else {
|
|
118
|
+
const match = nodes.findIndex(node =>
|
|
119
|
+
node.class === activeEls[0].class &&
|
|
120
|
+
node.type === activeEls[0].name &&
|
|
121
|
+
node.level === activeEls[0].level
|
|
122
|
+
);
|
|
123
|
+
return match + 1;
|
|
124
|
+
}
|
|
69
125
|
}
|
|
126
|
+
} else {
|
|
127
|
+
// No nodes, show the default label
|
|
128
|
+
return 1;
|
|
70
129
|
}
|
|
71
|
-
return 0;
|
|
72
130
|
},
|
|
73
131
|
moduleOptions() {
|
|
74
132
|
return window.apos.modules['@apostrophecms/rich-text-widget'];
|
|
75
133
|
}
|
|
76
134
|
},
|
|
135
|
+
watch: {
|
|
136
|
+
active(newValue) {
|
|
137
|
+
if (newValue === 0) {
|
|
138
|
+
this.multipleSelected = true;
|
|
139
|
+
} else {
|
|
140
|
+
this.multipleSelected = false;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
},
|
|
77
144
|
methods: {
|
|
78
145
|
setStyle($event) {
|
|
79
|
-
const style = this.
|
|
146
|
+
const style = this.nodeOptions[$event.target.value];
|
|
80
147
|
this.editor.commands.focus();
|
|
81
148
|
this.editor.commands[style.command](style.type, style.options || {});
|
|
82
149
|
}
|
|
@@ -90,7 +157,7 @@ export default {
|
|
|
90
157
|
@include apos-button-reset();
|
|
91
158
|
@include apos-transition();
|
|
92
159
|
height: 100%;
|
|
93
|
-
padding: 0
|
|
160
|
+
padding: 0 $spacing-half;
|
|
94
161
|
font-size: var(--a-type-smaller);
|
|
95
162
|
|
|
96
163
|
&:focus, &:active {
|
|
@@ -102,7 +169,7 @@ export default {
|
|
|
102
169
|
position: relative;
|
|
103
170
|
display: flex;
|
|
104
171
|
align-items: center;
|
|
105
|
-
padding: 0
|
|
172
|
+
padding: 0 $spacing-half;
|
|
106
173
|
color: var(--a-base-1);
|
|
107
174
|
border-radius: var(--a-border-radius);
|
|
108
175
|
transition: all 0.5s ease;
|
|
@@ -112,6 +179,11 @@ export default {
|
|
|
112
179
|
}
|
|
113
180
|
}
|
|
114
181
|
|
|
182
|
+
.apos-tiptap-select__icon {
|
|
183
|
+
position: absolute;
|
|
184
|
+
right: 0;
|
|
185
|
+
}
|
|
186
|
+
|
|
115
187
|
.apos-tiptap-select__type-icon {
|
|
116
188
|
padding-top: 2px;
|
|
117
189
|
}
|
|
@@ -1,14 +1,74 @@
|
|
|
1
1
|
// Enhances common node/mark types to accept a class parameter
|
|
2
2
|
// and filter out classes that don't match the list on paste/parse
|
|
3
3
|
import { Extension } from '@tiptap/core';
|
|
4
|
+
|
|
4
5
|
export default (options) => {
|
|
5
6
|
// Create a class allowlist map for each element
|
|
6
7
|
const allow = {};
|
|
7
|
-
options.
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
const styles = [ ...options.nodes || [], ...options.marks || [] ];
|
|
9
|
+
styles.forEach((style) => {
|
|
10
|
+
const tag = style.tag?.toLowerCase();
|
|
11
|
+
if (tag) {
|
|
12
|
+
allow[tag] = (allow[tag] || []).concat(
|
|
13
|
+
...(style.class ? style.class.split(' ') : [])
|
|
14
|
+
);
|
|
15
|
+
}
|
|
10
16
|
});
|
|
17
|
+
|
|
11
18
|
return Extension.create({
|
|
19
|
+
addCommands() {
|
|
20
|
+
return {
|
|
21
|
+
toggleClassOrToggleMark:
|
|
22
|
+
(type, options) =>
|
|
23
|
+
({ editor, commands }) => {
|
|
24
|
+
|
|
25
|
+
// If we're in a span we need to toggle the class
|
|
26
|
+
if (editor.isActive('textStyle')) {
|
|
27
|
+
let finalClasses;
|
|
28
|
+
let currentClasses = editor.getAttributes('textStyle').class;
|
|
29
|
+
|
|
30
|
+
// // Classes can come back as null, string, or array
|
|
31
|
+
// // normalize them to an array
|
|
32
|
+
|
|
33
|
+
if (typeof currentClasses === 'string') {
|
|
34
|
+
currentClasses = currentClasses.split(' ');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (Array.isArray(currentClasses)) {
|
|
38
|
+
currentClasses = currentClasses.filter((c) => {
|
|
39
|
+
return typeof c === 'string' && c.length > 0;
|
|
40
|
+
});
|
|
41
|
+
} else {
|
|
42
|
+
currentClasses = [];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// If this el already has this class, remove it
|
|
46
|
+
if (currentClasses.includes(options.class)) {
|
|
47
|
+
finalClasses = currentClasses.filter(
|
|
48
|
+
(c) => c !== options.class
|
|
49
|
+
);
|
|
50
|
+
// If not, add it
|
|
51
|
+
} else {
|
|
52
|
+
finalClasses = currentClasses.concat(options.class);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// If we're removing the last class, remove the span
|
|
56
|
+
if (finalClasses.length === 0) {
|
|
57
|
+
commands.toggleMark('textStyle', { class: options.class });
|
|
58
|
+
} else {
|
|
59
|
+
// Update the el we found with the final classes
|
|
60
|
+
commands.updateAttributes('textStyle', {
|
|
61
|
+
class: finalClasses.length
|
|
62
|
+
? finalClasses.join(' ')
|
|
63
|
+
: null
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
} else {
|
|
67
|
+
commands.toggleMark('textStyle', { class: options.class });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
},
|
|
12
72
|
addGlobalAttributes() {
|
|
13
73
|
return [
|
|
14
74
|
{
|
|
@@ -27,18 +87,22 @@ export default (options) => {
|
|
|
27
87
|
if (!allow[tag]) {
|
|
28
88
|
return null;
|
|
29
89
|
}
|
|
90
|
+
|
|
30
91
|
const classes = (element.getAttribute('class') || '')
|
|
31
92
|
.split(' ')
|
|
32
93
|
.filter(c => allow[tag].includes(c));
|
|
94
|
+
|
|
33
95
|
// If we have valid classes, join and return them.
|
|
34
96
|
// If no valid classes for this parse, default to the
|
|
35
97
|
// the first setting for this tag (including null for tags defined without classes).
|
|
36
98
|
// else, remove classes.
|
|
99
|
+
const defaultOrNull =
|
|
100
|
+
options.nodes.find(s => s.tag === tag)?.class ||
|
|
101
|
+
options.marks.find(s => s.tag === tag)?.class ||
|
|
102
|
+
null;
|
|
37
103
|
return classes.length
|
|
38
104
|
? classes.join(' ')
|
|
39
|
-
:
|
|
40
|
-
allow[tag].length ? allow[tag][0] : null
|
|
41
|
-
);
|
|
105
|
+
: defaultOrNull;
|
|
42
106
|
}
|
|
43
107
|
}
|
|
44
108
|
}
|
|
@@ -22,7 +22,8 @@ const nodeMap = {
|
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
export default (options) => {
|
|
25
|
-
const
|
|
25
|
+
const styles = [ ...options.nodes, ...options.marks ];
|
|
26
|
+
const [ def ] = styles.filter(style => style.def);
|
|
26
27
|
|
|
27
28
|
if (!def) {
|
|
28
29
|
return;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Acts as a custom Document extension
|
|
2
2
|
import { Node } from '@tiptap/core';
|
|
3
3
|
export default (options) => {
|
|
4
|
-
const def = options.
|
|
4
|
+
const def = options.nodes.filter(style => style.def)[0];
|
|
5
5
|
let content = 'block+'; // one or more block nodes (default Document setting)
|
|
6
6
|
if (def) {
|
|
7
7
|
// one/more defaultNodes (created in ./Default) or one/more other block nodes
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import Heading from '@tiptap/extension-heading';
|
|
3
3
|
|
|
4
4
|
export default (options) => {
|
|
5
|
-
const headings = options.
|
|
5
|
+
const headings = options.nodes.filter(style => style.type === 'heading');
|
|
6
6
|
const levels = headings.map(heading => heading.options.level);
|
|
7
7
|
const defaultLevel = headings.filter(heading => heading.def).length
|
|
8
8
|
? headings.filter(heading => heading.def)[0].options.level
|
|
@@ -58,9 +58,9 @@
|
|
|
58
58
|
</span>
|
|
59
59
|
<span data-apos-test="field-meta-wrapper" class="apos-field__label-meta">
|
|
60
60
|
<component
|
|
61
|
+
:is="name"
|
|
61
62
|
v-for="{name, namespace, data} in metaComponents"
|
|
62
63
|
:key="name"
|
|
63
|
-
:is="name"
|
|
64
64
|
:field="field"
|
|
65
65
|
:items="items"
|
|
66
66
|
:namespace="namespace"
|
|
@@ -67,7 +67,10 @@ export default {
|
|
|
67
67
|
delete oldClone.archived;
|
|
68
68
|
|
|
69
69
|
oldValue = Object.values(oldClone).join(' ');
|
|
70
|
+
oldValue = oldValue.replace(/\//g, ' ');
|
|
71
|
+
|
|
70
72
|
newValue = Object.values(newClone).join(' ');
|
|
73
|
+
newValue = newValue.replace(/\//g, ' ');
|
|
71
74
|
|
|
72
75
|
if (this.compatible(oldValue, this.next) && !newValue.archived) {
|
|
73
76
|
// If this is a page slug, we only replace the last section of the slug.
|
|
@@ -65,7 +65,7 @@ const {
|
|
|
65
65
|
elementsToFocus,
|
|
66
66
|
cycleElementsToFocus,
|
|
67
67
|
focusElement,
|
|
68
|
-
|
|
68
|
+
focusLastModalFocusedElement
|
|
69
69
|
} = useAposFocus();
|
|
70
70
|
|
|
71
71
|
const props = defineProps({
|
|
@@ -164,7 +164,7 @@ function menuOpen() {
|
|
|
164
164
|
}
|
|
165
165
|
|
|
166
166
|
function menuClose() {
|
|
167
|
-
|
|
167
|
+
focusLastModalFocusedElement();
|
|
168
168
|
}
|
|
169
169
|
</script>
|
|
170
170
|
<style lang="scss" scoped>
|
|
@@ -62,15 +62,11 @@ export default {
|
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
//
|
|
66
|
-
// for just one object argument with at least a `key`
|
|
67
|
-
// property, which makes it easier to pass both
|
|
68
|
-
// a label and its interpolation values through
|
|
69
|
-
// multiple layers of code, as a single `label`
|
|
70
|
-
// property for instance. You may also specify
|
|
71
|
-
// `localize: false` to pass a string through without
|
|
72
|
-
// invoking i18next.
|
|
65
|
+
// Makes available the $t function in all components through `this`.
|
|
73
66
|
app.config.globalProperties.$t = $t;
|
|
67
|
+
|
|
68
|
+
// This is for the composition API, allowing to inject $t in any component.
|
|
69
|
+
app.provide('i18n', $t);
|
|
74
70
|
}
|
|
75
71
|
};
|
|
76
72
|
|