apostrophe 4.5.3 → 4.6.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 +33 -0
- package/lib/mongodb-connect.js +9 -2
- package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposAdminBar.vue +6 -1
- package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposAdminBarLocale.vue +18 -180
- package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposAdminBarMenu.vue +6 -2
- package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextBar.vue +1 -1
- package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextModeAndSettings.vue +11 -0
- package/modules/@apostrophecms/any-page-type/index.js +6 -1
- package/modules/@apostrophecms/area/ui/apos/components/AposAreaEditor.vue +2 -0
- package/modules/@apostrophecms/area/ui/apos/components/AposAreaMenu.vue +6 -1
- package/modules/@apostrophecms/area/ui/apos/components/AposAreaWidget.vue +63 -11
- package/modules/@apostrophecms/area/ui/apos/components/AposWidgetControls.vue +12 -15
- package/modules/@apostrophecms/asset/lib/webpack/apos/webpack.config.js +10 -1
- package/modules/@apostrophecms/db/index.js +2 -3
- package/modules/@apostrophecms/doc-type/index.js +7 -1
- package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocEditor.vue +183 -109
- package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocLocalePicker.vue +177 -0
- package/modules/@apostrophecms/doc-type/ui/apos/logic/AposDocContextMenu.js +4 -0
- package/modules/@apostrophecms/i18n/i18n/de.json +1 -0
- package/modules/@apostrophecms/i18n/i18n/en.json +7 -1
- package/modules/@apostrophecms/i18n/i18n/es.json +1 -0
- package/modules/@apostrophecms/i18n/i18n/fr.json +1 -0
- package/modules/@apostrophecms/i18n/i18n/it.json +1 -0
- package/modules/@apostrophecms/i18n/i18n/pt-BR.json +1 -0
- package/modules/@apostrophecms/i18n/i18n/sk.json +1 -0
- package/modules/@apostrophecms/i18n/index.js +22 -0
- package/modules/@apostrophecms/i18n/ui/apos/components/AposI18nLocalize.vue +1 -0
- package/modules/@apostrophecms/i18n/ui/src/index.js +3 -0
- package/modules/@apostrophecms/modal/ui/apos/apps/AposModals.js +1 -0
- package/modules/@apostrophecms/modal/ui/apos/components/AposModal.vue +22 -30
- package/modules/@apostrophecms/modal/ui/apos/components/AposModalConfirm.vue +24 -1
- package/modules/@apostrophecms/modal/ui/apos/components/TheAposModals.vue +48 -38
- package/modules/@apostrophecms/modal/ui/apos/mixins/AposDocsManagerMixin.js +7 -0
- package/modules/@apostrophecms/page/index.js +5 -3
- package/modules/@apostrophecms/page/ui/apos/components/AposPagesManager.vue +1 -0
- package/modules/@apostrophecms/page/ui/apos/logic/AposPagesManager.js +17 -3
- package/modules/@apostrophecms/piece-type/index.js +5 -3
- package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManager.vue +25 -4
- package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManagerSelectBox.vue +1 -1
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapLink.vue +4 -3
- package/modules/@apostrophecms/search/index.js +36 -2
- package/modules/@apostrophecms/ui/ui/apos/components/AposButton.vue +17 -10
- package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenu.vue +36 -23
- package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenuDialog.vue +1 -1
- package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenuItem.vue +1 -0
- package/modules/@apostrophecms/ui/ui/apos/components/AposLocale.vue +36 -0
- package/modules/@apostrophecms/ui/ui/apos/components/AposLocalePicker.vue +283 -0
- package/modules/@apostrophecms/ui/ui/apos/components/AposSlat.vue +56 -18
- package/modules/@apostrophecms/ui/ui/apos/lib/tooltip.js +2 -1
- package/modules/@apostrophecms/ui/ui/apos/mixins/AposArchiveMixin.js +4 -2
- package/modules/@apostrophecms/ui/ui/apos/mixins/AposPublishMixin.js +12 -6
- package/modules/@apostrophecms/ui/ui/apos/stores/modal.js +10 -1
- package/modules/@apostrophecms/util/ui/src/http.js +12 -5
- package/package.json +4 -4
- package/test/search.js +64 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 4.6.0 (2024-08-08)
|
|
4
|
+
|
|
5
|
+
### Adds
|
|
6
|
+
|
|
7
|
+
* 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.
|
|
8
|
+
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.
|
|
9
|
+
* Adds visual focus states and keyboard handlers for engaging with areas and widgets in-context
|
|
10
|
+
|
|
11
|
+
### Changes
|
|
12
|
+
|
|
13
|
+
* Add `title` and `_url` to select all projection.
|
|
14
|
+
* Display `Select all` message on all pages in the manager modal.
|
|
15
|
+
* Refresh `checked` in manager modal after archive action.
|
|
16
|
+
* Update `@apostrophecms/emulate-mongo-3-driver` dependency to keep supporting `mongodb@3.x` queries while using `mongodb@6.x`.
|
|
17
|
+
* Updates rich text link tool's keyboard key detection strategy.
|
|
18
|
+
* Buttons that appear on slats (preview, edit crop/relationship, remove) are visually focusable and keyboard accessible.
|
|
19
|
+
* Added tooltip for update button. Thanks to [gkumar9891](https://github.com/gkumar9891) for this addition.
|
|
20
|
+
|
|
21
|
+
### Fixes
|
|
22
|
+
|
|
23
|
+
* Fixes the rich text link tool's detection and display of the Remove Link button for removing existing links
|
|
24
|
+
* Fixes the rich text link tool's detection and display of Apostrophe Page relationship field.
|
|
25
|
+
* Overriding standard Vue.js components with `editorModal` and `managerModal` are now applied all the time.
|
|
26
|
+
* 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.
|
|
27
|
+
* Bump `oembetter` dependency to guarantee compatibility with YouTube. YouTube recently deployed broken `link rel="undefined"` tags on some of their video pages.
|
|
28
|
+
* 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.
|
|
29
|
+
|
|
30
|
+
## 4.5.4 (2024-07-22)
|
|
31
|
+
|
|
32
|
+
### Fixes
|
|
33
|
+
|
|
34
|
+
* Add a default projection to ancestors of search results in order to load a reasonable amount of data and avoid request timeouts.
|
|
35
|
+
|
|
3
36
|
## 4.5.3 (2024-07-17)
|
|
4
37
|
|
|
5
38
|
### Fixes
|
package/lib/mongodb-connect.js
CHANGED
|
@@ -16,8 +16,15 @@ module.exports = async (uri, options) => {
|
|
|
16
16
|
useNewUrlParser: true,
|
|
17
17
|
...options
|
|
18
18
|
};
|
|
19
|
-
|
|
20
|
-
|
|
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
|
|
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
|
-
<
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
|
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
|
-
<
|
|
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
|
-
</
|
|
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
|
});
|
package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextModeAndSettings.vue
CHANGED
|
@@ -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
|
},
|
|
@@ -120,6 +120,11 @@ module.exports = {
|
|
|
120
120
|
continue;
|
|
121
121
|
}
|
|
122
122
|
const subquery = self.apos.page.find(req);
|
|
123
|
+
|
|
124
|
+
if (req.aposAncestors === true && req.aposAncestorsApiProjection) {
|
|
125
|
+
subquery.project(req.aposAncestorsApiProjection);
|
|
126
|
+
}
|
|
127
|
+
|
|
123
128
|
subquery.ancestorPerformanceRestrictions();
|
|
124
129
|
const parameters = applySubqueryOptions(subquery, options, [ 'depth' ]);
|
|
125
130
|
const components = page.path.split('/');
|
|
@@ -145,7 +150,7 @@ module.exports = {
|
|
|
145
150
|
}
|
|
146
151
|
if (!paths.length) {
|
|
147
152
|
page._ancestors = [];
|
|
148
|
-
|
|
153
|
+
continue;
|
|
149
154
|
}
|
|
150
155
|
subquery.and({
|
|
151
156
|
path: { $in: paths }
|
|
@@ -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>
|
|
@@ -54,6 +54,10 @@ export default {
|
|
|
54
54
|
default: function() {
|
|
55
55
|
return {};
|
|
56
56
|
}
|
|
57
|
+
},
|
|
58
|
+
tabbable: {
|
|
59
|
+
type: Boolean,
|
|
60
|
+
default: false
|
|
57
61
|
}
|
|
58
62
|
},
|
|
59
63
|
emits: [ 'add' ],
|
|
@@ -64,7 +68,8 @@ export default {
|
|
|
64
68
|
icon: 'plus-icon',
|
|
65
69
|
type: 'primary',
|
|
66
70
|
modifiers: this.empty ? [] : [ 'round', 'tiny' ],
|
|
67
|
-
iconSize: this.empty ? 20 : 11
|
|
71
|
+
iconSize: this.empty ? 20 : 11,
|
|
72
|
+
disableFocus: !this.tabbable
|
|
68
73
|
};
|
|
69
74
|
}
|
|
70
75
|
},
|
|
@@ -10,11 +10,15 @@
|
|
|
10
10
|
:data-apos-widget-id="widget._id"
|
|
11
11
|
>
|
|
12
12
|
<div
|
|
13
|
+
ref="wrapper"
|
|
13
14
|
class="apos-area-widget-inner"
|
|
14
15
|
:class="containerClasses"
|
|
16
|
+
tabindex="0"
|
|
15
17
|
@mouseover="mouseover($event)"
|
|
16
18
|
@mouseleave="mouseleave"
|
|
17
|
-
@click="getFocus($event, widget._id)"
|
|
19
|
+
@click="getFocus($event, widget._id);"
|
|
20
|
+
@focus="attachKeyboardFocusHandler"
|
|
21
|
+
@blur="removeKeyboardFocusHandler"
|
|
18
22
|
>
|
|
19
23
|
<div
|
|
20
24
|
ref="label"
|
|
@@ -37,6 +41,7 @@
|
|
|
37
41
|
icon="chevron-right-icon"
|
|
38
42
|
:icon-size="9"
|
|
39
43
|
:modifiers="['icon-right', 'no-motion']"
|
|
44
|
+
:disable-focus="!(isHovered || isFocused)"
|
|
40
45
|
@click="getFocus($event, item.id)"
|
|
41
46
|
/>
|
|
42
47
|
</li>
|
|
@@ -50,6 +55,7 @@
|
|
|
50
55
|
:tooltip="!isContextual && 'apostrophe:editWidgetForeignTooltip'"
|
|
51
56
|
:icon-size="11"
|
|
52
57
|
:modifiers="['no-motion']"
|
|
58
|
+
:disable-focus="!(isHovered || isFocused)"
|
|
53
59
|
@click="foreign ? $emit('edit', i) : null"
|
|
54
60
|
@dblclick="(!foreign && !isContextual) ? $emit('edit', i) : null"
|
|
55
61
|
/>
|
|
@@ -68,9 +74,14 @@
|
|
|
68
74
|
:widget-options="widgets"
|
|
69
75
|
:options="options"
|
|
70
76
|
:disabled="disabled"
|
|
77
|
+
:tabbable="isHovered || isFocused"
|
|
71
78
|
@add="$emit('add', $event);"
|
|
72
79
|
/>
|
|
73
80
|
</div>
|
|
81
|
+
<div
|
|
82
|
+
class="apos-area-widget-guard"
|
|
83
|
+
:class="{'apos-is-disabled': isFocused}"
|
|
84
|
+
/>
|
|
74
85
|
<div
|
|
75
86
|
class="apos-area-widget-controls apos-area-widget-controls--modify"
|
|
76
87
|
:class="controlsClasses"
|
|
@@ -83,6 +94,7 @@
|
|
|
83
94
|
:foreign="foreign"
|
|
84
95
|
:disabled="disabled"
|
|
85
96
|
:max-reached="maxReached"
|
|
97
|
+
:tabbable="isFocused"
|
|
86
98
|
@up="$emit('up', i);"
|
|
87
99
|
@remove="$emit('remove', i);"
|
|
88
100
|
@edit="$emit('edit', i);"
|
|
@@ -92,14 +104,6 @@
|
|
|
92
104
|
@down="$emit('down', i);"
|
|
93
105
|
/>
|
|
94
106
|
</div>
|
|
95
|
-
<!--
|
|
96
|
-
Note: we will not need this guard layer when we implement widget controls outside of the widget DOM
|
|
97
|
-
because we will be drawing and fitting a new layer ontop of the widget, which we can use to proxy event handling.
|
|
98
|
-
-->
|
|
99
|
-
<div
|
|
100
|
-
class="apos-area-widget-guard"
|
|
101
|
-
:class="{'apos-is-disabled': isFocused}"
|
|
102
|
-
/>
|
|
103
107
|
<!-- Still used for contextual editing components -->
|
|
104
108
|
<component
|
|
105
109
|
:is="widgetEditorComponent(widget.type)"
|
|
@@ -143,6 +147,7 @@
|
|
|
143
147
|
:widget-options="widgets"
|
|
144
148
|
:options="options"
|
|
145
149
|
:disabled="disabled"
|
|
150
|
+
:tabbable="isHovered || isFocused"
|
|
146
151
|
@add="$emit('add', $event)"
|
|
147
152
|
/>
|
|
148
153
|
</div>
|
|
@@ -348,6 +353,15 @@ export default {
|
|
|
348
353
|
return !!(this.docId && (window.apos.adminBar.contextId !== this.docId));
|
|
349
354
|
}
|
|
350
355
|
},
|
|
356
|
+
watch: {
|
|
357
|
+
isFocused(newVal) {
|
|
358
|
+
if (newVal) {
|
|
359
|
+
this.$refs.wrapper.addEventListener('keydown', this.handleKeyboardUnfocus);
|
|
360
|
+
} else {
|
|
361
|
+
this.$refs.wrapper.removeEventListener('keydown', this.handleKeyboardUnfocus);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
},
|
|
351
365
|
created() {
|
|
352
366
|
if (this.options.groups) {
|
|
353
367
|
for (const group of Object.keys(this.options.groups)) {
|
|
@@ -390,6 +404,14 @@ export default {
|
|
|
390
404
|
return offsetTop - labelHeight < adminBarHeight;
|
|
391
405
|
},
|
|
392
406
|
|
|
407
|
+
attachKeyboardFocusHandler() {
|
|
408
|
+
this.$refs.wrapper.addEventListener('keydown', this.handleKeyboardFocus);
|
|
409
|
+
},
|
|
410
|
+
|
|
411
|
+
removeKeyboardFocusHandler() {
|
|
412
|
+
this.$refs.wrapper.removeEventListener('keydown', this.handleKeyboardFocus);
|
|
413
|
+
},
|
|
414
|
+
|
|
393
415
|
// Focus parent, useful for obtrusive UI
|
|
394
416
|
focusParent() {
|
|
395
417
|
// Something above us asked the focused widget to try and focus its parent
|
|
@@ -439,6 +461,22 @@ export default {
|
|
|
439
461
|
}
|
|
440
462
|
},
|
|
441
463
|
|
|
464
|
+
handleKeyboardFocus($event) {
|
|
465
|
+
if ($event.key === 'Enter' || $event.code === 'Space') {
|
|
466
|
+
$event.preventDefault();
|
|
467
|
+
this.getFocus($event, this.widget._id);
|
|
468
|
+
this.$refs.wrapper.removeEventListener('keydown', this.handleKeyboardFocus);
|
|
469
|
+
}
|
|
470
|
+
},
|
|
471
|
+
|
|
472
|
+
handleKeyboardUnfocus($event) {
|
|
473
|
+
if ($event.key === 'Escape') {
|
|
474
|
+
this.getFocus($event, null);
|
|
475
|
+
document.activeElement.blur();
|
|
476
|
+
this.$refs.wrapper.focus();
|
|
477
|
+
}
|
|
478
|
+
},
|
|
479
|
+
|
|
442
480
|
getParent() {
|
|
443
481
|
if (!this.mounted) {
|
|
444
482
|
return false;
|
|
@@ -500,6 +538,12 @@ export default {
|
|
|
500
538
|
outline: 1px solid transparent;
|
|
501
539
|
transition: outline 200ms ease;
|
|
502
540
|
|
|
541
|
+
&:focus {
|
|
542
|
+
box-shadow: 0 0 11px 1px var(--a-primary-transparent-25);
|
|
543
|
+
outline: 1px dashed var(--a-primary-transparent-50);
|
|
544
|
+
outline-offset: 2px;
|
|
545
|
+
}
|
|
546
|
+
|
|
503
547
|
&.apos-is-highlighted {
|
|
504
548
|
outline: 1px dashed var(--a-primary-transparent-50);
|
|
505
549
|
}
|
|
@@ -514,6 +558,7 @@ export default {
|
|
|
514
558
|
|
|
515
559
|
&.apos-is-ui-adjusted {
|
|
516
560
|
.apos-area-widget-controls--modify {
|
|
561
|
+
top: 0;
|
|
517
562
|
transform: translate3d(-10px, 50px, 0);
|
|
518
563
|
}
|
|
519
564
|
|
|
@@ -571,8 +616,9 @@ export default {
|
|
|
571
616
|
}
|
|
572
617
|
|
|
573
618
|
.apos-area-widget-controls--modify {
|
|
619
|
+
top: 50%;
|
|
574
620
|
right: 0;
|
|
575
|
-
transform: translate3d(-10px,
|
|
621
|
+
transform: translate3d(-10px, -50%, 0);
|
|
576
622
|
|
|
577
623
|
:deep(.apos-button-group__inner) {
|
|
578
624
|
border: 1px solid var(--a-primary-transparent-25);
|
|
@@ -597,6 +643,10 @@ export default {
|
|
|
597
643
|
color: var(--a-primary);
|
|
598
644
|
}
|
|
599
645
|
|
|
646
|
+
&:focus:not([disabled])::after {
|
|
647
|
+
background-color: transparent;
|
|
648
|
+
}
|
|
649
|
+
|
|
600
650
|
&[disabled] {
|
|
601
651
|
color: var(--a-base-6);
|
|
602
652
|
}
|
|
@@ -735,7 +785,9 @@ export default {
|
|
|
735
785
|
color: var(--a-primary-dark-10);
|
|
736
786
|
|
|
737
787
|
&:hover, &:active, &:focus {
|
|
738
|
-
|
|
788
|
+
.apos-button__content {
|
|
789
|
+
color: var(--a-primary);
|
|
790
|
+
}
|
|
739
791
|
}
|
|
740
792
|
}
|
|
741
793
|
|