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.
Files changed (48) hide show
  1. package/CHANGELOG.md +34 -1
  2. package/lib/mongodb-connect.js +1 -1
  3. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposAdminBarMenu.vue +1 -0
  4. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextBar.vue +22 -16
  5. package/modules/@apostrophecms/area/index.js +1 -1
  6. package/modules/@apostrophecms/area/ui/apos/components/AposAreaEditor.vue +6 -1
  7. package/modules/@apostrophecms/db/index.js +0 -6
  8. package/modules/@apostrophecms/doc/index.js +19 -31
  9. package/modules/@apostrophecms/doc/lib/migrations.js +59 -0
  10. package/modules/@apostrophecms/doc-type/index.js +5 -2
  11. package/modules/@apostrophecms/express/index.js +3 -2
  12. package/modules/@apostrophecms/i18n/i18n/de.json +5 -1
  13. package/modules/@apostrophecms/i18n/i18n/en.json +5 -1
  14. package/modules/@apostrophecms/i18n/i18n/es.json +5 -1
  15. package/modules/@apostrophecms/i18n/i18n/fr.json +5 -1
  16. package/modules/@apostrophecms/i18n/i18n/it.json +5 -1
  17. package/modules/@apostrophecms/i18n/i18n/pt-BR.json +5 -1
  18. package/modules/@apostrophecms/i18n/i18n/sk.json +5 -1
  19. package/modules/@apostrophecms/i18n/index.js +1 -1
  20. package/modules/@apostrophecms/migration/index.js +5 -0
  21. package/modules/@apostrophecms/modal/ui/apos/apps/AposModals.js +2 -2
  22. package/modules/@apostrophecms/modal/ui/apos/components/AposModal.vue +188 -187
  23. package/modules/@apostrophecms/modal/ui/apos/composables/AposFocus.js +2 -2
  24. package/modules/@apostrophecms/notification/index.js +2 -0
  25. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManager.vue +2 -2
  26. package/modules/@apostrophecms/rich-text-widget/index.js +19 -8
  27. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposRichTextWidgetEditor.vue +44 -15
  28. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapMarks.vue +226 -0
  29. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapStyles.vue +90 -18
  30. package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Classes.js +70 -6
  31. package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Default.js +2 -1
  32. package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Document.js +1 -1
  33. package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Heading.js +1 -1
  34. package/modules/@apostrophecms/schema/ui/apos/components/AposInputColor.vue +1 -1
  35. package/modules/@apostrophecms/schema/ui/apos/components/AposInputWrapper.vue +1 -1
  36. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputColor.js +4 -0
  37. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputSlug.js +3 -0
  38. package/modules/@apostrophecms/ui/ui/apos/components/AposButtonSplit.vue +2 -2
  39. package/modules/@apostrophecms/ui/ui/apos/lib/i18next.js +4 -8
  40. package/modules/@apostrophecms/user/index.js +40 -14
  41. package/modules/@apostrophecms/user/lib/password-hash.js +122 -0
  42. package/modules/@apostrophecms/util/ui/src/http.js +7 -0
  43. package/package.json +3 -5
  44. package/test/docs.js +151 -0
  45. package/test/password-hash.js +56 -0
  46. package/test/users.js +19 -3
  47. package/.github/workflows/outdated-dependencies.yml +0 -43
  48. 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="hasSecondaryControls" class="apos-modal__controls--secondary">
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 || hasPrimaryControls" class="apos-modal__controls--header">
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="hasPrimaryControls" class="apos-modal__controls--primary">
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="hasBreadcrumbs" class="apos-modal__breadcrumbs">
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 v-if="hasLeftRail" name="leftRail" />
71
+ <slot name="leftRail" />
72
72
  <slot name="main" />
73
73
  <slot name="rightRail" />
74
74
  </div>
75
- <footer v-if="hasFooter" class="apos-modal__footer">
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
- import AposFocusMixin from 'Modules/@apostrophecms/modal/mixins/AposFocusMixin';
98
-
99
- export default {
100
- name: 'AposModal',
101
- mixins: [
102
- AposFocusMixin
103
- ],
104
- props: {
105
- modal: {
106
- type: Object,
107
- required: true
108
- },
109
- modalTitle: {
110
- type: [ String, Object ],
111
- default: ''
112
- }
113
- },
114
- emits: [ 'inactive', 'esc', 'show-modal', 'no-modal', 'ready' ],
115
- data() {
116
- return {
117
- // For aria purposes
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
- methods: {
216
- onKeydown (e) {
217
- const hasPressedEsc = e.keyCode === 27;
218
- if (hasPressedEsc) {
219
- this.close(e);
220
- }
221
- },
222
- onEnter () {
223
- this.$emit('show-modal');
224
- this.bindEventListeners();
225
- apos.modal.stack = apos.modal.stack || [];
226
- apos.modal.stack.push(this);
227
- this.$nextTick(() => {
228
- this.$emit('ready');
229
- });
230
- },
231
- onLeave () {
232
- this.removeEventListeners();
233
- this.$emit('no-modal');
234
- // pop doesn't quite suffice because of race conditions when
235
- // closing one and opening another
236
- apos.modal.stack = apos.modal.stack.filter(modal => modal !== this);
237
- this.focusLastModalFocusedElement();
238
- },
239
- bindEventListeners () {
240
- window.addEventListener('keydown', this.onKeydown);
241
- },
242
- removeEventListeners () {
243
- window.removeEventListener('keydown', this.onKeydown);
244
- },
245
- close (e) {
246
- if (apos.modal.stack[apos.modal.stack.length - 1] !== this) {
247
- return;
248
- }
249
- e.stopPropagation();
250
- this.$emit('esc');
251
- },
252
- trapFocus () {
253
- const elementSelectors = [
254
- '[tabindex]',
255
- '[href]',
256
- 'input',
257
- 'select',
258
- 'textarea',
259
- 'button'
260
- ];
261
-
262
- const selector = elementSelectors
263
- .map(addExcludingAttributes)
264
- .join(', ');
265
-
266
- this.elementsToFocus = [ ...this.$refs.modalEl.querySelectorAll(selector) ]
267
- .filter(this.isElementVisible);
268
-
269
- this.focusElement(this.focusedElement, this.elementsToFocus[0]);
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
- focuslastmodalfocusedelement,
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 focuslastmodalfocusedelement() {
59
+ function focusLastModalFocusedElement() {
60
60
  const lastModal = apos.modal.stack.at(-1);
61
61
 
62
62
  if (!lastModal) {
@@ -271,6 +271,8 @@ module.exports = {
271
271
  throw self.apos.error('required');
272
272
  }
273
273
 
274
+ req.body = req.body || {};
275
+
274
276
  const notification = {
275
277
  _id: self.apos.util.generateId(),
276
278
  createdAt: new Date(),
@@ -405,11 +405,11 @@ export default {
405
405
  },
406
406
  shortcutNew(event) {
407
407
  const interesting = event.keyCode === 78; // N(ew)
408
- const topModal = apos.modal.stack[apos.modal.stack.length - 1] ? apos.modal.stack[apos.modal.stack.length - 1].id : null;
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 === topModal
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
- styles: {
145
+ nodes: {
146
146
  component: 'AposTiptapStyles',
147
- label: 'apostrophe:richTextStyles'
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', 'span', 'u', 'anchor',
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
- allowedClasses[tag] = allowedClasses[tag] || {};
613
- for (const c of classes) {
614
- allowedClasses[tag][c] = true;
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,
@@ -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 (TODO: find them all)
217
- const activeOptions = klona(this.options);
218
-
219
- activeOptions.styles = this.enhanceStyles(
220
- activeOptions.styles?.length
221
- ? activeOptions.styles
222
- : this.defaultOptions.styles
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 = this.editorOptions.styles.find(style => style.def);
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
- return styles;
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
- styles: this.editorOptions.styles.map(this.localizeStyle),
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: 4px;
811
+ gap: 6px;
783
812
  }
784
813
 
785
814
  .apos-rich-text-editor__editor :deep(.ProseMirror) {