apostrophe 3.48.0 → 3.49.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 (45) hide show
  1. package/CHANGELOG.md +43 -2
  2. package/index.js +20 -2
  3. package/lib/locales.js +1 -1
  4. package/lib/moog-require.js +3 -0
  5. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextBar.vue +12 -2
  6. package/modules/@apostrophecms/area/ui/apos/components/AposAreaEditor.vue +2 -0
  7. package/modules/@apostrophecms/area/ui/apos/components/AposAreaWidget.vue +7 -24
  8. package/modules/@apostrophecms/asset/index.js +27 -2
  9. package/modules/@apostrophecms/asset/lib/webpack/apos/webpack.config.js +23 -2
  10. package/modules/@apostrophecms/asset/lib/webpack/src/webpack.config.js +26 -2
  11. package/modules/@apostrophecms/doc/index.js +149 -0
  12. package/modules/@apostrophecms/doc-type/index.js +9 -1
  13. package/modules/@apostrophecms/global/index.js +4 -15
  14. package/modules/@apostrophecms/i18n/i18n/en.json +3 -2
  15. package/modules/@apostrophecms/i18n/index.js +76 -61
  16. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerDisplay.vue +14 -1
  17. package/modules/@apostrophecms/login/ui/apos/components/AposForgotPasswordForm.vue +3 -60
  18. package/modules/@apostrophecms/login/ui/apos/components/AposLoginForm.vue +3 -231
  19. package/modules/@apostrophecms/login/ui/apos/components/AposResetPasswordForm.vue +3 -96
  20. package/modules/@apostrophecms/login/ui/apos/components/TheAposLogin.vue +2 -99
  21. package/modules/@apostrophecms/login/ui/apos/logic/AposForgotPasswordForm.js +68 -0
  22. package/modules/@apostrophecms/login/ui/apos/logic/AposLoginForm.js +239 -0
  23. package/modules/@apostrophecms/login/ui/apos/logic/AposResetPasswordForm.js +105 -0
  24. package/modules/@apostrophecms/login/ui/apos/logic/TheAposLogin.js +107 -0
  25. package/modules/@apostrophecms/modal/ui/apos/components/AposDocsManagerToolbar.vue +9 -3
  26. package/modules/@apostrophecms/modal/ui/apos/components/AposModalToolbar.vue +1 -0
  27. package/modules/@apostrophecms/page/index.js +63 -1
  28. package/modules/@apostrophecms/piece-type/index.js +57 -9
  29. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposImageControlDialog.vue +11 -8
  30. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposRichTextWidgetEditor.vue +226 -72
  31. package/modules/@apostrophecms/schema/index.js +0 -1
  32. package/modules/@apostrophecms/schema/lib/addFieldTypes.js +35 -7
  33. package/modules/@apostrophecms/schema/ui/apos/components/AposInputSlug.vue +21 -1
  34. package/modules/@apostrophecms/schema/ui/apos/components/AposInputString.vue +12 -7
  35. package/modules/@apostrophecms/schema/ui/apos/components/AposSchema.vue +1 -0
  36. package/modules/@apostrophecms/ui/ui/apos/components/AposCombo.vue +178 -20
  37. package/modules/@apostrophecms/ui/ui/apos/components/AposFilterMenu.vue +1 -1
  38. package/modules/@apostrophecms/ui/ui/apos/components/AposPager.vue +4 -6
  39. package/modules/@apostrophecms/ui/ui/apos/scss/mixins/_theme_mixins.scss +1 -0
  40. package/modules/@apostrophecms/util/index.js +5 -6
  41. package/modules/@apostrophecms/util/ui/src/http.js +6 -3
  42. package/package.json +20 -3
  43. package/test/change-doc-ids.js +134 -0
  44. package/test/i18n.js +310 -0
  45. package/test/static-i18n.js +0 -105
@@ -100,6 +100,7 @@ module.exports = {
100
100
  self.enableBrowserData();
101
101
  self.addLegacyMigrations();
102
102
  self.addMisreplicatedParkedPagesMigration();
103
+ self.addDuplicateParkedPagesMigration();
103
104
  await self.createIndexes();
104
105
  },
105
106
  restApiRoutes(self) {
@@ -1959,7 +1960,10 @@ database.`);
1959
1960
  delete _item._children;
1960
1961
  if (!parent) {
1961
1962
  // Parking the home page for the first time
1962
- _item.aposDocId = self.apos.util.generateId();
1963
+ _item.aposDocId = await self.apos.doc.bestAposDocId({
1964
+ level: 0,
1965
+ slug: '/'
1966
+ });
1963
1967
  _item.path = _item.aposDocId;
1964
1968
  _item.lastPublishedAt = new Date();
1965
1969
  return self.apos.doc.insert(req, _item);
@@ -2365,6 +2369,64 @@ database.`);
2365
2369
  }
2366
2370
  }
2367
2371
  });
2372
+ },
2373
+ addDuplicateParkedPagesMigration() {
2374
+ self.apos.migration.add('duplicate-parked-pages', async () => {
2375
+ let parkedPages = await self.apos.doc.db.find({
2376
+ parkedId: {
2377
+ $ne: null
2378
+ }
2379
+ }).toArray();
2380
+ const parkedIds = [ ...new Set(parkedPages.map(page => page.parkedId)) ];
2381
+ const names = Object.keys(self.apos.i18n.locales);
2382
+ const locales = [
2383
+ ...names.map(locale => `${locale}:draft`),
2384
+ ...names.map(locale => `${locale}:published`),
2385
+ ...names.map(locale => `${locale}:previous`)
2386
+ ];
2387
+ let changes = 0;
2388
+ const winners = new Map();
2389
+ for (const locale of locales) {
2390
+ for (const parkedId of parkedIds) {
2391
+ let matches = parkedPages.filter(page =>
2392
+ (page.parkedId === parkedId) &&
2393
+ (page.aposLocale === locale)
2394
+ );
2395
+ if (matches.length > 0) {
2396
+ if (!winners.has(parkedId)) {
2397
+ winners.set(parkedId, matches[0].aposDocId);
2398
+ }
2399
+ }
2400
+ if (matches.length > 1) {
2401
+ matches = matches.sort((a, b) => a.createdAt - b.createdAt);
2402
+ const ids = matches.slice(1).map(page => page._id);
2403
+ await self.apos.doc.db.removeMany({
2404
+ _id: {
2405
+ $in: ids
2406
+ }
2407
+ });
2408
+ parkedPages = parkedPages.filter(page => !ids.includes(page._id));
2409
+ changes++;
2410
+ }
2411
+ }
2412
+ }
2413
+ const idChanges = [];
2414
+ for (const parkedId of parkedIds) {
2415
+ const aposDocId = winners.get(parkedId);
2416
+ const matches = parkedPages.filter(page => page.parkedId === parkedId);
2417
+ for (const match of matches) {
2418
+ if (match.aposDocId !== aposDocId) {
2419
+ idChanges.push([ match._id, match._id.replace(match.aposDocId, aposDocId) ]);
2420
+ }
2421
+ }
2422
+ }
2423
+ if (idChanges.length) {
2424
+ // Also calls self.apos.attachment.recomputeAllDocReferences
2425
+ await self.apos.doc.changeDocIds(idChanges);
2426
+ } else if (changes > 0) {
2427
+ await self.apos.attachment.recomputeAllDocReferences();
2428
+ }
2429
+ });
2368
2430
  }
2369
2431
  };
2370
2432
  },
@@ -42,15 +42,23 @@ module.exports = {
42
42
  // visibility: 1
43
43
  // }
44
44
  },
45
- fields: {
46
- add: {
47
- slug: {
48
- type: 'slug',
49
- label: 'apostrophe:slug',
50
- following: [ 'title', 'archived' ],
51
- required: true
52
- }
53
- }
45
+ fields(self) {
46
+ return {
47
+ add: {
48
+ slug: {
49
+ type: 'slug',
50
+ label: 'apostrophe:slug',
51
+ following: [ 'title', 'archived' ],
52
+ required: true
53
+ }
54
+ },
55
+ remove: self.options.singletonAuto ? [
56
+ 'title',
57
+ 'slug',
58
+ 'archived',
59
+ 'visibility'
60
+ ] : []
61
+ };
54
62
  },
55
63
  columns(self) {
56
64
  return {
@@ -1099,6 +1107,31 @@ module.exports = {
1099
1107
  });
1100
1108
 
1101
1109
  return projection;
1110
+ },
1111
+ async insertIfMissing() {
1112
+ if (!self.options.singletonAuto) {
1113
+ return;
1114
+ }
1115
+ // Insert at startup
1116
+ const req = self.apos.task.getReq();
1117
+ const criteria = {
1118
+ type: self.name
1119
+ };
1120
+ if (self.options.localized) {
1121
+ criteria.aposLocale = {
1122
+ $in: Object.keys(self.apos.i18n.locales).map(locale => [ `${locale}:published`, `${locale}:draft` ]).flat()
1123
+ };
1124
+ }
1125
+ const existing = await self.apos.doc.db.findOne(criteria, { _id: 1 });
1126
+ if (!existing) {
1127
+ const _new = {
1128
+ ...self.newInstance(),
1129
+ aposDocId: await self.apos.doc.bestAposDocId({
1130
+ type: self.name
1131
+ })
1132
+ };
1133
+ await self.insert(req, _new);
1134
+ }
1102
1135
  }
1103
1136
  };
1104
1137
  },
@@ -1133,6 +1166,21 @@ module.exports = {
1133
1166
  },
1134
1167
  find(_super, req, criteria, projection) {
1135
1168
  return _super(req, criteria, projection).defaultSort(self.options.sort || { updatedAt: -1 });
1169
+ },
1170
+ newInstance(_super) {
1171
+ if (!self.options.singletonAuto) {
1172
+ return _super();
1173
+ }
1174
+ const slug = self.apos.util.slugify(self.options.singletonAuto?.slug || self.name);
1175
+ return {
1176
+ ..._super(),
1177
+ // These fields are removed from the editable schema of singletons,
1178
+ // but we assign them directly for broader compatibility
1179
+ slug,
1180
+ title: slug,
1181
+ archived: false,
1182
+ visibility: 'public'
1183
+ };
1136
1184
  }
1137
1185
  };
1138
1186
  },
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <div
3
3
  v-if="active"
4
- v-click-outside-element="close"
4
+ v-click-outside-element="cancel"
5
5
  class="apos-popover apos-image-control__dialog"
6
6
  x-placement="bottom"
7
7
  :class="{
@@ -25,7 +25,7 @@
25
25
  <footer class="apos-image-control__footer">
26
26
  <AposButton
27
27
  type="default" label="apostrophe:cancel"
28
- @click="close"
28
+ @click="cancel"
29
29
  :modifiers="formModifiers"
30
30
  />
31
31
  <AposButton
@@ -54,7 +54,7 @@ export default {
54
54
  required: true
55
55
  }
56
56
  },
57
- emits: [ 'before-commands', 'close' ],
57
+ emits: [ 'before-commands', 'done', 'cancel' ],
58
58
  data() {
59
59
  return {
60
60
  generation: 1,
@@ -113,8 +113,11 @@ export default {
113
113
  }
114
114
  },
115
115
  methods: {
116
- close() {
117
- this.$emit('close');
116
+ cancel() {
117
+ this.$emit('cancel');
118
+ },
119
+ done() {
120
+ this.$emit('done');
118
121
  },
119
122
  save() {
120
123
  this.triggerValidation = true;
@@ -132,17 +135,17 @@ export default {
132
135
  style: this.docFields.data.style,
133
136
  alt: this.docFields.data.alt
134
137
  });
135
- this.close();
138
+ this.done();
136
139
  });
137
140
  },
138
141
  keyboardHandler(e) {
139
142
  if (e.keyCode === 27) {
140
- this.close();
143
+ this.cancel();
141
144
  }
142
145
  if (e.keyCode === 13) {
143
146
  if (this.docFields.data.href || e.metaKey) {
144
147
  this.save();
145
- this.close();
148
+ this.done();
146
149
  }
147
150
  e.preventDefault();
148
151
  }
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div>
2
+ <div :aria-controls="`insert-menu-${value._id}`" @keydown="handleUIKeydown">
3
3
  <bubble-menu
4
4
  class="bubble-menu"
5
5
  :tippy-options="{ maxWidth: 'none', duration: 100, zIndex: 2000 }"
@@ -26,44 +26,64 @@
26
26
  </AposContextMenuDialog>
27
27
  </bubble-menu>
28
28
  <floating-menu
29
- class="apos-rich-text-insert-menu" :should-show="showFloatingMenu"
30
- :editor="editor"
31
- :tippy-options="{ duration: 100, zIndex: 2000 }"
32
29
  v-if="editor"
30
+ class="apos-rich-text-insert-menu"
31
+ :tippy-options="{ duration: 100, zIndex: 2000, placement: 'bottom-start' }"
32
+ :should-show="showFloatingMenu"
33
+ :editor="editor"
34
+ role="listbox"
35
+ tabindex="0"
36
+ ref="insertMenu"
37
+ :id="`insert-menu-${value._id}`"
38
+ :key="insertMenuKey"
33
39
  >
34
40
  <div class="apos-rich-text-insert-menu-heading">
35
41
  {{ $t('apostrophe:richTextInsertMenuHeading') }}
36
42
  </div>
37
43
  <div
38
- v-for="(item, index) in insert"
39
- :key="`${item}-${index}`"
40
- class="apos-rich-text-insert-menu-item"
44
+ class="apos-rich-text-insert-menu-wrapper"
45
+ @keydown.prevent.arrow-up="focusInsertMenuItem(true)"
46
+ @keydown.prevent.arrow-down="focusInsertMenuItem()"
47
+ @keydown="closeInsertMenu"
41
48
  >
42
- <div class="apos-rich-text-insert-menu-icon">
43
- <AposIndicator
44
- :icon="insertMenu[item].icon"
45
- :icon-size="35"
46
- class="apos-button__icon"
47
- fill-color="currentColor"
48
- @click="activateInsertMenuItem(item, insertMenu[item])"
49
- />
50
- <component
51
- v-if="item === activeInsertMenuComponent?.name"
52
- :is="activeInsertMenuComponent.component"
53
- :active="true"
54
- :editor="editor"
55
- :options="editorOptions"
56
- @before-commands="removeSlash"
57
- @close="closeInsertMenuItem"
58
- @click.stop="$event => null"
59
- />
60
- </div>
61
- <div
62
- class="apos-rich-text-insert-menu-label"
49
+ <button
50
+ v-for="(item, index) in insert"
51
+ :key="`${item}-${index}`"
52
+ class="apos-rich-text-insert-menu-item"
53
+ role="option"
54
+ data-insert-menu-item
63
55
  @click="activateInsertMenuItem(item, insertMenu[item])"
64
56
  >
65
- <h4>{{ $t(insertMenu[item].label) }}</h4>
66
- <p>{{ $t(insertMenu[item].description) }}</p>
57
+ <div class="apos-rich-text-insert-menu-icon">
58
+ <AposIndicator
59
+ :icon="insertMenu[item].icon"
60
+ :icon-size="24"
61
+ class="apos-button__icon"
62
+ fill-color="currentColor"
63
+ />
64
+ </div>
65
+ <div class="apos-rich-text-insert-menu-label">
66
+ <h4>{{ $t(insertMenu[item].label) }}</h4>
67
+ <p>{{ $t(insertMenu[item].description) }}</p>
68
+ </div>
69
+ </button>
70
+ <div class="apos-rich-text-insert-menu-components">
71
+ <div
72
+ v-for="(item, index) in insert"
73
+ :key="`${item}-${index}-component`"
74
+ >
75
+ <component
76
+ v-if="item === activeInsertMenuComponent?.name"
77
+ :is="activeInsertMenuComponent.component"
78
+ :active="true"
79
+ :editor="editor"
80
+ :options="editorOptions"
81
+ @before-commands="removeSlash"
82
+ @cancel="cancelInsertMenuItem"
83
+ @done="closeInsertMenuItem"
84
+ @close="closeInsertMenuItem"
85
+ />
86
+ </div>
67
87
  </div>
68
88
  </div>
69
89
  </floating-menu>
@@ -86,7 +106,23 @@ import {
86
106
  BubbleMenu,
87
107
  FloatingMenu
88
108
  } from '@tiptap/vue-2';
89
- import StarterKit from '@tiptap/starter-kit';
109
+ // Starter Kit extensions
110
+ import BlockQuote from '@tiptap/extension-blockquote';
111
+ import Bold from '@tiptap/extension-bold';
112
+ import BulletList from '@tiptap/extension-bullet-list';
113
+ import Code from '@tiptap/extension-code';
114
+ import CodeBlock from '@tiptap/extension-code-block';
115
+ import Dropcursor from '@tiptap/extension-dropcursor';
116
+ import Gapcursor from '@tiptap/extension-gapcursor';
117
+ import HardBreak from '@tiptap/extension-hard-break';
118
+ import History from '@tiptap/extension-history';
119
+ import HorizontalRule from '@tiptap/extension-horizontal-rule';
120
+ import Italic from '@tiptap/extension-italic';
121
+ import OrderedList from '@tiptap/extension-ordered-list';
122
+ import Paragraph from '@tiptap/extension-paragraph';
123
+ import Strike from '@tiptap/extension-strike';
124
+ import Text from '@tiptap/extension-text';
125
+ // End starter kit extensions
90
126
  import TextAlign from '@tiptap/extension-text-align';
91
127
  import Highlight from '@tiptap/extension-highlight';
92
128
  import Underline from '@tiptap/extension-underline';
@@ -144,8 +180,11 @@ export default {
144
180
  },
145
181
  pending: null,
146
182
  isFocused: null,
183
+ isShowingInsert: false,
147
184
  showPlaceholder: null,
148
- activeInsertMenuComponent: null
185
+ activeInsertMenuComponent: null,
186
+ suppressInsertMenu: false,
187
+ insertMenuKey: null
149
188
  };
150
189
  },
151
190
  computed: {
@@ -246,17 +285,33 @@ export default {
246
285
  this.emitWidgetUpdate();
247
286
  }
248
287
  }
288
+ },
289
+ isShowingInsert(newVal) {
290
+ if (newVal) {
291
+ this.focusInsertMenuItem(false, 0);
292
+ }
249
293
  }
250
294
  },
251
295
  mounted() {
296
+ this.insertMenuKey = this.generateKey();
252
297
  // Cleanly namespace it so we don't conflict with other uses and instances
253
298
  const CustomPlaceholder = Placeholder.extend();
254
299
  const extensions = [
255
- StarterKit.configure({
256
- document: false,
257
- heading: false,
258
- listItem: false
259
- }),
300
+ BlockQuote,
301
+ Bold,
302
+ BulletList,
303
+ Code,
304
+ CodeBlock,
305
+ Dropcursor,
306
+ Gapcursor,
307
+ HardBreak,
308
+ History,
309
+ HorizontalRule,
310
+ Italic,
311
+ OrderedList,
312
+ Paragraph,
313
+ Strike,
314
+ Text,
260
315
  TextAlign.configure({
261
316
  types: [ 'heading', 'paragraph', 'defaultNode' ]
262
317
  }),
@@ -273,7 +328,7 @@ export default {
273
328
  const text = this.$t(this.placeholderText);
274
329
  return text;
275
330
  },
276
- emptyNodeClass: this.insert.length ? 'apos-is-empty' : 'apos-is-empty-without-insert'
331
+ emptyNodeClass: 'apos-is-empty'
277
332
  }),
278
333
  FloatingMenu
279
334
  ]
@@ -316,6 +371,22 @@ export default {
316
371
  apos.bus.$off('apos-refreshing', this.onAposRefreshing);
317
372
  },
318
373
  methods: {
374
+ generateKey() {
375
+ return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
376
+ },
377
+ handleUIKeydown(e) {
378
+ if (e.key === 'Escape') {
379
+ this.doSuppressInsertMenu();
380
+ } else {
381
+ this.suppressInsertMenu = false;
382
+ }
383
+ },
384
+ doSuppressInsertMenu() {
385
+ this.suppressInsertMenu = true;
386
+ this.activeInsertMenuComponent = null;
387
+ this.insertMenuKey = this.generateKey();
388
+ this.editor.commands.focus();
389
+ },
319
390
  onAposRefreshing(refreshOptions) {
320
391
  if (this.activeInsertMenuComponent) {
321
392
  refreshOptions.refresh = false;
@@ -335,6 +406,7 @@ export default {
335
406
  // least once per second if the user is actively typing
336
407
  return;
337
408
  }
409
+
338
410
  this.pending = setTimeout(() => {
339
411
  this.emitWidgetUpdate();
340
412
  }, 1000);
@@ -472,24 +544,34 @@ export default {
472
544
  types: this.tiptapTypes
473
545
  }));
474
546
  },
475
- showFloatingMenu({ state }) {
476
- if (!this.insertMenu || !this.insert.length) {
547
+ showFloatingMenu({
548
+ state, oldState
549
+ }) {
550
+ const hasChanges = JSON.stringify(state?.doc.toJSON()) !== JSON.stringify(oldState?.doc.toJSON());
551
+ const { $to } = state.selection;
552
+
553
+ if (
554
+ !this.insertMenu ||
555
+ !this.insert.length ||
556
+ !hasChanges ||
557
+ ($to.nodeAfter && $to.nodeAfter.text) ||
558
+ this.suppressInsertMenu
559
+ ) {
560
+ this.isShowingInsert = false;
477
561
  return false;
478
562
  }
479
- const { $from, $to } = state.selection;
563
+
480
564
  if (state.selection.empty) {
481
565
  if ($to.nodeBefore && $to.nodeBefore.text) {
482
566
  const text = $to.nodeBefore.text;
483
- // Only show when the user has just entered a '/' character or
484
- // an insert menu component is active
485
- if (text === '/') {
567
+ if (text.slice(-1) === '/') {
568
+ this.isShowingInsert = true;
486
569
  return true;
487
570
  }
488
571
  }
489
- return false;
490
- } else if (state.doc.textBetween($from, $to, ' ') === '/') {
491
- return true;
492
572
  }
573
+
574
+ this.isShowingInsert = false;
493
575
  return false;
494
576
  },
495
577
  activateInsertMenuItem(name, info) {
@@ -523,6 +605,38 @@ export default {
523
605
  closeInsertMenuItem() {
524
606
  this.removeSlash();
525
607
  this.activeInsertMenuComponent = null;
608
+ },
609
+ cancelInsertMenuItem() {
610
+ this.doSuppressInsertMenu();
611
+ },
612
+ closeInsertMenu(e) {
613
+ if (
614
+ [ 'ArrowUp', 'ArrowDown', 'Enter', ' ' ].includes(e.key) ||
615
+ this.activeInsertMenuComponent
616
+ ) {
617
+ return;
618
+ }
619
+ this.editor.commands.focus();
620
+ this.activeInsertMenuComponent = null;
621
+ // Only insert character keys
622
+ if (e.key.length === 1) {
623
+ this.editor.commands.insertContent(e.key);
624
+ }
625
+ },
626
+ focusInsertMenuItem(prev = false, index) {
627
+ if (this.activeInsertMenuComponent) {
628
+ return;
629
+ }
630
+ const buttons = Array.from(this.$refs.insertMenu.$el.querySelectorAll('[data-insert-menu-item]'));
631
+ const currentIndex = buttons.findIndex(el => el === document.activeElement);
632
+ let targetIndex = prev ? currentIndex - 1 : currentIndex + 1;
633
+ if (targetIndex >= buttons.length) {
634
+ targetIndex = 0;
635
+ }
636
+ if (targetIndex < 0) {
637
+ targetIndex = buttons.length - 1;
638
+ }
639
+ buttons[index || targetIndex]?.focus();
526
640
  }
527
641
  }
528
642
  };
@@ -569,27 +683,41 @@ function traverseNextNode(node) {
569
683
  background-color: var(--a-base-9);
570
684
  }
571
685
 
686
+ .apos-rich-text-editor__editor ::v-deep .ProseMirror {
687
+ @include apos-transition();
688
+ }
689
+
572
690
  .apos-rich-text-editor__editor ::v-deep .ProseMirror:focus {
573
691
  outline: none;
574
692
  }
575
693
 
576
- .apos-rich-text-editor__editor ::v-deep .ProseMirror:focus p.apos-is-empty::before,
577
- .apos-rich-text-editor__editor.apos-is-visually-empty ::v-deep .ProseMirror:focus p:first-of-type::before {
694
+ .apos-rich-text-editor__editor ::v-deep .ProseMirror {
695
+ padding: 10px 0;
696
+ }
697
+
698
+ .apos-rich-text-editor__editor ::v-deep .ProseMirror:focus p.apos-is-empty::after {
699
+ display: block;
700
+ margin: 5px 0 10px;
701
+ color: var(--a-primary-transparent-50);
702
+ font-size: var(--a-type-smaller);
703
+ text-transform: uppercase;
704
+ letter-spacing: 0.5px;
705
+ font-weight: 600;
706
+ border-top: 1px solid var(--a-primary-transparent-50);
707
+ padding-top: 5px;
578
708
  content: attr(data-placeholder);
579
- float: left;
580
709
  pointer-events: none;
581
- height: 0;
582
- color: var(--a-base-4);
583
710
  }
584
711
 
585
712
  .apos-rich-text-editor__editor {
586
713
  @include apos-transition();
587
714
  position: relative;
588
715
  border-radius: var(--a-border-radius);
589
- box-shadow: 0 0 0 1px transparent;
716
+ background-color: transparent;
590
717
  }
591
718
  .apos-rich-text-editor__editor.apos-is-visually-empty {
592
- box-shadow: 0 0 0 1px var(--a-primary-transparent-50);
719
+ background-color: var(--a-primary-transparent-10);
720
+ min-height: 50px;
593
721
  }
594
722
  .apos-rich-text-editor__editor_after {
595
723
  @include type-small;
@@ -602,9 +730,7 @@ function traverseNextNode(node) {
602
730
  width: 200px;
603
731
  height: 10px;
604
732
  margin: auto;
605
- margin-top: 7.5px;
606
- margin-bottom: 7.5px;
607
- color: var(--a-base-5);
733
+ color: var(--a-primary-transparent-50);
608
734
  opacity: 0;
609
735
  visibility: hidden;
610
736
  pointer-events: none;
@@ -655,12 +781,9 @@ function traverseNextNode(node) {
655
781
  }
656
782
 
657
783
  .apos-rich-text-insert-menu {
658
- display: flex;
659
- flex-direction: column;
660
784
  cursor: pointer;
661
785
  user-select: none;
662
- gap: 16px;
663
- padding: 16px;
786
+ min-width: 350px;
664
787
  border-radius: var(--a-border-radius);
665
788
  box-shadow: var(--a-box-shadow);
666
789
  background-color: var(--a-background-primary);
@@ -670,44 +793,75 @@ function traverseNextNode(node) {
670
793
  font-size: var(--a-type-base);
671
794
  }
672
795
 
796
+ .apos-rich-text-insert-menu-wrapper {
797
+ display: flex;
798
+ flex-direction: column;
799
+ }
800
+
673
801
  .apos-rich-text-insert-menu-item {
802
+ all: unset;
674
803
  display: flex;
675
804
  flex-direction: row;
676
- gap: 16px;
805
+ align-items: center;
806
+ gap: 12px;
807
+ padding: 14px 16px;
808
+ border-bottom: 1px solid var(--a-base-9);
809
+ @include apos-transition();
810
+ &:last-of-type {
811
+ border-bottom: none;
812
+ }
677
813
  &:hover {
678
- color: var(--a-text-primary);
814
+ background-color: var(--a-primary-transparent-10);
815
+ }
816
+ &:active, &:focus {
817
+ background-color: var(--a-primary);
818
+ color: var(--a-white);
679
819
  }
680
820
  }
681
821
 
682
822
  .apos-rich-text-insert-menu-label {
683
823
  display: flex;
684
824
  flex-direction: column;
825
+ gap: 5px;
685
826
  h4, p {
686
- margin: 4px;
827
+ margin: 0;
687
828
  font-family: var(--a-family-default);
688
- font-size: var(--a-type-base);
689
829
  }
690
830
  h4 {
691
- font-weight: bold;
831
+ font-weight: 500;
832
+ font-size: var(--a-type-large);
833
+ }
834
+ p {
835
+ font-size: var(--a-type-label);
692
836
  }
693
837
  }
694
838
  .apos-rich-text-insert-menu-icon {
695
- // Positions the popover meaningfully
696
839
  position: relative;
840
+ display: flex;
841
+ width: 40px;
842
+ height: 40px;
843
+ align-items: center;
844
+ justify-content: center;
845
+ border: 1px solid var(--a-base-8);
846
+ color: var(--a-text-primary);
847
+ background-color: var(--a-white);
848
+ border-radius: var(--a-border-radius);
697
849
  }
698
850
 
699
851
  .apos-rich-text-insert-menu-heading {
700
- color: var(--a-base-5);
852
+ padding: 12px 16px;
853
+ background-color: var(--a-base-9);
854
+ color: var(--a-base-2);
855
+ font-weight: 500;
856
+ border-bottom: 1px solid var(--a-base-7);
857
+ font-size: var(--a-type-label);
858
+ letter-spacing: 0.25px;
701
859
  }
702
860
 
703
861
  ::v-deep .ProseMirror {
704
862
  > * + * {
705
863
  margin-top: 0.75em;
706
864
  }
707
-
708
- > :last-child {
709
- margin-bottom: 1.75em;
710
- }
711
865
  }
712
866
 
713
867
  ::v-deep .ProseMirror-gapcursor {
@@ -1615,7 +1615,6 @@ module.exports = {
1615
1615
  throw self.apos.error('invalid', error.message);
1616
1616
  }
1617
1617
  }
1618
-
1619
1618
  };
1620
1619
  },
1621
1620
  apiRoutes(self) {