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

Pro 6518 mobile preview #4720

Open
wants to merge 36 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
38e5020
experiment with mobile preview
haroun Sep 6, 2024
be578e8
fix lint issue
haroun Sep 6, 2024
60de087
add media to container query loader
haroun Sep 6, 2024
4c20516
clean up media to container queries loader
haroun Sep 10, 2024
62fd03a
use apostrophe options to toggle mobile preview
haroun Sep 10, 2024
c9579c8
add breakpoints support
haroun Sep 10, 2024
895fb42
experiment with screen options
haroun Sep 10, 2024
7db9493
refactor as device preview mode to avoid name conflicts with preview …
haroun Sep 11, 2024
8991290
set todos
haroun Sep 11, 2024
6806ded
rename devicePreview into devicePreviewMode and validate screens
haroun Sep 12, 2024
174c897
set active style
haroun Sep 12, 2024
4573f98
set active style and keyboard shortcuts
haroun Sep 12, 2024
e52a478
restore previous filename logic
haroun Sep 12, 2024
bf528e8
revert plugins
haroun Sep 12, 2024
ca2bb37
note about deep merge
haroun Sep 12, 2024
c296953
Merge branch 'main' into pro-6518-mobile-preview
haroun Sep 12, 2024
e6e4cd1
update changelog
haroun Sep 12, 2024
52c8cb3
remove test styles
haroun Sep 12, 2024
129cc3a
fix mixed declaration warnings
haroun Sep 12, 2024
0ac0e49
translate device
haroun Sep 12, 2024
f7eaa02
translate additional keys in keyboard shortcut label
haroun Sep 12, 2024
070fc88
add transform function
haroun Sep 13, 2024
1329f7e
explain available transform options
haroun Sep 13, 2024
64e6c27
up to 9 shortcuts
haroun Sep 13, 2024
5b8b7a3
Merge branch 'main' into pro-6518-mobile-preview
haroun Sep 23, 2024
b2c051a
use flexbox
haroun Sep 23, 2024
ec60630
rename devicePreviewMode options
haroun Sep 23, 2024
bffb915
add label
haroun Sep 23, 2024
c93db00
fix lint issue
haroun Sep 23, 2024
180a12d
support preview mode background
haroun Sep 23, 2024
3b887ff
style tweaks
stuartromanek Sep 23, 2024
05f0f75
lint
stuartromanek Sep 23, 2024
0d072dd
save device preview state, transition only for non-resizable containers
haroun Sep 24, 2024
d08b8fd
save device preview mode state inside own component
haroun Sep 24, 2024
f86f60a
update background on body
haroun Sep 24, 2024
7da9500
keep state
haroun Sep 24, 2024
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,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.

### Changes

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

devicePreviewModeCommands[`${self.__meta.name}:toggle-device-preview-mode:${name}`] = {
type: 'item',
label: {
key: 'apostrophe:commandMenuToggleDevicePreviewMode',
device: `$t(${screen.label})`
},
action: {
type: 'command-menu-admin-bar-toggle-device-preview-mode',
payload: {
mode: name,
label: `$t(${screen.label})`,
width: screen.width,
height: screen.height
}
},
shortcut: `P,${index}`
Copy link
Contributor

@ETLaurent ETLaurent Sep 24, 2024

Choose a reason for hiding this comment

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

Suggested change
shortcut: `P,${index}`
shortcut: `P ${index}`

seems more UX-friendly to me

};

index += 1;
};

return {
add: {
[`${self.__meta.name}:undo`]: {
Expand Down Expand Up @@ -63,7 +113,8 @@ module.exports = {
type: 'command-menu-admin-bar-toggle-publish-draft'
},
shortcut: 'Ctrl+Shift+D Meta+Shift+D'
}
},
...devicePreviewModeCommands
},
modal: {
default: {
Expand All @@ -80,7 +131,8 @@ module.exports = {
label: 'apostrophe:commandMenuMode',
commands: [
`${self.__meta.name}:toggle-edit-preview-mode`,
`${self.__meta.name}:toggle-published-draft-document`
`${self.__meta.name}:toggle-published-draft-document`,
...Object.keys(devicePreviewModeCommands)
]
}
}
Expand Down Expand Up @@ -355,6 +407,13 @@ module.exports = {
aposLocale: context.aposLocale,
aposDocId: context.aposDocId
},
devicePreviewMode: self.apos.asset.options.devicePreviewMode ||
{
enable: false,
debug: false,
resizable: false,
screens: {}
},
// Base API URL appropriate to the context document
contextBar: context && self.apos.doc.getManager(context.type).options.contextBar,
showAdminBar: self.getShowAdminBar(req),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
<template>
<div
data-apos-test="devicePreviewMode"
class="apos-admin-bar__device-preview-mode"
>
<component
:is="'AposButton'"
v-for="(screen, name) in screens"
:key="name"
:data-apos-test="`devicePreviewMode:${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-is-active': mode === name }"
@click="toggleDevicePreviewMode({ mode: name, label: screen.label, width: screen.width, height: screen.height })"
/>
</div>
</template>
<script>

export default {
name: 'TheAposContextDevicePreview',
props: {
// { screenName: { label: string, width: string, height: string, icon: string } }
screens: {
type: Object,
validator(value, props) {
return Object.values(value).every(screen =>
typeof screen.label === 'string' &&
typeof screen.width === 'string' &&
typeof screen.height === 'string' &&
typeof screen.icon === 'string'
);
},
default: () => {
return {};
}
},
resizable: {
type: Boolean,
default: false
}
},
emits: [ 'switch-device-preview-mode', 'reset-device-preview-mode' ],
data() {
return {
mode: null
};
},
mounted() {
apos.bus.$on('command-menu-admin-bar-toggle-device-preview-mode', this.toggleDevicePreviewMode);

const state = this.loadState();
if (state.mode) {
this.toggleDevicePreviewMode(state);
}
},
unmounted() {
apos.bus.$off('command-menu-admin-bar-toggle-device-preview-mode', this.toggleDevicePreviewMode);
},
methods: {
switchDevicePreviewMode({
mode,
label,
width,
height
}) {
document.querySelector('body').setAttribute('data-device-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;
this.mode = mode;
this.$emit('switch-device-preview-mode', {
mode,
label,
width,
height
});
this.saveState({
mode,
label,
width,
height
});
},
toggleDevicePreviewMode({
mode,
label,
width,
height
}) {
if (this.mode === mode || mode === null) {
document.querySelector('body').removeAttribute('data-device-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');
this.mode = null;
this.$emit('reset-device-preview-mode');

return;
}

this.switchDevicePreviewMode({
mode,
label,
width,
height
});
},
loadState() {
return JSON.parse(sessionStorage.getItem('aposDevicePreviewMode') || '{}');
},
saveState({
mode,
label,
width,
height
}) {
sessionStorage.setItem(
'aposDevicePreviewMode',
JSON.stringify({
mode,
label,
width,
height
})
);
}
}
};
</script>
<style lang="scss" scoped>
.apos-admin-bar__device-preview-mode {
display: flex;
gap: $spacing-half;
margin-left: $spacing-double;
}

.apos-admin-bar__device-preview-mode-button {
&.apos-is-active {
color: var(--a-text-primary);
text-decoration: none;
background-color: var(--a-base-10);
border-radius: var(--a-border-radius);
outline: 1px solid var(--a-base-7);
}
}
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@
:tooltip="tooltip"
:modifiers="modifiers"
/>
<TheAposContextDevicePreviewMode
v-if="isDevicePreviewModeEnabled"
:screens="devicePreviewModeScreens"
:resizable="devicePreviewModeResizable"
/>
</span>
</transition-group>
</template>
Expand Down Expand Up @@ -94,6 +99,15 @@ export default {
isUnpublished() {
return !this.context.lastPublishedAt;
},
isDevicePreviewModeEnabled() {
return this.moduleOptions.devicePreviewMode.enable || false;
},
devicePreviewModeScreens() {
return this.moduleOptions.devicePreviewMode.screens || {};
},
devicePreviewModeResizable() {
return this.moduleOptions.devicePreviewMode.resizable || false;
},
docTooltip() {
return {
key: 'apostrophe:lastUpdatedBy',
Expand Down
42 changes: 41 additions & 1 deletion modules/@apostrophecms/asset/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,47 @@ module.exports = {
rebundleModules: undefined,
// In case of external front end like Astro, this option allows to
// disable the build of the public UI assets.
publicBundle: true
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.
// Nested object options are not deep merged in Apostrophe.
devicePreviewMode: {
// Enable device preview mode
enable: false,
// Warn during build about unsupported media queries.
debug: false,
// If we can resize the preview container?
resizable: false,
// Screens with icons
// For adding icons, please refer to the icons documentation
// https://docs.apostrophecms.org/reference/module-api/module-overview.html#icons
screens: {
desktop: {
label: 'apostrophe:devicePreviewDesktop',
width: '1500px',
height: '900px',
icon: 'monitor-icon'
},
tablet: {
label: 'apostrophe:devicePreviewTablet',
width: '1024px',
height: '768px',
icon: 'tablet-icon'
},
mobile: {
label: 'apostrophe:devicePreviewMobile',
width: '480px',
height: '1000px',
icon: 'cellphone-icon'
}
},
// Transform method used on media feature
// Can be either:
// - (mediaFeature) => { return mediaFeature; }
// - null
transform: null
}
},

async init(self) {
Expand Down
3 changes: 3 additions & 0 deletions modules/@apostrophecms/asset/lib/globalIcons.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ module.exports = {
'arrow-up-icon': 'ArrowUp',
'binoculars-icon': 'Binoculars',
'calendar-icon': 'Calendar',
'cellphone-icon': 'Cellphone',
'check-all-icon': 'CheckAll',
'check-bold-icon': 'CheckBold',
'check-circle-icon': 'CheckCircle',
Expand Down Expand Up @@ -98,6 +99,7 @@ module.exports = {
'menu-down-icon': 'MenuDown',
'minus-box-icon': 'MinusBox',
'minus-icon': 'Minus',
'monitor-icon': 'Monitor',
'paperclip-icon': 'Paperclip',
'pencil-icon': 'Pencil',
'phone-icon': 'Phone',
Expand All @@ -107,6 +109,7 @@ module.exports = {
'refresh-icon': 'Refresh',
'shape-icon': 'Shape',
'sign-text-icon': 'SignText',
'tablet-icon': 'Tablet',
'tag-icon': 'Tag',
'text-box-icon': 'TextBox',
'text-box-multiple-icon': 'TextBoxMultiple',
Expand Down
Loading
Loading