apostrophe 4.5.4 → 4.6.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 (55) hide show
  1. package/CHANGELOG.md +30 -1
  2. package/lib/mongodb-connect.js +9 -2
  3. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposAdminBar.vue +6 -1
  4. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposAdminBarLocale.vue +18 -180
  5. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposAdminBarMenu.vue +6 -2
  6. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextBar.vue +1 -1
  7. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextModeAndSettings.vue +11 -0
  8. package/modules/@apostrophecms/area/ui/apos/components/AposAreaContextualMenu.vue +7 -3
  9. package/modules/@apostrophecms/area/ui/apos/components/AposAreaEditor.vue +2 -0
  10. package/modules/@apostrophecms/area/ui/apos/components/AposAreaMenu.vue +11 -1
  11. package/modules/@apostrophecms/area/ui/apos/components/AposAreaWidget.vue +128 -30
  12. package/modules/@apostrophecms/area/ui/apos/components/AposWidgetControls.vue +12 -15
  13. package/modules/@apostrophecms/asset/index.js +8 -4
  14. package/modules/@apostrophecms/asset/lib/webpack/apos/webpack.config.js +10 -1
  15. package/modules/@apostrophecms/db/index.js +2 -3
  16. package/modules/@apostrophecms/doc-type/index.js +7 -1
  17. package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocEditor.vue +183 -109
  18. package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocLocalePicker.vue +177 -0
  19. package/modules/@apostrophecms/doc-type/ui/apos/logic/AposDocContextMenu.js +4 -0
  20. package/modules/@apostrophecms/i18n/i18n/de.json +1 -0
  21. package/modules/@apostrophecms/i18n/i18n/en.json +7 -1
  22. package/modules/@apostrophecms/i18n/i18n/es.json +1 -0
  23. package/modules/@apostrophecms/i18n/i18n/fr.json +1 -0
  24. package/modules/@apostrophecms/i18n/i18n/it.json +1 -0
  25. package/modules/@apostrophecms/i18n/i18n/pt-BR.json +1 -0
  26. package/modules/@apostrophecms/i18n/i18n/sk.json +1 -0
  27. package/modules/@apostrophecms/i18n/index.js +22 -0
  28. package/modules/@apostrophecms/i18n/ui/apos/components/AposI18nLocalize.vue +1 -0
  29. package/modules/@apostrophecms/i18n/ui/src/index.js +3 -0
  30. package/modules/@apostrophecms/modal/ui/apos/apps/AposModals.js +1 -0
  31. package/modules/@apostrophecms/modal/ui/apos/components/AposModal.vue +22 -30
  32. package/modules/@apostrophecms/modal/ui/apos/components/AposModalConfirm.vue +24 -1
  33. package/modules/@apostrophecms/modal/ui/apos/components/TheAposModals.vue +48 -38
  34. package/modules/@apostrophecms/modal/ui/apos/mixins/AposDocsManagerMixin.js +7 -0
  35. package/modules/@apostrophecms/page/index.js +3 -1
  36. package/modules/@apostrophecms/page/ui/apos/components/AposPagesManager.vue +1 -0
  37. package/modules/@apostrophecms/page/ui/apos/logic/AposPagesManager.js +17 -3
  38. package/modules/@apostrophecms/piece-type/index.js +3 -1
  39. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManager.vue +25 -4
  40. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManagerSelectBox.vue +1 -1
  41. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposRichTextWidgetEditor.vue +2 -0
  42. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapLink.vue +4 -3
  43. package/modules/@apostrophecms/ui/ui/apos/components/AposButton.vue +17 -10
  44. package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenu.vue +51 -30
  45. package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenuItem.vue +1 -0
  46. package/modules/@apostrophecms/ui/ui/apos/components/AposLocale.vue +36 -0
  47. package/modules/@apostrophecms/ui/ui/apos/components/AposLocalePicker.vue +283 -0
  48. package/modules/@apostrophecms/ui/ui/apos/components/AposSlat.vue +56 -18
  49. package/modules/@apostrophecms/ui/ui/apos/lib/tooltip.js +2 -1
  50. package/modules/@apostrophecms/ui/ui/apos/mixins/AposArchiveMixin.js +4 -2
  51. package/modules/@apostrophecms/ui/ui/apos/mixins/AposPublishMixin.js +12 -6
  52. package/modules/@apostrophecms/ui/ui/apos/scss/mixins/_zindex.scss +1 -0
  53. package/modules/@apostrophecms/ui/ui/apos/stores/modal.js +10 -1
  54. package/modules/@apostrophecms/util/ui/src/http.js +12 -5
  55. package/package.json +4 -4
package/CHANGELOG.md CHANGED
@@ -1,10 +1,39 @@
1
1
  # Changelog
2
2
 
3
- ## UNRELEASED
3
+ ## 4.6.1 (2024-08-26)
4
4
 
5
5
  ### Fixes
6
6
 
7
+ * Registering duplicate icon is no longer breaking the build.
8
+ * Fix widget focus state so that the in-context Add Content menu stays visible during animation.
9
+ * Fix UI of areas in schemas so that their context menus are layered overtop sibling schema fields UI.
10
+
11
+ ## 4.6.0 (2024-08-08)
12
+
13
+ ### Adds
14
+
15
+ * Add a locale switcher in pieces and pages editor modals. This is available for localized documents only, and allows you to switch between locales for the same document.
16
+ The locale can be switche at only one level, meaning that sub documents of a document that already switched locale will not be able to switch locale itself.
17
+ * Adds visual focus states and keyboard handlers for engaging with areas and widgets in-context
18
+
19
+ ### Changes
20
+
21
+ * Add `title` and `_url` to select all projection.
22
+ * Display `Select all` message on all pages in the manager modal.
23
+ * Refresh `checked` in manager modal after archive action.
24
+ * Update `@apostrophecms/emulate-mongo-3-driver` dependency to keep supporting `mongodb@3.x` queries while using `mongodb@6.x`.
25
+ * Updates rich text link tool's keyboard key detection strategy.
26
+ * Buttons that appear on slats (preview, edit crop/relationship, remove) are visually focusable and keyboard accessible.
27
+ * Added tooltip for update button. Thanks to [gkumar9891](https://github.com/gkumar9891) for this addition.
28
+
29
+ ### Fixes
30
+
31
+ * Fixes the rich text link tool's detection and display of the Remove Link button for removing existing links
32
+ * Fixes the rich text link tool's detection and display of Apostrophe Page relationship field.
7
33
  * Overriding standard Vue.js components with `editorModal` and `managerModal` are now applied all the time.
34
+ * Accommodate old-style replica set URIs with comma-separated servers by passing any MongoDB URIs that Node.js cannot parse directly to the MongoDB driver, and avoiding unnecessary parsing of the URI in general.
35
+ * Bump `oembetter` dependency to guarantee compatibility with YouTube. YouTube recently deployed broken `link rel="undefined"` tags on some of their video pages.
36
+ * It is now possible to see the right filename and line number when debugging the admin UI build in the browser. This is automatically disabled when `@apostrophecms/security-headers` is installed, because its defaults are incompatible by design.
8
37
 
9
38
  ## 4.5.4 (2024-07-22)
10
39
 
@@ -16,8 +16,15 @@ module.exports = async (uri, options) => {
16
16
  useNewUrlParser: true,
17
17
  ...options
18
18
  };
19
- const parsed = new URL(uri);
20
- if ((parsed.protocol !== 'mongodb:') || (parsed.hostname !== 'localhost')) {
19
+ let parsed;
20
+ try {
21
+ parsed = new URL(uri);
22
+ } catch (e) {
23
+ // Parse failed, e.g. old school replica set URI
24
+ // with commas, just let the mongo driver handle it
25
+ return mongo.MongoClient.connect(uri, connectOptions);
26
+ }
27
+ if (!parsed || (parsed.protocol !== 'mongodb:') || (parsed.hostname !== 'localhost')) {
21
28
  return mongo.MongoClient.connect(parsed.toString(), connectOptions);
22
29
  }
23
30
  const records = await dns.promises.lookup('localhost', { all: true });
@@ -5,7 +5,12 @@
5
5
  :class="themeClass"
6
6
  >
7
7
  <div ref="spacer" class="apos-admin-bar-spacer" />
8
- <nav ref="adminBar" class="apos-admin-bar">
8
+ <nav
9
+ ref="adminBar"
10
+ class="apos-admin-bar"
11
+ role="menubar"
12
+ aria-label="Apostrophe Admin Bar"
13
+ >
9
14
  <div class="apos-admin-bar__row">
10
15
  <AposLogoPadless class="apos-admin-bar__logo" />
11
16
  <TheAposAdminBarMenu :items="menuItems" />
@@ -2,63 +2,19 @@
2
2
  <AposContextMenu
3
3
  ref="menu"
4
4
  class="apos-admin-locales"
5
+ identifier="localePickerTrigger"
5
6
  :button="button"
6
7
  :unpadded="true"
7
8
  menu-placement="bottom-end"
8
9
  @open="open"
10
+ @close="isOpen = false"
9
11
  >
10
- <div class="apos-locales-picker">
11
- <div class="apos-input-wrapper">
12
- <input
13
- v-model="search"
14
- type="text"
15
- class="apos-locales-filter"
16
- :placeholder="$t('apostrophe:searchLocalesPlaceholder')"
17
- >
18
- </div>
19
- <ul class="apos-locales">
20
- <li
21
- v-for="locale in filteredLocales"
22
- :key="locale.name"
23
- class="apos-locale-item"
24
- :class="localeClasses(locale)"
25
- @click="switchLocale(locale)"
26
- >
27
- <span class="apos-locale">
28
- <AposIndicator
29
- v-if="isActive(locale)"
30
- icon="check-bold-icon"
31
- fill-color="var(--a-primary)"
32
- class="apos-check"
33
- :icon-size="12"
34
- :title="$t('apostrophe:currentLocale')"
35
- />
36
- {{ locale.label }}
37
- <span class="apos-locale-name">
38
- ({{ locale.name }})
39
- </span>
40
- <span
41
- class="apos-locale-localized"
42
- :class="{ 'apos-state-is-localized': isLocalized(locale) }"
43
- />
44
- </span>
45
- </li>
46
- </ul>
47
- <div class="apos-available-locales">
48
- <p class="apos-available-description">
49
- {{ $t('apostrophe:documentExistsInLocales') }}
50
- </p>
51
- <AposButton
52
- v-for="locale in availableLocales"
53
- :key="locale.name"
54
- class="apos-available-locale"
55
- :label="locale.label"
56
- type="quiet"
57
- :modifiers="['no-motion']"
58
- @click="switchLocale(locale)"
59
- />
60
- </div>
61
- </div>
12
+ <AposLocalePicker
13
+ :current-locale="locale"
14
+ :localized="localized"
15
+ :is-open="isOpen"
16
+ @switch-locale="switchLocale"
17
+ />
62
18
  </AposContextMenu>
63
19
  </template>
64
20
 
@@ -76,10 +32,14 @@ export default {
76
32
  };
77
33
  }
78
34
  ),
79
- localized: {}
35
+ localized: {},
36
+ isOpen: false
80
37
  };
81
38
  },
82
39
  computed: {
40
+ locale() {
41
+ return window.apos.i18n.locale;
42
+ },
83
43
  button() {
84
44
  return {
85
45
  label: {
@@ -91,21 +51,14 @@ export default {
91
51
  type: 'quiet'
92
52
  };
93
53
  },
94
- filteredLocales(input) {
95
- return this.locales.filter(({ name, label }) => {
96
- const matches = term =>
97
- term.toLowerCase().includes(this.search.toLowerCase());
98
- return matches(name) || matches(label);
99
- });
100
- },
101
- availableLocales() {
102
- return this.locales.filter(locale => !!this.localized[locale.name]);
103
- },
104
54
  action() {
105
55
  return apos.modules[apos.adminBar.context.type]?.action;
106
56
  }
107
57
  },
108
58
  methods: {
59
+ close() {
60
+ this.isOpen = false;
61
+ },
109
62
  async open() {
110
63
  if (apos.adminBar.context) {
111
64
  const docs = await apos.http.get(
@@ -120,20 +73,13 @@ export default {
120
73
  .map(doc => [ doc.aposLocale.split(':')[0], doc ])
121
74
  );
122
75
  }
76
+ this.isOpen = true;
123
77
  },
124
78
  isActive(locale) {
125
79
  return window.apos.i18n.locale === locale.name;
126
80
  },
127
81
  isLocalized(locale) {
128
- return !!this.localized[locale.name];
129
- },
130
- localeClasses(locale) {
131
- const classes = {};
132
- if (this.isActive(locale)) {
133
- classes['apos-active'] = true;
134
- }
135
- classes['apos-exists'] = this.localized[locale.name];
136
- return classes;
82
+ return Boolean(this.localized[locale.name]);
137
83
  },
138
84
  async switchLocale(locale) {
139
85
  const { name } = locale;
@@ -221,112 +167,4 @@ export default {
221
167
  letter-spacing: 1px;
222
168
  }
223
169
  }
224
-
225
- .apos-locales-picker {
226
- width: 315px;
227
- }
228
-
229
- .apos-locales-filter {
230
- @include type-large;
231
-
232
- box-sizing: border-box;
233
- width: 100%;
234
- padding: 20px 45px 20px 20px;
235
- border-top: 0;
236
- border-right: 0;
237
- border-bottom: 1px solid var(--a-base-9);
238
- border-left: 0;
239
- border-top-right-radius: var(--a-border-radius);
240
- border-top-left-radius: var(--a-border-radius);
241
-
242
- &::placeholder {
243
- color: var(--a-base-4);
244
- font-style: italic;
245
- }
246
-
247
- &:focus {
248
- outline: none;
249
- background-color: var(--a-base-10);
250
- }
251
- }
252
-
253
- .apos-locales {
254
- margin: $spacing-base 0;
255
- padding-left: 0;
256
- list-style-type: none;
257
- max-height: 350px;
258
- overflow-y: scroll;
259
- font-weight: var(--a-weight-base);
260
- }
261
-
262
- .apos-locale-item {
263
- position: relative;
264
- padding: 12px 35px;
265
- line-height: 1;
266
- cursor: pointer;
267
-
268
- .state {
269
- opacity: 0;
270
- }
271
-
272
- &:hover {
273
- background-color: var(--a-base-10);
274
- }
275
-
276
- .apos-check {
277
- position: absolute;
278
- top: 50%;
279
- left: 18px;
280
- transform: translateY(-50%);
281
- color: var(--a-primary);
282
- stroke: var(--a-primary);
283
- }
284
-
285
- &.apos-active {
286
- .active {
287
- opacity: 1;
288
- }
289
- }
290
-
291
- .apos-locale-localized {
292
- position: relative;
293
- top: -1px;
294
- left: 5px;
295
- display: inline-block;
296
- width: 3px;
297
- height: 3px;
298
- border: 1px solid var(--a-base-5);
299
- border-radius: 50%;
300
-
301
- &.apos-state-is-localized {
302
- background-color: var(--a-success);
303
- border-color: var(--a-success);
304
- }
305
- }
306
- }
307
-
308
- .apos-available-locales {
309
- padding: $spacing-double;
310
- border-top: 1px solid var(--a-base-9);
311
- }
312
-
313
- .apos-available-locale {
314
- display: inline-block;
315
- color: var(--a-primary);
316
- font-size: var(--a-type-small);
317
- }
318
-
319
- .apos-available-locale:not(:last-of-type) {
320
- margin-right: 10px;
321
- margin-bottom: 5px;
322
- }
323
-
324
- .apos-available-description {
325
- margin-top: 0;
326
- }
327
-
328
- .apos-locale-name {
329
- text-transform: uppercase;
330
- }
331
-
332
170
  </style>
@@ -1,11 +1,12 @@
1
1
  <template>
2
- <ul class="apos-admin-bar__items">
2
+ <ol class="apos-admin-bar__items" role="menu">
3
3
  <li v-if="pageTree" class="apos-admin-bar__item">
4
4
  <AposButton
5
5
  type="subtle"
6
6
  label="apostrophe:pages"
7
7
  class="apos-admin-bar__btn"
8
8
  :modifiers="['no-motion']"
9
+ role="menuitem"
9
10
  @click="emitEvent('@apostrophecms/page:manager')"
10
11
  />
11
12
  </li>
@@ -24,6 +25,7 @@
24
25
  class: 'apos-admin-bar__btn',
25
26
  type: 'subtle'
26
27
  }"
28
+ role="menuitem"
27
29
  @item-clicked="emitEvent"
28
30
  />
29
31
  <Component
@@ -33,6 +35,7 @@
33
35
  :label="item.label"
34
36
  :modifiers="['no-motion']"
35
37
  class="apos-admin-bar__btn"
38
+ role="menuitem"
36
39
  @click="emitEvent(item.action)"
37
40
  />
38
41
  </li>
@@ -47,6 +50,7 @@
47
50
  type: 'primary',
48
51
  modifiers: ['round', 'no-motion']
49
52
  }"
53
+ role="menuitem"
50
54
  @item-clicked="emitEvent"
51
55
  />
52
56
  </li>
@@ -76,7 +80,7 @@
76
80
  />
77
81
  </template>
78
82
  </li>
79
- </ul>
83
+ </ol>
80
84
  </template>
81
85
 
82
86
  <script>
@@ -498,7 +498,7 @@ export default {
498
498
  const contextOptions = this.context
499
499
  ? apos.modules[this.context.type]
500
500
  : { contentChangedRefresh: true };
501
- if (contextOptions.contentChangedRefresh) {
501
+ if (!e.localeSwitched && contextOptions.contentChangedRefresh) {
502
502
  await this.refresh({
503
503
  scrollcheck: e.action === 'history'
504
504
  });
@@ -53,6 +53,7 @@
53
53
  v-if="editMode && !isAutopublished"
54
54
  type="primary"
55
55
  :label="publishLabel"
56
+ :tooltip="publishTooltip"
56
57
  :disabled="!readyToPublish"
57
58
  class="apos-admin-bar__btn apos-admin-bar__context-button"
58
59
  :modifiers="['no-motion']"
@@ -133,6 +134,16 @@ export default {
133
134
  }
134
135
  }
135
136
  },
137
+ publishTooltip() {
138
+ if (this.canPublish && this.context.lastPublishedAt && !this.hasBeenPublishedThisPageload) {
139
+ return {
140
+ content: 'apostrophe:updateTooltip',
141
+ placement: 'bottom'
142
+ };
143
+ }
144
+
145
+ return false;
146
+ },
136
147
  isAutopublished() {
137
148
  return this.context._aposAutopublish ?? (window.apos.modules[this.context.type].autopublish || false);
138
149
  },
@@ -6,6 +6,7 @@
6
6
  :disabled="isDisabled"
7
7
  :button="buttonOptions"
8
8
  :popover-modifiers="inContext ? ['z-index-in-context'] : []"
9
+ :menu-id="menuId"
9
10
  >
10
11
  <ul class="apos-area-menu__wrapper">
11
12
  <li
@@ -117,6 +118,12 @@ export default {
117
118
  default: function() {
118
119
  return {};
119
120
  }
121
+ },
122
+ menuId: {
123
+ type: String,
124
+ default() {
125
+ return `areaMenu-${createId()}`;
126
+ }
120
127
  }
121
128
  },
122
129
  emits: [ 'add' ],
@@ -182,9 +189,6 @@ export default {
182
189
  } else {
183
190
  return menu;
184
191
  }
185
- },
186
- menuId() {
187
- return `areaMenu-${createId()}`;
188
192
  }
189
193
  },
190
194
  mounted() {
@@ -15,6 +15,7 @@
15
15
  label: $t(contextMenuOptions.menu[0].label)
16
16
  }"
17
17
  :disabled="field && field.readOnly"
18
+ :disable-focus="false"
18
19
  type="primary"
19
20
  :icon="icon"
20
21
  @click="add({ index: 0, name: contextMenuOptions.menu[0].name })"
@@ -29,6 +30,7 @@
29
30
  :max-reached="maxReached"
30
31
  :disabled="field && field.readOnly"
31
32
  :widget-options="options.widgets"
33
+ :tabbable="true"
32
34
  @add="add"
33
35
  />
34
36
  </template>
@@ -16,6 +16,7 @@
16
16
  :options="options"
17
17
  :max-reached="maxReached"
18
18
  :disabled="disabled"
19
+ :menu-id="menuId"
19
20
  @add="$emit('add', $event);"
20
21
  />
21
22
  </template>
@@ -54,6 +55,14 @@ export default {
54
55
  default: function() {
55
56
  return {};
56
57
  }
58
+ },
59
+ tabbable: {
60
+ type: Boolean,
61
+ default: false
62
+ },
63
+ menuId: {
64
+ type: String,
65
+ default: null
57
66
  }
58
67
  },
59
68
  emits: [ 'add' ],
@@ -64,7 +73,8 @@ export default {
64
73
  icon: 'plus-icon',
65
74
  type: 'primary',
66
75
  modifiers: this.empty ? [] : [ 'round', 'tiny' ],
67
- iconSize: this.empty ? 20 : 11
76
+ iconSize: this.empty ? 20 : 11,
77
+ disableFocus: !this.tabbable
68
78
  };
69
79
  }
70
80
  },