Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable by default, other tweaks #4741

Merged
merged 2 commits into from
Oct 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* Adds focus states for media library's Uploader tile
* Adds focus states file attachment's input UI
* 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.
* Add mobile preview feature to the admin UI. The feature can be enabled using the `@apostrophecms/asset` module new `devicePreviewMode` option. Once enabled, the asset build process will duplicate existing media queries as container queries. There are some limitations in the equivalence media queries / 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 `devicePreviewMode.debug` to be notified in the console when the build encounter an unsupported media query.
* Add mobile preview feature to the admin UI. The feature can be enabled using the `@apostrophecms/asset` module new `breakpointPreviewMode` option. Once enabled, the asset build process will duplicate existing media queries as container queries. There are some limitations in the equivalence media queries / 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 encounter an unsupported media query.

### Changes

Expand Down
32 changes: 16 additions & 16 deletions modules/@apostrophecms/admin-bar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,19 @@ module.exports = {
pageTree: true
},
commands(self) {
const devicePreviewModeScreens = (
self.apos.asset.options.devicePreviewMode?.enable &&
self.apos.asset.options.devicePreviewMode?.screens
const breakpointPreviewModeScreens = (
self.apos.asset.options.breakpointPreviewMode?.enable &&
self.apos.asset.options.breakpointPreviewMode?.screens
) || {};
const devicePreviewModeCommands = {
[`${self.__meta.name}:toggle-device-preview-mode:exit`]: {
const breakpointPreviewModeCommands = {
[`${self.__meta.name}:toggle-breakpoint-preview-mode:exit`]: {
type: 'item',
label: {
key: 'apostrophe:commandMenuToggleDevicePreviewMode',
device: '$t(apostrophe:devicePreviewExit)'
key: 'apostrophe:commandMenuToggleBreakpointPreviewMode',
breakpoint: '$t(apostrophe:breakpointPreviewExit)'
},
action: {
type: 'command-menu-admin-bar-toggle-device-preview-mode',
type: 'command-menu-admin-bar-toggle-breakpoint-preview-mode',
payload: {
mode: null,
width: null,
Expand All @@ -37,20 +37,20 @@ module.exports = {
}
};
let index = 1;
for (const [ name, screen ] of Object.entries(devicePreviewModeScreens)) {
for (const [ name, screen ] of Object.entries(breakpointPreviewModeScreens)) {
// Up to 9 shortcuts available
if (index === 9) {
break;
}

devicePreviewModeCommands[`${self.__meta.name}:toggle-device-preview-mode:${name}`] = {
breakpointPreviewModeCommands[`${self.__meta.name}:toggle-breakpoint-preview-mode:${name}`] = {
type: 'item',
label: {
key: 'apostrophe:commandMenuToggleDevicePreviewMode',
device: `$t(${screen.label})`
key: 'apostrophe:commandMenuToggleBreakpointPreviewMode',
breakpoint: `$t(${screen.label})`
},
action: {
type: 'command-menu-admin-bar-toggle-device-preview-mode',
type: 'command-menu-admin-bar-toggle-breakpoint-preview-mode',
payload: {
mode: name,
label: `$t(${screen.label})`,
Expand Down Expand Up @@ -114,7 +114,7 @@ module.exports = {
},
shortcut: 'Ctrl+Shift+D Meta+Shift+D'
},
...devicePreviewModeCommands
...breakpointPreviewModeCommands
},
modal: {
default: {
Expand All @@ -132,7 +132,7 @@ module.exports = {
commands: [
`${self.__meta.name}:toggle-edit-preview-mode`,
`${self.__meta.name}:toggle-published-draft-document`,
...Object.keys(devicePreviewModeCommands)
...Object.keys(breakpointPreviewModeCommands)
]
}
}
Expand Down Expand Up @@ -407,7 +407,7 @@ module.exports = {
aposLocale: context.aposLocale,
aposDocId: context.aposDocId
},
devicePreviewMode: self.apos.asset.options.devicePreviewMode ||
breakpointPreviewMode: self.apos.asset.options.breakpointPreviewMode ||
{
enable: false,
debug: false,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
<template>
<div
data-apos-test="devicePreviewMode"
class="apos-admin-bar__device-preview-mode"
data-apos-test="breakpointPreviewMode"
class="apos-admin-bar__breakpoint-preview-mode"
>
<component
:is="'AposButton'"
v-for="(screen, name) in screens"
:key="name"
:data-apos-test="`devicePreviewMode:${name}`"
:data-apos-test="`breakpointPreviewMode:${name}`"
:modifiers="['small', 'no-motion']"
:label="screen.label"
:title="$t(screen.label)"
:icon="screen.icon"
:icon-only="true"
type="subtle"
class="apos-admin-bar__device-preview-mode-button"
class="apos-admin-bar__breakpoint-preview-mode-button"
:class="{ 'apos-is-active': mode === name }"
@click="toggleDevicePreviewMode({ mode: name, label: screen.label, width: screen.width, height: screen.height })"
@click="toggleBreakpointPreviewMode({ mode: name, label: screen.label, width: screen.width, height: screen.height })"
/>
</div>
</template>
<script>

export default {
name: 'TheAposContextDevicePreview',
name: 'TheAposContextBreakpointPreviewMode',
props: {
// { screenName: { label: string, width: string, height: string, icon: string } }
screens: {
Expand All @@ -45,43 +45,43 @@ export default {
default: false
}
},
emits: [ 'switch-device-preview-mode', 'reset-device-preview-mode' ],
emits: [ 'switch-breakpoint-preview-mode', 'reset-breakpoint-preview-mode' ],
data() {
return {
mode: null,
originalBodyBackground: null
};
},
mounted() {
apos.bus.$on('command-menu-admin-bar-toggle-device-preview-mode', this.toggleDevicePreviewMode);
apos.bus.$on('command-menu-admin-bar-toggle-breakpoint-preview-mode', this.toggleBreakpointPreviewMode);

this.originalBodyBackground = window.getComputedStyle(document.querySelector('body'))?.background ||
'#fff';

const state = this.loadState();
if (state.mode) {
this.toggleDevicePreviewMode(state);
this.toggleBreakpointPreviewMode(state);
}
},
unmounted() {
apos.bus.$off('command-menu-admin-bar-toggle-device-preview-mode', this.toggleDevicePreviewMode);
apos.bus.$off('command-menu-admin-bar-toggle-breakpoint-preview-mode', this.toggleBreakpointPreviewMode);
},
methods: {
switchDevicePreviewMode({
switchBreakpointPreviewMode({
mode,
label,
width,
height
}) {
document.querySelector('body').setAttribute('data-device-preview-mode', mode);
document.querySelector('body').setAttribute('data-breakpoint-preview-mode', mode);
document.querySelector('[data-apos-refreshable]').setAttribute('data-resizable', this.resizable);
document.querySelector('[data-apos-refreshable]').setAttribute('data-label', this.$t(label));
document.querySelector('[data-apos-refreshable]').style.width = width;
document.querySelector('[data-apos-refreshable]').style.height = height;
document.querySelector('[data-apos-refreshable]').style.background = this.originalBodyBackground;

this.mode = mode;
this.$emit('switch-device-preview-mode', {
this.$emit('switch-breakpoint-preview-mode', {
mode,
label,
width,
Expand All @@ -94,36 +94,36 @@ export default {
height
});
},
toggleDevicePreviewMode({
toggleBreakpointPreviewMode({
mode,
label,
width,
height
}) {
if (this.mode === mode || mode === null) {
document.querySelector('body').removeAttribute('data-device-preview-mode');
document.querySelector('body').removeAttribute('data-breakpoint-preview-mode');
document.querySelector('[data-apos-refreshable]').removeAttribute('data-resizable');
document.querySelector('[data-apos-refreshable]').removeAttribute('data-label');
document.querySelector('[data-apos-refreshable]').style.removeProperty('width');
document.querySelector('[data-apos-refreshable]').style.removeProperty('height');
document.querySelector('[data-apos-refreshable]').style.removeProperty('background');

this.mode = null;
this.$emit('reset-device-preview-mode');
this.$emit('reset-breakpoint-preview-mode');
this.saveState({ mode: this.mode });

return;
}

this.switchDevicePreviewMode({
this.switchBreakpointPreviewMode({
mode,
label,
width,
height
});
},
loadState() {
return JSON.parse(sessionStorage.getItem('aposDevicePreviewMode') || '{}');
return JSON.parse(sessionStorage.getItem('aposBreakpointPreviewMode') || '{}');
},
saveState({
mode = null,
Expand All @@ -134,7 +134,7 @@ export default {
const state = this.loadState();
if (state.mode !== mode) {
sessionStorage.setItem(
'aposDevicePreviewMode',
'aposBreakpointPreviewMode',
JSON.stringify({
mode,
label,
Expand All @@ -148,13 +148,13 @@ export default {
};
</script>
<style lang="scss" scoped>
.apos-admin-bar__device-preview-mode {
.apos-admin-bar__breakpoint-preview-mode {
display: flex;
gap: $spacing-half;
margin-left: $spacing-double;
}

.apos-admin-bar__device-preview-mode-button {
.apos-admin-bar__breakpoint-preview-mode-button {
&.apos-is-active {
color: var(--a-text-primary);
text-decoration: none;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,12 @@
:tooltip="tooltip"
:modifiers="modifiers"
/>
<TheAposContextDevicePreviewMode
v-if="isDevicePreviewModeEnabled"
:screens="devicePreviewModeScreens"
:resizable="devicePreviewModeResizable"
@switch-device-preview-mode="addContextLabel"
@reset-device-preview-mode="removeContextLabel"
<TheAposContextBreakpointPreviewMode
v-if="isBreakpointPreviewModeEnabled"
:screens="breakpointPreviewModeScreens"
:resizable="breakpointPreviewModeResizable"
@switch-breakpoint-preview-mode="addContextLabel"
@reset-breakpoint-preview-mode="removeContextLabel"
/>
</span>
</transition-group>
Expand Down Expand Up @@ -101,14 +101,14 @@ export default {
isUnpublished() {
return !this.context.lastPublishedAt;
},
isDevicePreviewModeEnabled() {
return this.moduleOptions.devicePreviewMode.enable || false;
isBreakpointPreviewModeEnabled() {
return this.moduleOptions.breakpointPreviewMode.enable || false;
},
devicePreviewModeScreens() {
return this.moduleOptions.devicePreviewMode.screens || {};
breakpointPreviewModeScreens() {
return this.moduleOptions.breakpointPreviewMode.screens || {};
},
devicePreviewModeResizable() {
return this.moduleOptions.devicePreviewMode.resizable || false;
breakpointPreviewModeResizable() {
return this.moduleOptions.breakpointPreviewMode.resizable || false;
},
docTooltip() {
return {
Expand Down
24 changes: 12 additions & 12 deletions modules/@apostrophecms/asset/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,13 @@ module.exports = {
// In case of external front end like Astro, this option allows to
// disable the build of the public UI assets.
publicBundle: true,
// Device preview in the admin UI.
// NOTE: the whole devicePreviewMode option must be carried over
// to the project for override to work properly.
// Breakpoint preview in the admin UI.
// NOTE: the whole breakpointPreviewMode option must be carried over
// to the project for overrides to work properly.
// Nested object options are not deep merged in Apostrophe.
devicePreviewMode: {
// Enable device preview mode
enable: false,
breakpointPreviewMode: {
// Enable breakpoint preview mode
enable: true,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please tell me if we're missing something, e.g. we think this feature is highly likely to break somebody's build completely if we default it to true. Otherwise we really want people to have this experience out of the box

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do some css transformations on css declarations inside media queries.
If you have

@media (max-width: 700px) {
  main {
    width: 100%;
  }
}

you will end up with

@media (max-width: 700px) {
  :where(:not(body[data-breakpoint-preview-mode])) main {
    width: 100%;
  }
}
@container (max-width: 700px) {
  main {
    width: 100%;
  }
}

:where is widely supported https://developer.mozilla.org/en-US/docs/Web/CSS/:where

I should be safe to use.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that was the main driver for setting enabled: false by default.

// Warn during build about unsupported media queries.
debug: false,
// If we can resize the preview container?
Expand All @@ -61,21 +61,21 @@ module.exports = {
// https://docs.apostrophecms.org/reference/module-api/module-overview.html#icons
screens: {
desktop: {
label: 'apostrophe:devicePreviewDesktop',
width: '1500px',
label: 'apostrophe:breakpointPreviewDesktop',
width: '1440px',
height: '900px',
icon: 'monitor-icon'
},
tablet: {
label: 'apostrophe:devicePreviewTablet',
label: 'apostrophe:breakpointPreviewTablet',
width: '1024px',
height: '768px',
icon: 'tablet-icon'
},
mobile: {
label: 'apostrophe:devicePreviewMobile',
width: '480px',
height: '1000px',
label: 'apostrophe:breakpointPreviewMobile',
width: '414px',
height: '896px',
icon: 'cellphone-icon'
}
},
Expand Down
6 changes: 3 additions & 3 deletions modules/@apostrophecms/asset/lib/webpack/apos/webpack.scss.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
const path = require('path');

module.exports = (options, apos) => {
const mediaToContainerQueriesLoader = apos.asset.options.devicePreviewMode?.enable === true
const mediaToContainerQueriesLoader = apos.asset.options.breakpointPreviewMode?.enable === true
? {
loader: path.resolve(__dirname, '../media-to-container-queries-loader.js'),
options: {
debug: apos.asset.options.devicePreviewMode?.debug === true,
transform: apos.asset.options.devicePreviewMode?.transform || null
debug: apos.asset.options.breakpointPreviewMode?.debug === true,
transform: apos.asset.options.breakpointPreviewMode?.transform || null
}
}
: '';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,15 @@ module.exports = function (source) {
});

// Media query
// Only apply when data-device-preview-mode is not set
// Only apply when data-breakpoint-preview-mode is not set
atRule.walkRules(rule => {
const newRule = rule.clone({
selectors: rule.selectors.map(selector => {
if (selector.startsWith('body')) {
return selector.replace('body', ':where(body:not([data-device-preview-mode]))');
return selector.replace('body', ':where(body:not([data-breakpoint-preview-mode]))');
}

return `:where(body:not([data-device-preview-mode])) ${selector}`;
return `:where(body:not([data-breakpoint-preview-mode])) ${selector}`;
})
});

Expand Down
6 changes: 3 additions & 3 deletions modules/@apostrophecms/asset/lib/webpack/src/webpack.scss.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = (options, apos, srcBuildNames) => {
const mediaToContainerQueriesLoader = apos.asset.options.devicePreviewMode?.enable === true
const mediaToContainerQueriesLoader = apos.asset.options.breakpointPreviewMode?.enable === true
? {
loader: path.resolve(__dirname, '../media-to-container-queries-loader.js'),
options: {
debug: apos.asset.options.devicePreviewMode?.debug === true,
transform: apos.asset.options.devicePreviewMode?.transform || null
debug: apos.asset.options.breakpointPreviewMode?.debug === true,
transform: apos.asset.options.breakpointPreviewMode?.transform || null
}
}
: '';
Expand Down
Loading
Loading