apostrophe 4.7.2 → 4.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.
- package/CHANGELOG.md +32 -2
- package/index.js +1 -1
- package/lib/moog.js +1 -1
- package/modules/@apostrophecms/admin-bar/index.js +61 -2
- package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextBreakpointPreviewMode.vue +166 -0
- package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextTitle.vue +25 -0
- package/modules/@apostrophecms/area/ui/apos/components/AposAreaEditor.vue +3 -12
- package/modules/@apostrophecms/asset/index.js +41 -1
- package/modules/@apostrophecms/asset/lib/globalIcons.js +6 -0
- package/modules/@apostrophecms/asset/lib/webpack/apos/webpack.scss.js +16 -16
- package/modules/@apostrophecms/asset/lib/webpack/media-to-container-queries-loader.js +94 -0
- package/modules/@apostrophecms/asset/lib/webpack/src/webpack.scss.js +12 -0
- package/modules/@apostrophecms/attachment/index.js +8 -1
- package/modules/@apostrophecms/command-menu/ui/apos/components/AposCommandMenuKey.vue +1 -1
- package/modules/@apostrophecms/command-menu/ui/apos/components/AposCommandMenuShortcut.vue +5 -2
- package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocEditor.vue +1 -19
- package/modules/@apostrophecms/i18n/i18n/en.json +12 -1
- package/modules/@apostrophecms/image/ui/apos/components/AposMediaUploader.vue +9 -18
- package/modules/@apostrophecms/migration/index.js +20 -13
- package/modules/@apostrophecms/migration/lib/addMissingSchemaFields.js +182 -0
- package/modules/@apostrophecms/page/index.js +4 -0
- package/modules/@apostrophecms/piece-type/ui/apos/components/AposRelationshipEditor.vue +2 -7
- package/modules/@apostrophecms/rich-text-widget/index.js +66 -0
- package/modules/@apostrophecms/schema/index.js +20 -29
- package/modules/@apostrophecms/schema/lib/addFieldTypes.js +2 -27
- package/modules/@apostrophecms/schema/lib/newInstance.js +36 -0
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputArray.vue +238 -80
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputWrapper.vue +34 -24
- package/modules/@apostrophecms/schema/ui/apos/logic/AposArrayEditor.js +2 -7
- package/modules/@apostrophecms/schema/ui/apos/logic/AposInputArray.js +207 -44
- package/modules/@apostrophecms/schema/ui/apos/logic/AposInputRelationship.js +3 -14
- package/modules/@apostrophecms/schema/ui/apos/scss/AposInputArray.scss +288 -105
- package/modules/@apostrophecms/template/views/outerLayoutBase.html +1 -0
- package/modules/@apostrophecms/ui/ui/apos/components/AposButton.vue +2 -1
- package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenu.vue +9 -6
- package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenuDialog.vue +8 -6
- package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenuItem.vue +52 -51
- package/modules/@apostrophecms/ui/ui/apos/components/AposFile.vue +16 -6
- package/modules/@apostrophecms/ui/ui/apos/components/AposIndicator.vue +1 -0
- package/modules/@apostrophecms/ui/ui/apos/components/AposLocalePicker.vue +6 -4
- package/modules/@apostrophecms/ui/ui/apos/components/AposSlatList.vue +6 -2
- package/modules/@apostrophecms/ui/ui/apos/components/AposTagApply.vue +8 -6
- package/modules/@apostrophecms/ui/ui/apos/scss/global/_breakpoint_preview.scss +38 -0
- package/modules/@apostrophecms/ui/ui/apos/scss/global/_tables.scss +3 -1
- package/modules/@apostrophecms/ui/ui/apos/scss/global/import-all.scss +1 -0
- package/modules/@apostrophecms/widget-type/ui/apos/components/AposWidgetEditor.vue +2 -13
- package/package.json +3 -2
- package/test/add-missing-schema-fields.js +323 -0
- package/test/pages.js +10 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,32 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 4.
|
|
3
|
+
## 4.8.1 (2024-10-09)
|
|
4
|
+
|
|
5
|
+
* Correct a race condition that can cause a crash at startup when custom `uploadfs` options are present in some environments.
|
|
6
|
+
|
|
7
|
+
## 4.8.0 (2024-10-03)
|
|
8
|
+
|
|
9
|
+
### Adds
|
|
10
|
+
|
|
11
|
+
* Adds a mobile preview feature to the admin UI. The feature can be enabled using the `@apostrophecms/asset` module's new `breakpointPreviewMode` option. Once enabled, the asset build process will duplicate existing media queries as container queries. There are some limitations in the equivalence between media queries and container queries. You can refer to the [CSS @container at-rule](https://developer.mozilla.org/en-US/docs/Web/CSS/@container) documentation for more information. You can also enable `breakpointPreviewMode.debug` to be notified in the console when the build encounters an unsupported media query.
|
|
12
|
+
* Apostrophe now automatically adds the appropriate default values for new properties in the schema, even for existing documents in the database. This is done automatically during the migration phase of startup.
|
|
13
|
+
* Adds focus states for media library's Uploader tile.
|
|
14
|
+
* Adds focus states file attachment's input UI.
|
|
15
|
+
* Simplified importing rich text widgets via the REST API. If you you have HTML that contains `img` tags pointing to existing images, you can now import them all quickly. When supplying the rich text widget object, include an `import` property with an `html` subproperty, rather than the usual `content` property. You can optionally provide a `baseUrl` subproperty as well. Any images present in `html` will be imported automatically and the correct `figure` tags will be added to the new rich text widget, along with any other markup acceptable to the widget's configuration.
|
|
16
|
+
|
|
17
|
+
### Changes
|
|
18
|
+
|
|
19
|
+
* The various implementations of `newInstance` found in Apostrophe, e.g. for widgets, array items, relationship fields and documents themselves, have been consolidated in one implementation. The same code is now reused both on the front and the back end, ensuring the same result without the need to introduce additional back end API calls.
|
|
4
20
|
|
|
5
21
|
### Fixes
|
|
6
22
|
|
|
7
|
-
*
|
|
23
|
+
* Apostrophe's migration logic is no longer executed twice on every startup and three times in the migration task. It is executed exactly once, always at the same point in the startup process. This bug did not cause significant performance issues because migrations were always only executed once, but there is a small performance improvement due to not checking for them more than once.
|
|
24
|
+
* The `@apostrophecms/page` module APIs no longer allow a page to become a child of itself. Thanks to [Maarten Marx](https://github.com/Pixelguymm) for reporting the issue.
|
|
25
|
+
* Uploaded SVGs now permit `<use>` tags granted their `xlink:href` property is a local reference and begins with the `#` character. This improves SVG support while mitgating XSS vulnerabilities.
|
|
26
|
+
* Default properties of object fields present in a widget now populate correctly even if never focused in the editor.
|
|
27
|
+
* Fixed the "choices" query builder to correctly support dynamic choices, ensuring compatibility with the [`piecesFilters`](https://docs.apostrophecms.org/reference/modules/piece-page-type.html#piecesfilters) feature when using dynamic choices.
|
|
28
|
+
* Fix a reordering issue for arrays when dragging and dropping items in the admin UI.
|
|
29
|
+
* The inline array item extract the label now using `title` as `titleField` value by default (consistent with the Slat list).
|
|
8
30
|
|
|
9
31
|
## 4.7.1 (2024-09-20)
|
|
10
32
|
|
|
@@ -14,6 +36,10 @@
|
|
|
14
36
|
|
|
15
37
|
## 4.7.0 (2024-09-05)
|
|
16
38
|
|
|
39
|
+
### Changes
|
|
40
|
+
|
|
41
|
+
* UI and UX of inline arrays and their table styles
|
|
42
|
+
|
|
17
43
|
### Adds
|
|
18
44
|
|
|
19
45
|
* To aid debugging, when a file extension is unacceptable as an Apostrophe attachment the rejected extension is now printed as part of the error message.
|
|
@@ -51,6 +77,9 @@ This resolves the issue for new uploads.
|
|
|
51
77
|
* Fix widget focus state so that the in-context Add Content menu stays visible during animation.
|
|
52
78
|
* Fix UI of areas in schemas so that their context menus are layered overtop sibling schema fields UI.
|
|
53
79
|
|
|
80
|
+
### Removes
|
|
81
|
+
* Inline array option for `alwaysOpen` replaced with UI toggles
|
|
82
|
+
|
|
54
83
|
## 4.6.0 (2024-08-08)
|
|
55
84
|
|
|
56
85
|
### Adds
|
|
@@ -78,6 +107,7 @@ The shape of the relationship field is still validated.
|
|
|
78
107
|
|
|
79
108
|
### Fixes
|
|
80
109
|
|
|
110
|
+
* Fixes the rendering of conditional fields in arrays where the `inline: true` option is used.
|
|
81
111
|
* Fixes the rich text link tool's detection and display of the Remove Link button for removing existing links
|
|
82
112
|
* Fixes the rich text link tool's detection and display of Apostrophe Page relationship field.
|
|
83
113
|
* Overriding standard Vue.js components with `editorModal` and `managerModal` are now applied all the time.
|
package/index.js
CHANGED
|
@@ -300,7 +300,7 @@ async function apostrophe(options, telemetry, rootSpan) {
|
|
|
300
300
|
self.apos.schema.validateAllSchemas();
|
|
301
301
|
self.apos.schema.registerAllSchemas();
|
|
302
302
|
await self.apos.lock.withLock('@apostrophecms/migration:migrate', async () => {
|
|
303
|
-
await self.apos.migration.migrate();
|
|
303
|
+
await self.apos.migration.migrate(self.argv);
|
|
304
304
|
// Inserts the global doc in the default locale if it does not exist; same for other
|
|
305
305
|
// singleton piece types registered by other modules
|
|
306
306
|
for (const module of Object.values(self.modules)) {
|
package/lib/moog.js
CHANGED
|
@@ -170,7 +170,7 @@ module.exports = function(options) {
|
|
|
170
170
|
}
|
|
171
171
|
for (const key of Object.keys(step)) {
|
|
172
172
|
if (!(validKeys.includes(key) || cascades.includes(key))) {
|
|
173
|
-
const message = upgradeHints[key] || `${key} is not a valid top level property for an Apostrophe
|
|
173
|
+
const message = upgradeHints[key] || `${key} is not a valid top level property for an Apostrophe module. Make sure you nest regular module options in the "options" property.`;
|
|
174
174
|
throw `${clarifyModuleName(step.__meta.name)}: ${message}`;
|
|
175
175
|
}
|
|
176
176
|
}
|
|
@@ -14,6 +14,56 @@ module.exports = {
|
|
|
14
14
|
pageTree: true
|
|
15
15
|
},
|
|
16
16
|
commands(self) {
|
|
17
|
+
const breakpointPreviewModeScreens = (
|
|
18
|
+
self.apos.asset.options.breakpointPreviewMode?.enable &&
|
|
19
|
+
self.apos.asset.options.breakpointPreviewMode?.screens
|
|
20
|
+
) || {};
|
|
21
|
+
const breakpointPreviewModeCommands = {
|
|
22
|
+
[`${self.__meta.name}:toggle-breakpoint-preview-mode:exit`]: {
|
|
23
|
+
type: 'item',
|
|
24
|
+
label: {
|
|
25
|
+
key: 'apostrophe:commandMenuToggleBreakpointPreviewMode',
|
|
26
|
+
breakpoint: '$t(apostrophe:breakpointPreviewExit)'
|
|
27
|
+
},
|
|
28
|
+
action: {
|
|
29
|
+
type: 'command-menu-admin-bar-toggle-breakpoint-preview-mode',
|
|
30
|
+
payload: {
|
|
31
|
+
mode: null,
|
|
32
|
+
width: null,
|
|
33
|
+
height: null
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
shortcut: 'P,0'
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
let index = 1;
|
|
40
|
+
for (const [ name, screen ] of Object.entries(breakpointPreviewModeScreens)) {
|
|
41
|
+
// Up to 9 shortcuts available
|
|
42
|
+
if (index === 9) {
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
breakpointPreviewModeCommands[`${self.__meta.name}:toggle-breakpoint-preview-mode:${name}`] = {
|
|
47
|
+
type: 'item',
|
|
48
|
+
label: {
|
|
49
|
+
key: 'apostrophe:commandMenuToggleBreakpointPreviewMode',
|
|
50
|
+
breakpoint: `$t(${screen.label})`
|
|
51
|
+
},
|
|
52
|
+
action: {
|
|
53
|
+
type: 'command-menu-admin-bar-toggle-breakpoint-preview-mode',
|
|
54
|
+
payload: {
|
|
55
|
+
mode: name,
|
|
56
|
+
label: `$t(${screen.label})`,
|
|
57
|
+
width: screen.width,
|
|
58
|
+
height: screen.height
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
shortcut: `P,${index}`
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
index += 1;
|
|
65
|
+
};
|
|
66
|
+
|
|
17
67
|
return {
|
|
18
68
|
add: {
|
|
19
69
|
[`${self.__meta.name}:undo`]: {
|
|
@@ -63,7 +113,8 @@ module.exports = {
|
|
|
63
113
|
type: 'command-menu-admin-bar-toggle-publish-draft'
|
|
64
114
|
},
|
|
65
115
|
shortcut: 'Ctrl+Shift+D Meta+Shift+D'
|
|
66
|
-
}
|
|
116
|
+
},
|
|
117
|
+
...breakpointPreviewModeCommands
|
|
67
118
|
},
|
|
68
119
|
modal: {
|
|
69
120
|
default: {
|
|
@@ -80,7 +131,8 @@ module.exports = {
|
|
|
80
131
|
label: 'apostrophe:commandMenuMode',
|
|
81
132
|
commands: [
|
|
82
133
|
`${self.__meta.name}:toggle-edit-preview-mode`,
|
|
83
|
-
`${self.__meta.name}:toggle-published-draft-document
|
|
134
|
+
`${self.__meta.name}:toggle-published-draft-document`,
|
|
135
|
+
...Object.keys(breakpointPreviewModeCommands)
|
|
84
136
|
]
|
|
85
137
|
}
|
|
86
138
|
}
|
|
@@ -355,6 +407,13 @@ module.exports = {
|
|
|
355
407
|
aposLocale: context.aposLocale,
|
|
356
408
|
aposDocId: context.aposDocId
|
|
357
409
|
},
|
|
410
|
+
breakpointPreviewMode: self.apos.asset.options.breakpointPreviewMode ||
|
|
411
|
+
{
|
|
412
|
+
enable: false,
|
|
413
|
+
debug: false,
|
|
414
|
+
resizable: false,
|
|
415
|
+
screens: {}
|
|
416
|
+
},
|
|
358
417
|
// Base API URL appropriate to the context document
|
|
359
418
|
contextBar: context && self.apos.doc.getManager(context.type).options.contextBar,
|
|
360
419
|
showAdminBar: self.getShowAdminBar(req),
|
package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextBreakpointPreviewMode.vue
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
data-apos-test="breakpointPreviewMode"
|
|
4
|
+
class="apos-admin-bar__breakpoint-preview-mode"
|
|
5
|
+
>
|
|
6
|
+
<component
|
|
7
|
+
:is="'AposButton'"
|
|
8
|
+
v-for="(screen, name) in screens"
|
|
9
|
+
:key="name"
|
|
10
|
+
:data-apos-test="`breakpointPreviewMode:${name}`"
|
|
11
|
+
:modifiers="['small', 'no-motion']"
|
|
12
|
+
:label="screen.label"
|
|
13
|
+
:title="$t(screen.label)"
|
|
14
|
+
:icon="screen.icon"
|
|
15
|
+
:icon-only="true"
|
|
16
|
+
type="subtle"
|
|
17
|
+
class="apos-admin-bar__breakpoint-preview-mode-button"
|
|
18
|
+
:class="{ 'apos-is-active': mode === name }"
|
|
19
|
+
@click="toggleBreakpointPreviewMode({ mode: name, label: screen.label, width: screen.width, height: screen.height })"
|
|
20
|
+
/>
|
|
21
|
+
</div>
|
|
22
|
+
</template>
|
|
23
|
+
<script>
|
|
24
|
+
|
|
25
|
+
export default {
|
|
26
|
+
name: 'TheAposContextBreakpointPreviewMode',
|
|
27
|
+
props: {
|
|
28
|
+
// { screenName: { label: string, width: string, height: string, icon: string } }
|
|
29
|
+
screens: {
|
|
30
|
+
type: Object,
|
|
31
|
+
validator(value, props) {
|
|
32
|
+
return Object.values(value).every(screen =>
|
|
33
|
+
typeof screen.label === 'string' &&
|
|
34
|
+
typeof screen.width === 'string' &&
|
|
35
|
+
typeof screen.height === 'string' &&
|
|
36
|
+
typeof screen.icon === 'string'
|
|
37
|
+
);
|
|
38
|
+
},
|
|
39
|
+
default: () => {
|
|
40
|
+
return {};
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
resizable: {
|
|
44
|
+
type: Boolean,
|
|
45
|
+
default: false
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
emits: [ 'switch-breakpoint-preview-mode', 'reset-breakpoint-preview-mode' ],
|
|
49
|
+
data() {
|
|
50
|
+
return {
|
|
51
|
+
mode: null,
|
|
52
|
+
originalBodyBackground: null
|
|
53
|
+
};
|
|
54
|
+
},
|
|
55
|
+
mounted() {
|
|
56
|
+
apos.bus.$on('command-menu-admin-bar-toggle-breakpoint-preview-mode', this.toggleBreakpointPreviewMode);
|
|
57
|
+
|
|
58
|
+
this.originalBodyBackground = window.getComputedStyle(document.querySelector('body'))?.background ||
|
|
59
|
+
'#fff';
|
|
60
|
+
|
|
61
|
+
const state = this.loadState();
|
|
62
|
+
if (state.mode) {
|
|
63
|
+
this.toggleBreakpointPreviewMode(state);
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
unmounted() {
|
|
67
|
+
apos.bus.$off('command-menu-admin-bar-toggle-breakpoint-preview-mode', this.toggleBreakpointPreviewMode);
|
|
68
|
+
},
|
|
69
|
+
methods: {
|
|
70
|
+
switchBreakpointPreviewMode({
|
|
71
|
+
mode,
|
|
72
|
+
label,
|
|
73
|
+
width,
|
|
74
|
+
height
|
|
75
|
+
}) {
|
|
76
|
+
document.querySelector('body').setAttribute('data-breakpoint-preview-mode', mode);
|
|
77
|
+
document.querySelector('[data-apos-refreshable]').setAttribute('data-resizable', this.resizable);
|
|
78
|
+
document.querySelector('[data-apos-refreshable]').setAttribute('data-label', this.$t(label));
|
|
79
|
+
document.querySelector('[data-apos-refreshable]').style.width = width;
|
|
80
|
+
document.querySelector('[data-apos-refreshable]').style.height = height;
|
|
81
|
+
document.querySelector('[data-apos-refreshable]').style.background = this.originalBodyBackground;
|
|
82
|
+
|
|
83
|
+
this.mode = mode;
|
|
84
|
+
this.$emit('switch-breakpoint-preview-mode', {
|
|
85
|
+
mode,
|
|
86
|
+
label,
|
|
87
|
+
width,
|
|
88
|
+
height
|
|
89
|
+
});
|
|
90
|
+
this.saveState({
|
|
91
|
+
mode,
|
|
92
|
+
label,
|
|
93
|
+
width,
|
|
94
|
+
height
|
|
95
|
+
});
|
|
96
|
+
},
|
|
97
|
+
toggleBreakpointPreviewMode({
|
|
98
|
+
mode,
|
|
99
|
+
label,
|
|
100
|
+
width,
|
|
101
|
+
height
|
|
102
|
+
}) {
|
|
103
|
+
if (this.mode === mode || mode === null) {
|
|
104
|
+
document.querySelector('body').removeAttribute('data-breakpoint-preview-mode');
|
|
105
|
+
document.querySelector('[data-apos-refreshable]').removeAttribute('data-resizable');
|
|
106
|
+
document.querySelector('[data-apos-refreshable]').removeAttribute('data-label');
|
|
107
|
+
document.querySelector('[data-apos-refreshable]').style.removeProperty('width');
|
|
108
|
+
document.querySelector('[data-apos-refreshable]').style.removeProperty('height');
|
|
109
|
+
document.querySelector('[data-apos-refreshable]').style.removeProperty('background');
|
|
110
|
+
|
|
111
|
+
this.mode = null;
|
|
112
|
+
this.$emit('reset-breakpoint-preview-mode');
|
|
113
|
+
this.saveState({ mode: this.mode });
|
|
114
|
+
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
this.switchBreakpointPreviewMode({
|
|
119
|
+
mode,
|
|
120
|
+
label,
|
|
121
|
+
width,
|
|
122
|
+
height
|
|
123
|
+
});
|
|
124
|
+
},
|
|
125
|
+
loadState() {
|
|
126
|
+
return JSON.parse(sessionStorage.getItem('aposBreakpointPreviewMode') || '{}');
|
|
127
|
+
},
|
|
128
|
+
saveState({
|
|
129
|
+
mode = null,
|
|
130
|
+
label = null,
|
|
131
|
+
width = null,
|
|
132
|
+
height = null
|
|
133
|
+
} = {}) {
|
|
134
|
+
const state = this.loadState();
|
|
135
|
+
if (state.mode !== mode) {
|
|
136
|
+
sessionStorage.setItem(
|
|
137
|
+
'aposBreakpointPreviewMode',
|
|
138
|
+
JSON.stringify({
|
|
139
|
+
mode,
|
|
140
|
+
label,
|
|
141
|
+
width,
|
|
142
|
+
height
|
|
143
|
+
})
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
</script>
|
|
150
|
+
<style lang="scss" scoped>
|
|
151
|
+
.apos-admin-bar__breakpoint-preview-mode {
|
|
152
|
+
display: flex;
|
|
153
|
+
gap: $spacing-half;
|
|
154
|
+
margin-left: $spacing-double;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.apos-admin-bar__breakpoint-preview-mode-button {
|
|
158
|
+
&.apos-is-active {
|
|
159
|
+
color: var(--a-text-primary);
|
|
160
|
+
text-decoration: none;
|
|
161
|
+
background-color: var(--a-base-10);
|
|
162
|
+
border-radius: var(--a-border-radius);
|
|
163
|
+
outline: 1px solid var(--a-base-7);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
</style>
|
|
@@ -50,6 +50,13 @@
|
|
|
50
50
|
:tooltip="tooltip"
|
|
51
51
|
:modifiers="modifiers"
|
|
52
52
|
/>
|
|
53
|
+
<TheAposContextBreakpointPreviewMode
|
|
54
|
+
v-if="isBreakpointPreviewModeEnabled"
|
|
55
|
+
:screens="breakpointPreviewModeScreens"
|
|
56
|
+
:resizable="breakpointPreviewModeResizable"
|
|
57
|
+
@switch-breakpoint-preview-mode="addContextLabel"
|
|
58
|
+
@reset-breakpoint-preview-mode="removeContextLabel"
|
|
59
|
+
/>
|
|
53
60
|
</span>
|
|
54
61
|
</transition-group>
|
|
55
62
|
</template>
|
|
@@ -94,6 +101,15 @@ export default {
|
|
|
94
101
|
isUnpublished() {
|
|
95
102
|
return !this.context.lastPublishedAt;
|
|
96
103
|
},
|
|
104
|
+
isBreakpointPreviewModeEnabled() {
|
|
105
|
+
return this.moduleOptions.breakpointPreviewMode.enable || false;
|
|
106
|
+
},
|
|
107
|
+
breakpointPreviewModeScreens() {
|
|
108
|
+
return this.moduleOptions.breakpointPreviewMode.screens || {};
|
|
109
|
+
},
|
|
110
|
+
breakpointPreviewModeResizable() {
|
|
111
|
+
return this.moduleOptions.breakpointPreviewMode.resizable || false;
|
|
112
|
+
},
|
|
97
113
|
docTooltip() {
|
|
98
114
|
return {
|
|
99
115
|
key: 'apostrophe:lastUpdatedBy',
|
|
@@ -142,6 +158,15 @@ export default {
|
|
|
142
158
|
},
|
|
143
159
|
switchDraftMode(mode) {
|
|
144
160
|
this.$emit('switch-draft-mode', mode);
|
|
161
|
+
},
|
|
162
|
+
addContextLabel({
|
|
163
|
+
label
|
|
164
|
+
}) {
|
|
165
|
+
document.querySelector('[data-apos-context-label]')
|
|
166
|
+
?.replaceChildren(document.createTextNode(this.$t(label)));
|
|
167
|
+
},
|
|
168
|
+
removeContextLabel() {
|
|
169
|
+
document.querySelector('[data-apos-context-label]')?.replaceChildren();
|
|
145
170
|
}
|
|
146
171
|
}
|
|
147
172
|
};
|
|
@@ -75,6 +75,7 @@
|
|
|
75
75
|
import { createId } from '@paralleldrive/cuid2';
|
|
76
76
|
import { klona } from 'klona';
|
|
77
77
|
import AposThemeMixin from 'Modules/@apostrophecms/ui/mixins/AposThemeMixin';
|
|
78
|
+
import newInstance from 'apostrophe/modules/@apostrophecms/schema/lib/newInstance.js';
|
|
78
79
|
|
|
79
80
|
export default {
|
|
80
81
|
name: 'AposAreaEditor',
|
|
@@ -595,21 +596,11 @@ export default {
|
|
|
595
596
|
// Return a new widget object in which defaults are fully populated,
|
|
596
597
|
// especially valid sub-area objects, so that nested edits work on the page
|
|
597
598
|
newWidget(type) {
|
|
599
|
+
const schema = apos.modules[apos.area.widgetManagers[type]].schema;
|
|
598
600
|
const widget = {
|
|
601
|
+
...newInstance(schema),
|
|
599
602
|
type
|
|
600
603
|
};
|
|
601
|
-
const schema = apos.modules[apos.area.widgetManagers[type]].schema;
|
|
602
|
-
schema.forEach(field => {
|
|
603
|
-
if (field.type === 'area') {
|
|
604
|
-
widget[field.name] = {
|
|
605
|
-
_id: createId(),
|
|
606
|
-
metaType: 'area',
|
|
607
|
-
items: []
|
|
608
|
-
};
|
|
609
|
-
} else {
|
|
610
|
-
widget[field.name] = field.def ? klona(field.def) : field.def;
|
|
611
|
-
}
|
|
612
|
-
});
|
|
613
604
|
return widget;
|
|
614
605
|
}
|
|
615
606
|
}
|
|
@@ -44,7 +44,47 @@ module.exports = {
|
|
|
44
44
|
rebundleModules: undefined,
|
|
45
45
|
// In case of external front end like Astro, this option allows to
|
|
46
46
|
// disable the build of the public UI assets.
|
|
47
|
-
publicBundle: true
|
|
47
|
+
publicBundle: true,
|
|
48
|
+
// Breakpoint preview in the admin UI.
|
|
49
|
+
// NOTE: the whole breakpointPreviewMode option must be carried over
|
|
50
|
+
// to the project for overrides to work properly.
|
|
51
|
+
// Nested object options are not deep merged in Apostrophe.
|
|
52
|
+
breakpointPreviewMode: {
|
|
53
|
+
// Enable breakpoint preview mode
|
|
54
|
+
enable: true,
|
|
55
|
+
// Warn during build about unsupported media queries.
|
|
56
|
+
debug: false,
|
|
57
|
+
// If we can resize the preview container?
|
|
58
|
+
resizable: false,
|
|
59
|
+
// Screens with icons
|
|
60
|
+
// For adding icons, please refer to the icons documentation
|
|
61
|
+
// https://docs.apostrophecms.org/reference/module-api/module-overview.html#icons
|
|
62
|
+
screens: {
|
|
63
|
+
desktop: {
|
|
64
|
+
label: 'apostrophe:breakpointPreviewDesktop',
|
|
65
|
+
width: '1440px',
|
|
66
|
+
height: '900px',
|
|
67
|
+
icon: 'monitor-icon'
|
|
68
|
+
},
|
|
69
|
+
tablet: {
|
|
70
|
+
label: 'apostrophe:breakpointPreviewTablet',
|
|
71
|
+
width: '1024px',
|
|
72
|
+
height: '768px',
|
|
73
|
+
icon: 'tablet-icon'
|
|
74
|
+
},
|
|
75
|
+
mobile: {
|
|
76
|
+
label: 'apostrophe:breakpointPreviewMobile',
|
|
77
|
+
width: '414px',
|
|
78
|
+
height: '896px',
|
|
79
|
+
icon: 'cellphone-icon'
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
// Transform method used on media feature
|
|
83
|
+
// Can be either:
|
|
84
|
+
// - (mediaFeature) => { return mediaFeature.replaceAll('xx', 'yy'); }
|
|
85
|
+
// - null
|
|
86
|
+
transform: null
|
|
87
|
+
}
|
|
48
88
|
},
|
|
49
89
|
|
|
50
90
|
async init(self) {
|
|
@@ -11,12 +11,15 @@ module.exports = {
|
|
|
11
11
|
'apple-keyboard-shift': 'AppleKeyboardShift',
|
|
12
12
|
'archive-arrow-down-icon': 'ArchiveArrowDown',
|
|
13
13
|
'archive-arrow-up-icon': 'ArchiveArrowUp',
|
|
14
|
+
'arrow-collapse-vertical-icon': 'ArrowCollapseVertical',
|
|
14
15
|
'arrow-down-icon': 'ArrowDown',
|
|
16
|
+
'arrow-expand-vertical-icon': 'ArrowExpandVertical',
|
|
15
17
|
'arrow-left-icon': 'ArrowLeft',
|
|
16
18
|
'arrow-right-icon': 'ArrowRight',
|
|
17
19
|
'arrow-up-icon': 'ArrowUp',
|
|
18
20
|
'binoculars-icon': 'Binoculars',
|
|
19
21
|
'calendar-icon': 'Calendar',
|
|
22
|
+
'cellphone-icon': 'Cellphone',
|
|
20
23
|
'check-all-icon': 'CheckAll',
|
|
21
24
|
'check-bold-icon': 'CheckBold',
|
|
22
25
|
'check-circle-icon': 'CheckCircle',
|
|
@@ -96,6 +99,7 @@ module.exports = {
|
|
|
96
99
|
'menu-down-icon': 'MenuDown',
|
|
97
100
|
'minus-box-icon': 'MinusBox',
|
|
98
101
|
'minus-icon': 'Minus',
|
|
102
|
+
'monitor-icon': 'Monitor',
|
|
99
103
|
'paperclip-icon': 'Paperclip',
|
|
100
104
|
'pencil-icon': 'Pencil',
|
|
101
105
|
'phone-icon': 'Phone',
|
|
@@ -105,8 +109,10 @@ module.exports = {
|
|
|
105
109
|
'refresh-icon': 'Refresh',
|
|
106
110
|
'shape-icon': 'Shape',
|
|
107
111
|
'sign-text-icon': 'SignText',
|
|
112
|
+
'tablet-icon': 'Tablet',
|
|
108
113
|
'tag-icon': 'Tag',
|
|
109
114
|
'text-box-icon': 'TextBox',
|
|
115
|
+
'text-box-multiple-icon': 'TextBoxMultiple',
|
|
110
116
|
'text-box-remove-icon': 'TextBoxRemove',
|
|
111
117
|
'trash-can-icon': 'TrashCan',
|
|
112
118
|
'trash-can-outline-icon': 'TrashCanOutline',
|
|
@@ -1,4 +1,16 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
|
|
1
3
|
module.exports = (options, apos) => {
|
|
4
|
+
const mediaToContainerQueriesLoader = apos.asset.options.breakpointPreviewMode?.enable === true
|
|
5
|
+
? {
|
|
6
|
+
loader: path.resolve(__dirname, '../media-to-container-queries-loader.js'),
|
|
7
|
+
options: {
|
|
8
|
+
debug: apos.asset.options.breakpointPreviewMode?.debug === true,
|
|
9
|
+
transform: apos.asset.options.breakpointPreviewMode?.transform || null
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
: '';
|
|
13
|
+
|
|
2
14
|
return {
|
|
3
15
|
module: {
|
|
4
16
|
rules: [
|
|
@@ -6,28 +18,16 @@ module.exports = (options, apos) => {
|
|
|
6
18
|
test: /\.css$/,
|
|
7
19
|
use: [
|
|
8
20
|
'vue-style-loader',
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
loader: 'css-loader',
|
|
12
|
-
options: {
|
|
13
|
-
esModule: false,
|
|
14
|
-
sourceMap: true
|
|
15
|
-
}
|
|
16
|
-
}
|
|
21
|
+
mediaToContainerQueriesLoader,
|
|
22
|
+
'css-loader'
|
|
17
23
|
]
|
|
18
24
|
},
|
|
19
|
-
// https://github.com/vuejs/vue-style-loader/issues/46#issuecomment-670624576
|
|
20
25
|
{
|
|
21
26
|
test: /\.s[ac]ss$/,
|
|
22
27
|
use: [
|
|
23
28
|
'vue-style-loader',
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
options: {
|
|
27
|
-
esModule: false,
|
|
28
|
-
sourceMap: true
|
|
29
|
-
}
|
|
30
|
-
},
|
|
29
|
+
mediaToContainerQueriesLoader,
|
|
30
|
+
'css-loader',
|
|
31
31
|
{
|
|
32
32
|
loader: 'postcss-loader',
|
|
33
33
|
options: {
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
const postcss = require('postcss');
|
|
2
|
+
|
|
3
|
+
module.exports = function (source) {
|
|
4
|
+
const schema = {
|
|
5
|
+
title: 'Media to Container Queries Loader options',
|
|
6
|
+
type: 'object',
|
|
7
|
+
properties: {
|
|
8
|
+
debug: {
|
|
9
|
+
type: 'boolean'
|
|
10
|
+
},
|
|
11
|
+
transform: {
|
|
12
|
+
anyOf: [
|
|
13
|
+
{ type: 'null' },
|
|
14
|
+
{ instanceof: 'Function' }
|
|
15
|
+
]
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
const options = this.getOptions(schema);
|
|
20
|
+
|
|
21
|
+
const mediaQueryRegex = /@media[^{]*{([\s\S]*?})\s*(\\n)*}/g;
|
|
22
|
+
|
|
23
|
+
const convertToContainerQuery = (mediaFeature) => {
|
|
24
|
+
// NOTE: media queries does not work with the combo
|
|
25
|
+
// - min-width, max-width, min-height, max-height
|
|
26
|
+
// - lower than equal, greater than equal
|
|
27
|
+
const DESCRIPTORS = [
|
|
28
|
+
'min-width',
|
|
29
|
+
'max-width',
|
|
30
|
+
'min-height',
|
|
31
|
+
'max-height'
|
|
32
|
+
];
|
|
33
|
+
const OPERATORS = [
|
|
34
|
+
'>=',
|
|
35
|
+
'<='
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
const containerFeature = typeof options.transform === 'function'
|
|
39
|
+
? options.transform(mediaFeature)
|
|
40
|
+
: mediaFeature;
|
|
41
|
+
|
|
42
|
+
if (
|
|
43
|
+
options.debug &&
|
|
44
|
+
DESCRIPTORS.some(descriptor => containerFeature.includes(descriptor)) &&
|
|
45
|
+
OPERATORS.some(operator => containerFeature.includes(operator))
|
|
46
|
+
) {
|
|
47
|
+
console.warn('[mediaToContainerQueryLoader] Unsupported media query', containerFeature);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return containerFeature;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// Prepend container query to media queries
|
|
54
|
+
const modifiedSource = source.replace(mediaQueryRegex, (match) => {
|
|
55
|
+
const root = postcss.parse(match.replaceAll(/(?<!\\)\\[frntv]/g, ''));
|
|
56
|
+
root.walkAtRules('media', atRule => {
|
|
57
|
+
if (
|
|
58
|
+
atRule.params.includes('print') &&
|
|
59
|
+
(!atRule.params.includes('all') || !atRule.params.includes('screen'))
|
|
60
|
+
) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Container query
|
|
65
|
+
const containerAtRule = atRule.clone({
|
|
66
|
+
name: 'container',
|
|
67
|
+
params: convertToContainerQuery(atRule.params)
|
|
68
|
+
.replaceAll(/(only\s*)?(all|screen|print)(,)?(\s)*(and\s*)?/g, '')
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// Media query
|
|
72
|
+
// Only apply when data-breakpoint-preview-mode is not set
|
|
73
|
+
atRule.walkRules(rule => {
|
|
74
|
+
const newRule = rule.clone({
|
|
75
|
+
selectors: rule.selectors.map(selector => {
|
|
76
|
+
if (selector.startsWith('body')) {
|
|
77
|
+
return selector.replace('body', ':where(body:not([data-breakpoint-preview-mode]))');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return `:where(body:not([data-breakpoint-preview-mode])) ${selector}`;
|
|
81
|
+
})
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
rule.replaceWith(newRule);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
root.append(containerAtRule);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
return root.toString();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
return modifiedSource;
|
|
94
|
+
};
|
|
@@ -1,6 +1,17 @@
|
|
|
1
|
+
const path = require('path');
|
|
1
2
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
|
2
3
|
|
|
3
4
|
module.exports = (options, apos, srcBuildNames) => {
|
|
5
|
+
const mediaToContainerQueriesLoader = apos.asset.options.breakpointPreviewMode?.enable === true
|
|
6
|
+
? {
|
|
7
|
+
loader: path.resolve(__dirname, '../media-to-container-queries-loader.js'),
|
|
8
|
+
options: {
|
|
9
|
+
debug: apos.asset.options.breakpointPreviewMode?.debug === true,
|
|
10
|
+
transform: apos.asset.options.breakpointPreviewMode?.transform || null
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
: '';
|
|
14
|
+
|
|
4
15
|
return {
|
|
5
16
|
module: {
|
|
6
17
|
rules: [
|
|
@@ -9,6 +20,7 @@ module.exports = (options, apos, srcBuildNames) => {
|
|
|
9
20
|
use: [
|
|
10
21
|
// Instead of style-loader, to avoid FOUC
|
|
11
22
|
MiniCssExtractPlugin.loader,
|
|
23
|
+
mediaToContainerQueriesLoader,
|
|
12
24
|
// Parses CSS imports and make css-loader ignore urls. Urls will still be handled by webpack
|
|
13
25
|
{
|
|
14
26
|
loader: 'css-loader',
|