Fix builder preview

This commit is contained in:
Andrew Kingston 2020-11-23 11:29:24 +00:00
parent 093d7f95b9
commit b3cd81d832
9 changed files with 75 additions and 148 deletions

View File

@ -1,28 +1,13 @@
<script>
import { store, backendUiStore } from "builderStore"
import { map, join } from "lodash/fp"
import { onMount } from "svelte"
import { store } from "builderStore"
import iframeTemplate from "./iframeTemplate"
import { pipe } from "../../../helpers"
import { Screen } from "../../../builderStore/store/screenTemplates/utils/Screen"
import { Component } from "../../../builderStore/store/screenTemplates/utils/Component"
import { Screen } from "builderStore/store/screenTemplates/utils/Screen"
import { Component } from "builderStore/store/screenTemplates/utils/Component"
let iframe
let styles = ""
function transform_component(comp) {
const props = comp.props || comp
if (props && props._children && props._children.length) {
props._children = props._children.map(transform_component)
}
return props
}
const getComponentTypeName = component => {
let [componentName] = component._component.match(/[a-zA-Z]*$/)
return componentName || "element"
}
// Styles for screenslot placeholder
const headingStyle = {
width: "500px",
padding: "8px",
@ -70,71 +55,39 @@
// TODO: this ID is attached to how the screen slot is rendered, confusing, would be better a type etc
screenPlaceholder.props._id = "screenslot-placeholder"
$: hasComponent = !!$store.currentPreviewItem
$: {
styles = ""
// Apply the CSS from the currently selected page and its screens
const currentPage = $store.pages[$store.currentPageName]
styles += currentPage._css
for (let screen of currentPage._screens) {
styles += screen._css
}
styles = styles
// Extract data to pass to the iframe
$: page = $store.pages[$store.currentPageName]
$: screen =
$store.currentFrontEndType === "page"
? screenPlaceholder
: $store.currentPreviewItem
$: selectedComponentId = $store.currentComponentInfo?._id ?? ""
$: previewData = {
page,
screen,
selectedComponentId,
}
$: stylesheetLinks = pipe($store.pages.stylesheets, [
map(s => `<link rel="stylesheet" href="${s}"/>`),
join("\n"),
])
$: screensExist =
$store.currentPreviewItem._screens &&
$store.currentPreviewItem._screens.length > 0
$: frontendDefinition = {
appId: $store.appId,
libraries: $store.libraries,
page: $store.pages[$store.currentPageName],
screens: [
$store.currentFrontEndType === "page"
? screenPlaceholder
: $store.currentPreviewItem,
],
}
$: selectedComponentType = getComponentTypeName($store.currentComponentInfo)
$: selectedComponentId = $store.currentComponentInfo
? $store.currentComponentInfo._id
: ""
// Update the iframe with the builder info to render the correct preview
const refreshContent = () => {
iframe.contentWindow.postMessage(
JSON.stringify({
styles,
stylesheetLinks,
selectedComponentType,
selectedComponentId,
frontendDefinition,
appId: $store.appId,
instanceId: $backendUiStore.selectedDatabase._id,
})
)
if (iframe) {
iframe.contentWindow.postMessage(JSON.stringify(previewData))
}
}
$: if (iframe)
// Refrech the preview when required
$: refreshContent(previewData)
// Initialise the app when mounted
onMount(() => {
iframe.contentWindow.addEventListener("bb-ready", refreshContent, {
once: true,
})
$: if (iframe && frontendDefinition) {
refreshContent()
}
})
</script>
<div class="component-container">
{#if hasComponent && $store.currentPreviewItem}
{#if $store.currentPreviewItem}
<iframe
style="height: 100%; width: 100%"
title="componentPreview"
@ -152,7 +105,6 @@
margin: auto;
height: 100%;
}
.component-container iframe {
border: 0;
left: 0;

View File

@ -11,7 +11,7 @@ export default `<html>
*, *:before, *:after {
box-sizing: border-box;
}
[data-bb-id="container-screenslot-placeholder"] {
[data-bb-id="screenslot-placeholder"] {
display: flex;
align-items: center;
justify-content: center;
@ -23,7 +23,7 @@ export default `<html>
background-color: rgba(0, 0, 0, 0.05);
flex: 1 1 auto;
}
[data-bb-id="container-screenslot-placeholder"] span {
[data-bb-id="screenslot-placeholder"] span {
display: block;
margin-bottom: 10px;
}
@ -31,45 +31,46 @@ export default `<html>
<script src='/assets/budibase-client.js'></script>
<script>
function receiveMessage(event) {
if (!event.data) {
return
}
if (!event.data) return
const data = JSON.parse(event.data)
try {
if (styles) document.head.removeChild(styles)
} catch(_) { }
try {
if (selectedComponentStyle) document.head.removeChild(selectedComponentStyle)
} catch(_) { }
selectedComponentStyle = document.createElement('style');
// Extract data from message
const { selectedComponentId, page, screen } = JSON.parse(event.data)
// Update selected component style
if (selectedComponentStyle) {
document.head.removeChild(selectedComponentStyle)
}
selectedComponentStyle = document.createElement("style");
document.head.appendChild(selectedComponentStyle)
var selectedCss = '[data-bb-id="' + data.selectedComponentId + '"]' + '{border:2px solid #0055ff !important;}'
var selectedCss = '[data-bb-id="' + selectedComponentId + '"]' + '{border:2px solid #0055ff !important;}'
selectedComponentStyle.appendChild(document.createTextNode(selectedCss))
styles = document.createElement('style')
document.head.appendChild(styles)
styles.appendChild(document.createTextNode(data.styles))
// Set some flags so the app knows we're in the builder
window["##BUDIBASE_IN_BUILDER##"] = true;
window["##BUDIBASE_PREVIEW_PAGE##"] = page;
window["##BUDIBASE_PREVIEW_SCREEN##"] = screen;
// Initialise app
if (window.loadBudibase) {
loadBudibase()
}
}
let styles
let selectedComponentStyle
document.addEventListener("click", function(e) {
e.preventDefault()
e.stopPropagation()
return false;
}, true)
// Ignore clicks
["click", "mousedown"].forEach(type => {
document.addEventListener(type, function(e) {
e.preventDefault()
e.stopPropagation()
return false
}, true)
})
window.addEventListener('message', receiveMessage)
window.dispatchEvent(new Event('bb-ready'))
window.addEventListener("message", receiveMessage)
window.dispatchEvent(new Event("bb-ready"))
</script>
</head>
<body>

View File

@ -37,7 +37,6 @@
// Set contexts to be consumed by component
setContext("style", { ...definition._styles, id: definition._id })
$: console.log("Rendering: " + componentName)
</script>
{#if constructor}

View File

@ -4,7 +4,7 @@
// Keep route params up to date
export let params
$: routeStore.actions.setRouteParams(params)
$: routeStore.actions.setRouteParams(params || {})
// Get the screen definition for the current route
$: screenDefinition = $screenStore.activeScreen?.props

View File

@ -24,11 +24,20 @@ const createScreenStore = () => {
})
const fetchScreens = async () => {
const appDefinition = await API.fetchAppDefinition(getAppId())
config.set({
screens: appDefinition.screens,
page: appDefinition.page,
})
let screens
let page
const inBuilder = !!window["##BUDIBASE_IN_BUILDER##"]
if (inBuilder) {
// Load screen and page from the window object if in the builder
screens = [window["##BUDIBASE_PREVIEW_SCREEN##"]]
page = window["##BUDIBASE_PREVIEW_PAGE##"]
} else {
// Otherwise load from API
const appDefinition = await API.fetchAppDefinition(getAppId())
screens = appDefinition.screens
page = appDefinition.page
}
config.set({ screens, page })
}
return {

View File

@ -28,8 +28,5 @@ export const enrichDataBinding = (input, context) => {
if (!looksLikeMustache.test(input)) {
return input
}
console.log("====================================")
console.log(input)
console.log(context)
return mustache.render(input, context)
}

View File

@ -130,15 +130,6 @@
"form"
]
},
"select": {
"name": "Select",
"bindable": "value",
"description": "An HTML <select> (dropdown)",
"props": {
"value": "string",
"className": "string"
}
},
"option": {
"name": "Option",
"description": "An HTML <option>, to be used with <select>",
@ -181,28 +172,6 @@
"value": "string"
}
},
"checkbox": {
"name": "Checkbox",
"bindable": "value",
"description": "A selectable checkbox component",
"props": {
"label": "string",
"checked": "bool",
"value": "string",
"onchange": "event"
}
},
"radiobutton": {
"name": "Radiobutton",
"bindable": "value",
"description": "A selectable radiobutton component",
"props": {
"label": "string",
"checked": "bool",
"value": "string",
"onchange": "event"
}
},
"icon": {
"description": "A HTML icon tag",
"props": {
@ -687,7 +656,8 @@
"description": "Date Picker",
"bindable": "value",
"props": {
"placeholder": "string"
"placeholder": "string",
"value": "string"
}
},
"link": {

View File

@ -23,7 +23,6 @@
// if srcdoc, then we assume this is the builder preview
if ((pathParts.length === 0 || pathParts[0] === "srcdoc") && table) {
console.log("getting first row")
row = await fetchFirstRow()
} else if (routeParamId) {
row = await API.fetchRow({ tableId: table, rowId: routeParamId })

View File

@ -97,4 +97,4 @@
})
</script>
<ApexChart {options} {styles} />
<ApexChart {options} />