Add support for correct mobile and tablet previews

This commit is contained in:
Andrew Kingston 2021-09-08 09:40:25 +01:00
parent 802cdc48c8
commit 35abb39b24
5 changed files with 163 additions and 112 deletions

View File

@ -85,22 +85,36 @@
<UserBindingsProvider> <UserBindingsProvider>
<DeviceBindingsProvider> <DeviceBindingsProvider>
<StateBindingsProvider> <StateBindingsProvider>
<CustomThemeWrapper> <!-- Settings bar can be rendered outside of device preview -->
<div id="app-root" class:preview={$builderStore.inBuilder}>
{#key $screenStore.activeLayout._id}
<Component instance={$screenStore.activeLayout.props} />
{/key}
</div>
</CustomThemeWrapper>
<NotificationDisplay />
<ConfirmationDisplay />
<PeekScreenDisplay />
<!-- Key block needs to be outside the if statement or it breaks --> <!-- Key block needs to be outside the if statement or it breaks -->
{#key $builderStore.selectedComponentId} {#key $builderStore.selectedComponentId}
{#if $builderStore.inBuilder} {#if $builderStore.inBuilder}
<SettingsBar /> <SettingsBar />
{/if} {/if}
{/key} {/key}
<!-- Device boundary -->
<div
id="device-root"
class:preview={$builderStore.inBuilder}
class:tablet-preview={$builderStore.previewDevice === "tablet"}
class:mobile-preview={$builderStore.previewDevice === "mobile"}
>
<!-- Actual app -->
<div id="app-root">
<CustomThemeWrapper>
{#key $screenStore.activeLayout._id}
<Component instance={$screenStore.activeLayout.props} />
{/key}
<!-- Layers on top of app -->
<NotificationDisplay />
<ConfirmationDisplay />
<PeekScreenDisplay />
</CustomThemeWrapper>
</div>
<!-- Selection indicators should be bounded by device -->
<!-- <!--
We don't want to key these by componentID as they control their own We don't want to key these by componentID as they control their own
re-mounting to avoid flashes. re-mounting to avoid flashes.
@ -109,6 +123,7 @@
<SelectionIndicator /> <SelectionIndicator />
<HoverIndicator /> <HoverIndicator />
{/if} {/if}
</div>
</StateBindingsProvider> </StateBindingsProvider>
</DeviceBindingsProvider> </DeviceBindingsProvider>
</UserBindingsProvider> </UserBindingsProvider>
@ -117,20 +132,33 @@
{/if} {/if}
<style> <style>
#spectrum-root, #spectrum-root {
#app-root {
height: 100%;
width: 100%;
padding: 0; padding: 0;
margin: 0; margin: 0;
overflow: hidden; overflow: hidden;
height: 100%;
width: 100%;
background: transparent;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
#device-root {
max-width: 100%;
max-height: 100%;
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
background-color: transparent;
} }
#app-root { #app-root {
position: relative; overflow: hidden;
} height: 100%;
#app-root.preview { width: 100%;
border: 1px solid var(--spectrum-global-color-gray-300);
} }
.error { .error {
position: absolute; position: absolute;
width: 100%; width: 100%;
@ -157,4 +185,21 @@
.error :global(h1) { .error :global(h1) {
font-weight: 400; font-weight: 400;
} }
/* Preview styles */
#device-root.preview {
padding: 2px;
}
#device-root.tablet-preview {
width: calc(1024px + 8px);
height: calc(768px + 8px);
}
#device-root.mobile-preview {
width: calc(390px + 8px);
height: calc(844px + 8px);
}
.preview #app-root {
border: 2px solid var(--spectrum-global-color-gray-300);
border-radius: 4px;
}
</style> </style>

View File

@ -5,6 +5,7 @@
const { routeStore, styleable, linkable, builderStore } = getContext("sdk") const { routeStore, styleable, linkable, builderStore } = getContext("sdk")
const component = getContext("component") const component = getContext("component")
const context = getContext("context")
export let title export let title
export let hideTitle = false export let hideTitle = false
@ -58,7 +59,12 @@
} }
</script> </script>
<div class="layout layout--{typeClass}" use:styleable={$component.styles}> <div
class="layout layout--{typeClass}"
use:styleable={$component.styles}
class:desktop={!$context.device.mobile && !$context.device.tablet}
class:mobile={!!$context.device.mobile}
>
{#if typeClass !== "none"} {#if typeClass !== "none"}
<div class="nav-wrapper" class:sticky class:hidden={isPeeking}> <div class="nav-wrapper" class:sticky class:hidden={isPeeking}>
<div class="nav nav--{typeClass} size--{widthClass}"> <div class="nav nav--{typeClass} size--{widthClass}">
@ -286,35 +292,32 @@
} }
/* Desktop nav overrides */ /* Desktop nav overrides */
@media (min-width: 600px) { .desktop.layout--left {
.layout--left {
flex-direction: row; flex-direction: row;
overflow: hidden; overflow: hidden;
} }
.layout--left .main-wrapper { .desktop.layout--left .main-wrapper {
height: 100%; height: 100%;
overflow: auto; overflow: auto;
} }
.nav--left { .desktop .nav--left {
width: 250px; width: 250px;
padding: var(--spacing-xl); padding: var(--spacing-xl);
} }
.nav--left .links { .desktop .nav--left .links {
margin-top: var(--spacing-m); margin-top: var(--spacing-m);
flex-direction: column; flex-direction: column;
justify-content: flex-start; justify-content: flex-start;
align-items: stretch; align-items: stretch;
} }
.nav--left .link { .desktop .nav--left .link {
font-size: var(--spectrum-global-dimension-font-size-150); font-size: var(--spectrum-global-dimension-font-size-150);
} }
}
/* Mobile nav overrides */ /* Mobile nav overrides */
@media (max-width: 600px) { .mobile .nav-wrapper {
.nav-wrapper {
position: sticky; position: sticky;
top: 0; top: 0;
left: 0; left: 0;
@ -322,34 +325,34 @@
} }
/* Show close button in drawer */ /* Show close button in drawer */
.close { .mobile .close {
display: block; display: block;
} }
/* Force standard top bar */ /* Force standard top bar */
.nav { .mobile .nav {
padding: var(--spacing-m) 16px; padding: var(--spacing-m) 16px;
} }
.burger { .mobile .burger {
display: grid; display: grid;
place-items: center; place-items: center;
} }
.logo { .mobile .logo {
flex: 0 0 auto; flex: 0 0 auto;
} }
.logo :global(h1) { .mobile .logo :global(h1) {
display: none; display: none;
} }
/* Reduce padding */ /* Reduce padding */
.main { .mobile .main {
padding: 16px; padding: 16px;
} }
/* Transform links into drawer */ /* Transform links into drawer */
.links { .mobile .links {
margin-top: 0; margin-top: 0;
position: fixed; position: absolute;
top: 0; top: 0;
left: -250px; left: -250px;
transform: translateX(0); transform: translateX(0);
@ -364,17 +367,17 @@
align-items: stretch; align-items: stretch;
padding: var(--spacing-xl); padding: var(--spacing-xl);
} }
.link { .mobile .link {
width: calc(100% - 30px); width: calc(100% - 30px);
font-size: 120%; font-size: 120%;
} }
.links.visible { .mobile .links.visible {
opacity: 1; opacity: 1;
transform: translateX(250px); transform: translateX(250px);
box-shadow: 0 0 80px 20px rgba(0, 0, 0, 0.3); box-shadow: 0 0 80px 20px rgba(0, 0, 0, 0.3);
} }
.mobile-click-handler.visible { .mobile .mobile-click-handler.visible {
position: fixed; position: absolute;
display: block; display: block;
top: 0; top: 0;
left: 0; left: 0;
@ -382,5 +385,4 @@
height: 100vh; height: 100vh;
z-index: 998; z-index: 998;
} }
}
</style> </style>

View File

@ -17,7 +17,7 @@
} }
onMount(() => { onMount(() => {
const doc = document.documentElement const doc = document.getElementById("device-root")
resizeObserver.observe(doc) resizeObserver.observe(doc)
return () => { return () => {

View File

@ -18,6 +18,7 @@ const loadBudibase = () => {
previewType: window["##BUDIBASE_PREVIEW_TYPE##"], previewType: window["##BUDIBASE_PREVIEW_TYPE##"],
theme: window["##BUDIBASE_PREVIEW_THEME##"], theme: window["##BUDIBASE_PREVIEW_THEME##"],
customTheme: window["##BUDIBASE_PREVIEW_CUSTOM_THEME##"], customTheme: window["##BUDIBASE_PREVIEW_CUSTOM_THEME##"],
previewDevice: window["##BUDIBASE_PREVIEW_DEVICE##"],
}) })
// Set app ID - this window flag is set by both the preview and the real // Set app ID - this window flag is set by both the preview and the real

View File

@ -20,6 +20,9 @@ const createBuilderStore = () => {
previewId: null, previewId: null,
previewType: null, previewType: null,
selectedPath: [], selectedPath: [],
theme: null,
customTheme: null,
previewDevice: "desktop",
} }
const writableStore = writable(initialState) const writableStore = writable(initialState)
const derivedStore = derived(writableStore, $state => { const derivedStore = derived(writableStore, $state => {