apostrophe 3.5.0 → 3.8.1

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 (88) hide show
  1. package/.eslintrc +4 -0
  2. package/.scratch.md +2 -0
  3. package/CHANGELOG.md +96 -3
  4. package/README.md +1 -1
  5. package/index.js +108 -3
  6. package/lib/moog-require.js +23 -0
  7. package/lib/moog.js +1 -0
  8. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextBar.vue +30 -14
  9. package/modules/@apostrophecms/area/lib/custom-tags/area.js +1 -1
  10. package/modules/@apostrophecms/area/lib/custom-tags/widget.js +1 -1
  11. package/modules/@apostrophecms/area/ui/apos/components/AposAreaWidget.vue +2 -2
  12. package/modules/@apostrophecms/asset/index.js +77 -13
  13. package/modules/@apostrophecms/attachment/index.js +1 -0
  14. package/modules/@apostrophecms/db/index.js +5 -6
  15. package/modules/@apostrophecms/doc/index.js +2 -0
  16. package/modules/@apostrophecms/doc-type/index.js +39 -16
  17. package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocContextMenu.vue +13 -1
  18. package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocEditor.vue +3 -0
  19. package/modules/@apostrophecms/i18n/i18n/en.json +19 -6
  20. package/modules/@apostrophecms/i18n/i18n/es.json +382 -0
  21. package/modules/@apostrophecms/i18n/i18n/pt-BR.json +379 -0
  22. package/modules/@apostrophecms/i18n/i18n/sk.json +380 -0
  23. package/modules/@apostrophecms/i18n/index.js +10 -1
  24. package/modules/@apostrophecms/image/index.js +2 -1
  25. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManager.vue +6 -3
  26. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerEditor.vue +2 -1
  27. package/modules/@apostrophecms/image-widget/index.js +2 -1
  28. package/modules/@apostrophecms/image-widget/views/widget.html +12 -2
  29. package/modules/@apostrophecms/job/index.js +164 -212
  30. package/modules/@apostrophecms/login/index.js +1 -16
  31. package/modules/@apostrophecms/login/ui/apos/components/TheAposLogin.vue +5 -0
  32. package/modules/@apostrophecms/migration/index.js +1 -1
  33. package/modules/@apostrophecms/modal/ui/apos/components/AposDocsManagerToolbar.vue +151 -61
  34. package/modules/@apostrophecms/modal/ui/apos/components/AposModal.vue +6 -2
  35. package/modules/@apostrophecms/modal/ui/apos/components/AposModalConfirm.vue +8 -6
  36. package/modules/@apostrophecms/modal/ui/apos/mixins/AposDocsManagerMixin.js +12 -15
  37. package/modules/@apostrophecms/modal/ui/apos/mixins/AposEditorMixin.js +6 -0
  38. package/modules/@apostrophecms/notification/index.js +116 -8
  39. package/modules/@apostrophecms/notification/ui/apos/components/AposNotification.vue +89 -11
  40. package/modules/@apostrophecms/notification/ui/apos/components/TheAposNotifications.vue +1 -1
  41. package/modules/@apostrophecms/page/index.js +37 -30
  42. package/modules/@apostrophecms/permission/index.js +1 -1
  43. package/modules/@apostrophecms/permission/ui/apos/components/AposInputRole.vue +4 -2
  44. package/modules/@apostrophecms/piece-type/index.js +178 -61
  45. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManager.vue +179 -47
  46. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManagerDisplay.vue +1 -3
  47. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManagerSelectBox.vue +138 -0
  48. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposRichTextWidgetEditor.vue +35 -6
  49. package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Classes.js +1 -3
  50. package/modules/@apostrophecms/schema/index.js +97 -20
  51. package/modules/@apostrophecms/schema/ui/apos/components/AposArrayEditor.vue +1 -0
  52. package/modules/@apostrophecms/schema/ui/apos/components/AposInputArray.vue +4 -1
  53. package/modules/@apostrophecms/schema/ui/apos/components/AposInputAttachment.vue +11 -160
  54. package/modules/@apostrophecms/schema/ui/apos/components/AposInputRelationship.vue +24 -2
  55. package/modules/@apostrophecms/schema/ui/apos/components/AposInputSelect.vue +24 -6
  56. package/modules/@apostrophecms/schema/ui/apos/components/AposInputSlug.vue +0 -4
  57. package/modules/@apostrophecms/schema/ui/apos/components/AposInputString.vue +0 -7
  58. package/modules/@apostrophecms/schema/ui/apos/components/AposSchema.vue +25 -3
  59. package/modules/@apostrophecms/schema/ui/apos/mixins/AposInputMixin.js +10 -2
  60. package/modules/@apostrophecms/task/index.js +2 -2
  61. package/modules/@apostrophecms/template/index.js +63 -36
  62. package/modules/@apostrophecms/template/lib/custom-tags/component.js +1 -1
  63. package/modules/@apostrophecms/template/lib/custom-tags/render.js +6 -2
  64. package/modules/@apostrophecms/ui/ui/apos/components/AposButton.vue +5 -2
  65. package/modules/@apostrophecms/ui/ui/apos/components/AposCellContextMenu.vue +1 -1
  66. package/modules/@apostrophecms/ui/ui/apos/components/AposFile.vue +205 -0
  67. package/modules/@apostrophecms/ui/ui/apos/lib/i18next.js +16 -2
  68. package/modules/@apostrophecms/ui/ui/apos/scss/global/_tables.scss +4 -3
  69. package/modules/@apostrophecms/util/index.js +2 -2
  70. package/modules/@apostrophecms/util/ui/src/http.js +12 -8
  71. package/modules/@apostrophecms/util/ui/src/util.js +15 -0
  72. package/modules/@apostrophecms/widget-type/index.js +1 -1
  73. package/modules/@apostrophecms/widget-type/ui/apos/components/AposWidgetEditor.vue +1 -0
  74. package/modules/@apostrophecms/widget-type/ui/apos/mixins/AposWidgetMixin.js +15 -7
  75. package/package.json +3 -3
  76. package/test/extra_node_modules/improve-global/index.js +7 -0
  77. package/test/extra_node_modules/improve-piece-type/index.js +7 -0
  78. package/test/improve-overrides.js +30 -0
  79. package/test/job.js +224 -0
  80. package/test/modules/@apostrophecms/global/index.js +8 -0
  81. package/test/modules/fragment-all/views/aux-test.html +7 -0
  82. package/test/modules/fragment-all/views/fragment.html +5 -0
  83. package/test/package.json +5 -4
  84. package/test/pieces.js +34 -0
  85. package/test/reverse-relationship.js +170 -0
  86. package/test/templates.js +7 -1
  87. package/test-lib/test.js +23 -12
  88. package/test-lib/util.js +33 -0
@@ -19,10 +19,10 @@
19
19
  </template>
20
20
  <template #primaryControls>
21
21
  <AposContextMenu
22
- v-if="moreMenu.menu.length"
23
- :button="moreMenu.button"
24
- :menu="moreMenu.menu"
25
- @item-clicked="moreMenuHandler"
22
+ v-if="utilityOperations.menu.length"
23
+ :button="utilityOperations.button"
24
+ :menu="utilityOperations.menu"
25
+ @item-clicked="utilityOperationsHandler"
26
26
  />
27
27
  <AposButton
28
28
  v-if="relationshipField"
@@ -66,19 +66,33 @@
66
66
  :selected-state="selectAllState"
67
67
  :total-pages="totalPages"
68
68
  :current-page="currentPage"
69
- :filters="moduleOptions.filters"
70
69
  :filter-choices="filterChoices"
71
70
  :filter-values="filterValues"
71
+ :filters="moduleOptions.filters"
72
72
  :labels="moduleLabels"
73
+ :displayed-items="items.length"
74
+ :is-relationship="!!relationshipField"
75
+ :checked-count="checked.length"
76
+ :batch-operations="moduleOptions.batchOperations"
73
77
  @select-click="selectAll"
74
78
  @search="search"
75
79
  @page-change="updatePage"
76
80
  @filter="filter"
81
+ @batch="handleBatchAction"
77
82
  :options="{
78
- disableUnchecked: maxReached(),
79
- hideSelectAll: !relationshipField
83
+ disableUnchecked: maxReached()
80
84
  }"
81
85
  />
86
+ <AposDocsManagerSelectBox
87
+ :selected-state="selectAllState"
88
+ :module-labels="moduleLabels"
89
+ :filter-values="filterValues"
90
+ :checked-ids="checked"
91
+ :all-pieces-selection="allPiecesSelection"
92
+ :displayed-items="items.length"
93
+ @select-all="selectAllPieces"
94
+ @set-all-pieces-selection="setAllPiecesSelection"
95
+ />
82
96
  </template>
83
97
  <template #bodyMain>
84
98
  <AposDocsManagerDisplay
@@ -90,7 +104,6 @@
90
104
  :options="{
91
105
  ...moduleOptions,
92
106
  disableUnchecked: maxReached(),
93
- hideCheckboxes: !relationshipField,
94
107
  disableUnpublished: disableUnpublished,
95
108
  manuallyPublished: manuallyPublished
96
109
  }"
@@ -136,7 +149,7 @@ export default {
136
149
  filterValues: {},
137
150
  queryExtras: {},
138
151
  holdQueries: false,
139
- moreMenu: {
152
+ utilityOperations: {
140
153
  button: {
141
154
  label: 'apostrophe:moreOperations',
142
155
  iconOnly: true,
@@ -145,7 +158,11 @@ export default {
145
158
  },
146
159
  menu: []
147
160
  },
148
- filterChoices: {}
161
+ filterChoices: {},
162
+ allPiecesSelection: {
163
+ isSelected: false,
164
+ total: 0
165
+ }
149
166
  };
150
167
  },
151
168
  computed: {
@@ -190,6 +207,16 @@ export default {
190
207
  },
191
208
  disableUnpublished() {
192
209
  return this.relationshipField && apos.modules[this.relationshipField.withType].localized;
210
+ },
211
+ selectAllChoice() {
212
+ const checkCount = this.checked.length;
213
+ const pageNotFullyChecked = this.items
214
+ .some((item) => !this.checked.includes(item._id));
215
+
216
+ return {
217
+ value: 'checked',
218
+ indeterminate: checkCount && pageNotFullyChecked
219
+ };
193
220
  }
194
221
  },
195
222
  created() {
@@ -206,14 +233,10 @@ export default {
206
233
  this.headers = this.computeHeaders();
207
234
  // Get the data. This will be more complex in actuality.
208
235
  this.modal.active = true;
209
- this.getPieces();
210
- if (this.relationshipField && this.moduleOptions.canEdit) {
211
- // Add computed singular label to context menu
212
- this.moreMenu.menu.unshift({
213
- action: 'new',
214
- label: `New ${this.moduleLabels.singular}`
215
- });
216
- }
236
+ this.setUtilityOperations();
237
+ await this.getPieces();
238
+ await this.getAllPiecesTotal();
239
+
217
240
  apos.bus.$on('content-changed', this.getPieces);
218
241
  },
219
242
  destroyed() {
@@ -221,10 +244,13 @@ export default {
221
244
  apos.bus.$off('content-changed', this.getPieces);
222
245
  },
223
246
  methods: {
224
- moreMenuHandler(action) {
247
+ utilityOperationsHandler(action) {
225
248
  if (action === 'new') {
226
249
  this.create();
250
+ return;
227
251
  }
252
+
253
+ this.handleUtilityOperation(action);
228
254
  },
229
255
  setCheckedDocs(checked) {
230
256
  this.checkedDocs = checked;
@@ -235,6 +261,29 @@ export default {
235
261
  async create() {
236
262
  await this.edit(null);
237
263
  },
264
+ async handleUtilityOperation(action) {
265
+ const operation = this.utilityOperations.menu
266
+ .find((op) => op.action === action);
267
+
268
+ if (!operation) {
269
+ return;
270
+ }
271
+
272
+ const {
273
+ modal, ...modalOptions
274
+ } = operation.modalOptions || {};
275
+
276
+ if (modal) {
277
+ await apos.modal.execute(modal, {
278
+ moduleAction: this.moduleOptions.action,
279
+ action,
280
+ labels: this.moduleLabels,
281
+ messages: operation.messages,
282
+ ...modalOptions
283
+ });
284
+ }
285
+ },
286
+
238
287
  // If pieceOrId is null, a new piece is created
239
288
  async edit(pieceOrId) {
240
289
  let piece;
@@ -268,42 +317,68 @@ export default {
268
317
  async finishSaved() {
269
318
  await this.getPieces();
270
319
  },
271
- async getPieces () {
272
- if (this.holdQueries) {
273
- return;
274
- }
275
-
276
- this.holdQueries = true;
277
-
278
- const qs = {
320
+ async request (mergeOptions) {
321
+ const options = {
279
322
  ...this.filterValues,
280
- page: this.currentPage,
281
323
  ...this.queryExtras,
282
- // Also fetch published docs as _publishedDoc subproperties
324
+ ...mergeOptions,
283
325
  withPublished: 1
284
326
  };
285
327
 
286
328
  // Avoid undefined properties.
287
- for (const prop in qs) {
288
- if (qs[prop] === undefined) {
289
- delete qs[prop];
290
- };
329
+ const qs = Object.entries(options)
330
+ .reduce((acc, [ key, val ]) => ({
331
+ ...acc,
332
+ ...val !== undefined && { [key]: val }
333
+ }), {});
334
+
335
+ return apos.http.get(this.moduleOptions.action, {
336
+ qs,
337
+ busy: true,
338
+ draft: true
339
+ });
340
+ },
341
+ async getPieces () {
342
+ if (this.holdQueries) {
343
+ return;
291
344
  }
292
345
 
293
- const getResponse = await apos.http.get(
294
- this.moduleOptions.action, {
295
- busy: true,
296
- qs,
297
- draft: true
298
- }
299
- );
346
+ this.holdQueries = true;
347
+
348
+ const {
349
+ currentPage, pages, results, choices
350
+ } = await this.request({
351
+ page: this.currentPage
352
+ });
300
353
 
301
- this.currentPage = getResponse.currentPage;
302
- this.totalPages = getResponse.pages;
303
- this.items = getResponse.results;
304
- this.filterChoices = getResponse.choices;
354
+ this.currentPage = currentPage;
355
+ this.totalPages = pages;
356
+ this.items = results;
357
+ this.filterChoices = choices;
305
358
  this.holdQueries = false;
306
359
  },
360
+ async getAllPiecesTotal () {
361
+ const { count: total } = await this.request({ count: 1 });
362
+
363
+ this.setAllPiecesSelection({
364
+ isSelected: false,
365
+ total
366
+ });
367
+ },
368
+ async selectAllPieces () {
369
+ const { results: docs } = await this.request({
370
+ project: {
371
+ _id: 1
372
+ },
373
+ attachments: false,
374
+ perPage: this.allPiecesSelection.total
375
+ });
376
+
377
+ this.setAllPiecesSelection({
378
+ isSelected: true,
379
+ docs
380
+ });
381
+ },
307
382
  updatePage(num) {
308
383
  if (num) {
309
384
  this.currentPage = num;
@@ -322,6 +397,7 @@ export default {
322
397
  this.currentPage = 1;
323
398
 
324
399
  await this.getPieces();
400
+ await this.getAllPiecesTotal();
325
401
  },
326
402
  async filter(filter, value) {
327
403
  if (this.filterValues[filter] === value) {
@@ -331,10 +407,12 @@ export default {
331
407
  this.filterValues[filter] = value;
332
408
  this.currentPage = 1;
333
409
 
334
- this.getPieces();
410
+ await this.getPieces();
411
+ await this.getAllPiecesTotal();
335
412
  this.headers = this.computeHeaders();
336
- },
337
413
 
414
+ this.setCheckedDocs([]);
415
+ },
338
416
  shortcutNew(event) {
339
417
  const interesting = (event.keyCode === 78 || event.keyCode === 67); // C(reate) or N(ew)
340
418
  const topModal = apos.modal.stack[apos.modal.stack.length - 1] ? apos.modal.stack[apos.modal.stack.length - 1].id : null;
@@ -346,7 +424,6 @@ export default {
346
424
  this.create();
347
425
  }
348
426
  },
349
-
350
427
  bindShortcuts() {
351
428
  window.addEventListener('keydown', this.shortcutNew);
352
429
  },
@@ -373,6 +450,61 @@ export default {
373
450
  _fields: result
374
451
  });
375
452
  }
453
+ },
454
+ setAllPiecesSelection ({
455
+ isSelected, total, docs
456
+ }) {
457
+ if (typeof isSelected === 'boolean') {
458
+ this.allPiecesSelection.isSelected = isSelected;
459
+ }
460
+
461
+ if (typeof total === 'number') {
462
+ this.allPiecesSelection.total = total;
463
+ }
464
+
465
+ if (docs) {
466
+ this.setCheckedDocs(docs);
467
+ }
468
+ },
469
+ async handleBatchAction({
470
+ label, action, requestOptions = {}, messages
471
+ }) {
472
+ if (action) {
473
+ try {
474
+ await apos.http.post(`${this.moduleOptions.action}/${action}`, {
475
+ body: {
476
+ ...requestOptions,
477
+ _ids: this.checked,
478
+ messages: messages,
479
+ type: this.checked.length === 1 ? this.moduleLabels.singular
480
+ : this.moduleLabels.plural
481
+ }
482
+ });
483
+ } catch (error) {
484
+ apos.notify('Batch operation {{ operation }} failed.', {
485
+ interpolate: { operation: label },
486
+ type: 'danger'
487
+ });
488
+ }
489
+ }
490
+ },
491
+ setUtilityOperations () {
492
+ const { utilityOperations } = this.moduleOptions;
493
+
494
+ const newPiece = {
495
+ action: 'new',
496
+ label: {
497
+ key: 'apostrophe:newDocType',
498
+ type: this.$t(this.moduleLabels.singular)
499
+ }
500
+ };
501
+
502
+ this.utilityOperations.menu = [
503
+ ...this.relationshipField && this.moduleOptions.canEdit
504
+ ? [ newPiece ] : [],
505
+ ...this.utilityOperations.menu,
506
+ ...(!this.relationshipField && Array.isArray(utilityOperations) && utilityOperations) || []
507
+ ];
376
508
  }
377
509
  }
378
510
  };
@@ -4,7 +4,6 @@
4
4
  <tr>
5
5
  <th
6
6
  class="apos-table__header"
7
- v-if="!options.hideCheckboxes"
8
7
  />
9
8
  <th
10
9
  v-for="header in headers" scope="col"
@@ -26,7 +25,7 @@
26
25
  <th class="apos-table__header" key="contextMenu">
27
26
  <component
28
27
  :is="getEl({})"
29
- class="apos-table__header-label is-hidden"
28
+ class="apos-table__header-label apos-is-hidden"
30
29
  >
31
30
  {{ $t('apostrophe:moreOperations') }}
32
31
  </component>
@@ -41,7 +40,6 @@
41
40
  >
42
41
  <td
43
42
  class="apos-table__cell"
44
- v-if="!options.hideCheckboxes"
45
43
  >
46
44
  <AposCheckbox
47
45
  v-if="item._id"
@@ -0,0 +1,138 @@
1
+ <template>
2
+ <transition
3
+ name="collapse"
4
+ :duration="300"
5
+ >
6
+ <div
7
+ v-if="selectedState === 'checked' || allPiecesSelection.isSelected"
8
+ class="apos-select-box"
9
+ >
10
+ <div class="apos-select-box__content">
11
+ <p class="apos-select-box__text">
12
+ {{ selectBoxMessage }}
13
+ <button
14
+ v-if="!allPiecesSelection.isSelected"
15
+ class="apos-select-box__select-all"
16
+ @click="$emit('select-all')"
17
+ >
18
+ {{ selectBoxMessageButton }}
19
+ </button>
20
+ <button
21
+ v-else
22
+ class="apos-select-box__select-all"
23
+ @click="clearSelection"
24
+ >
25
+ {{ $t('apostrophe:clearSelection') }}.
26
+ </button>
27
+ </p>
28
+ </div>
29
+ </div>
30
+ </transition>
31
+ </template>
32
+
33
+ <script>
34
+ export default {
35
+ props: {
36
+ selectedState: {
37
+ type: String,
38
+ required: true
39
+ },
40
+ moduleLabels: {
41
+ type: Object,
42
+ required: true
43
+ },
44
+ checkedIds: {
45
+ type: Array,
46
+ required: true
47
+ },
48
+ allPiecesSelection: {
49
+ type: Object,
50
+ required: true
51
+ },
52
+ displayedItems: {
53
+ type: Number,
54
+ required: true
55
+ }
56
+ },
57
+ emits: [ 'select-all', 'clear-select', 'set-all-pieces-selection' ],
58
+ computed: {
59
+ selectBoxMessage () {
60
+ const checkedCount = this.checkedIds.length;
61
+ const showAllWord = (checkedCount === this.allPiecesSelection.total) &&
62
+ checkedCount !== 1;
63
+
64
+ const translationKey = this.allPiecesSelection.isSelected
65
+ ? showAllWord
66
+ ? 'apostrophe:selectBoxMessageAllSelected'
67
+ : 'apostrophe:selectBoxMessageSelected'
68
+ : checkedCount > this.displayedItems
69
+ ? 'apostrophe:selectBoxMessage'
70
+ : 'apostrophe:selectBoxMessagePage';
71
+
72
+ return this.$t(translationKey, {
73
+ num: this.checkedIds.length,
74
+ label: this.getLabel(this.checkedIds.length)
75
+ });
76
+ },
77
+ selectBoxMessageButton () {
78
+ const translationKey = this.allPiecesSelection.total === 1
79
+ ? 'apostrophe:selectBoxMessageButton'
80
+ : 'apostrophe:selectBoxMessageAllButton';
81
+
82
+ return this.$t(translationKey, {
83
+ num: this.allPiecesSelection.total,
84
+ label: this.getLabel(this.allPiecesSelection.total)
85
+ });
86
+ }
87
+ },
88
+ methods: {
89
+ getLabel(number) {
90
+ return number === 1
91
+ ? this.$t(this.moduleLabels.singular).toLowerCase()
92
+ : this.$t(this.moduleLabels.plural).toLowerCase();
93
+ },
94
+ clearSelection () {
95
+ this.$emit('set-all-pieces-selection', {
96
+ isSelected: false,
97
+ docs: []
98
+ });
99
+ }
100
+ }
101
+ };
102
+ </script>
103
+ <style lang='scss' scoped>
104
+ .apos-select-box {
105
+ box-sizing: border-box;
106
+ overflow: hidden;
107
+ height: 5rem;
108
+ transition: all 0.3s linear;
109
+
110
+ &.collapse-enter, &.collapse-leave-to {
111
+ height: 0;
112
+ }
113
+
114
+ &__content {
115
+ display: flex;
116
+ align-items: center;
117
+ justify-content: center;
118
+ background-color: var(--a-base-9);
119
+ margin-top: 1rem;
120
+ color: var(--a-text-primary);
121
+ }
122
+
123
+ &__text {
124
+ @include type-large;
125
+ }
126
+
127
+ &__select-all {
128
+ color: var(--a-primary);
129
+ cursor: pointer;
130
+ margin-left: 0.4rem;
131
+ border: none;
132
+
133
+ &:hover {
134
+ text-decoration: underline;
135
+ }
136
+ }
137
+ }
138
+ </style>
@@ -26,7 +26,7 @@
26
26
  </AposContextMenuDialog>
27
27
  </bubble-menu>
28
28
  <div class="apos-rich-text-editor__editor" :class="editorModifiers">
29
- <editor-content :editor="editor" :class="moduleOptions.className" />
29
+ <editor-content :editor="editor" :class="editorOptions.className" />
30
30
  </div>
31
31
  <div class="apos-rich-text-editor__editor_after" :class="editorModifiers">
32
32
  {{ $t('apostrophe:emptyRichTextWidget') }}
@@ -100,13 +100,34 @@ export default {
100
100
  activeOptions.toolbar = (activeOptions.toolbar !== undefined)
101
101
  ? activeOptions.toolbar : this.defaultOptions.toolbar;
102
102
 
103
- activeOptions.styles = this.enhanceStyles(activeOptions.styles || this.defaultOptions.styles);
103
+ activeOptions.styles = this.enhanceStyles(
104
+ activeOptions.styles?.length ?
105
+ activeOptions.styles :
106
+ this.defaultOptions.styles
107
+ );
108
+
109
+ activeOptions.className = (activeOptions.className !== undefined)
110
+ ? activeOptions.className : this.moduleOptions.className;
104
111
 
105
112
  return activeOptions;
106
113
  },
107
-
114
+ autofocus() {
115
+ // Only true for a new rich text widget
116
+ return !this.stripPlaceholderBrs(this.value.content).length;
117
+ },
108
118
  initialContent() {
109
- return this.stripPlaceholderBrs(this.value.content);
119
+ const content = this.stripPlaceholderBrs(this.value.content);
120
+ if (!content.length) {
121
+ // If we don't supply a valid instance of the first style, then
122
+ // the text align control will not work until the user manually
123
+ // applies a style or refreshes the page
124
+ const defaultStyle = this.editorOptions.styles.find(style => style.def);
125
+
126
+ const _class = defaultStyle.class ? ` class="${defaultStyle.class}"` : '';
127
+ return `<${defaultStyle.tag}${_class}></${defaultStyle.tag}>`;
128
+ } else {
129
+ return content;
130
+ }
110
131
  },
111
132
  toolbar() {
112
133
  return this.editorOptions.toolbar;
@@ -135,7 +156,7 @@ export default {
135
156
  aposTiptapExtensions() {
136
157
  return (apos.tiptapExtensions || [])
137
158
  .map(extension => extension({
138
- styles: this.editorOptions.styles,
159
+ styles: this.editorOptions.styles.map(this.localizeStyle),
139
160
  types: this.tiptapTypes
140
161
  }));
141
162
  }
@@ -152,7 +173,7 @@ export default {
152
173
  mounted() {
153
174
  this.editor = new Editor({
154
175
  content: this.initialContent,
155
- autofocus: !this.initialContent,
176
+ autofocus: this.autofocus,
156
177
  onUpdate: this.editorUpdate,
157
178
  extensions: [
158
179
  StarterKit,
@@ -265,6 +286,14 @@ export default {
265
286
  }
266
287
  }
267
288
  return styles;
289
+ },
290
+ localizeStyle(style) {
291
+ style.label = this.$t(style.label);
292
+
293
+ return {
294
+ ...style,
295
+ label: this.$t(style.label)
296
+ };
268
297
  }
269
298
  }
270
299
  };
@@ -25,9 +25,7 @@ export default (options) => {
25
25
  const tag = element.tagName.toLowerCase();
26
26
  // This tag is not configured
27
27
  if (!allow[tag]) {
28
- return {
29
- class: null
30
- };
28
+ return null;
31
29
  }
32
30
  const classes = (element.getAttribute('class') || '')
33
31
  .split(' ')