Fix builder preview
This commit is contained in:
parent
093d7f95b9
commit
b3cd81d832
|
@ -1,28 +1,13 @@
|
||||||
<script>
|
<script>
|
||||||
import { store, backendUiStore } from "builderStore"
|
import { onMount } from "svelte"
|
||||||
import { map, join } from "lodash/fp"
|
import { store } from "builderStore"
|
||||||
import iframeTemplate from "./iframeTemplate"
|
import iframeTemplate from "./iframeTemplate"
|
||||||
import { pipe } from "../../../helpers"
|
import { Screen } from "builderStore/store/screenTemplates/utils/Screen"
|
||||||
import { Screen } from "../../../builderStore/store/screenTemplates/utils/Screen"
|
import { Component } from "builderStore/store/screenTemplates/utils/Component"
|
||||||
import { Component } from "../../../builderStore/store/screenTemplates/utils/Component"
|
|
||||||
|
|
||||||
let iframe
|
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 = {
|
const headingStyle = {
|
||||||
width: "500px",
|
width: "500px",
|
||||||
padding: "8px",
|
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
|
// TODO: this ID is attached to how the screen slot is rendered, confusing, would be better a type etc
|
||||||
screenPlaceholder.props._id = "screenslot-placeholder"
|
screenPlaceholder.props._id = "screenslot-placeholder"
|
||||||
|
|
||||||
$: hasComponent = !!$store.currentPreviewItem
|
// Extract data to pass to the iframe
|
||||||
|
$: page = $store.pages[$store.currentPageName]
|
||||||
$: {
|
$: screen =
|
||||||
styles = ""
|
$store.currentFrontEndType === "page"
|
||||||
// Apply the CSS from the currently selected page and its screens
|
? screenPlaceholder
|
||||||
const currentPage = $store.pages[$store.currentPageName]
|
: $store.currentPreviewItem
|
||||||
styles += currentPage._css
|
$: selectedComponentId = $store.currentComponentInfo?._id ?? ""
|
||||||
for (let screen of currentPage._screens) {
|
$: previewData = {
|
||||||
styles += screen._css
|
page,
|
||||||
}
|
screen,
|
||||||
styles = styles
|
selectedComponentId,
|
||||||
}
|
}
|
||||||
|
|
||||||
$: stylesheetLinks = pipe($store.pages.stylesheets, [
|
// Update the iframe with the builder info to render the correct preview
|
||||||
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
|
|
||||||
: ""
|
|
||||||
|
|
||||||
const refreshContent = () => {
|
const refreshContent = () => {
|
||||||
iframe.contentWindow.postMessage(
|
if (iframe) {
|
||||||
JSON.stringify({
|
iframe.contentWindow.postMessage(JSON.stringify(previewData))
|
||||||
styles,
|
}
|
||||||
stylesheetLinks,
|
|
||||||
selectedComponentType,
|
|
||||||
selectedComponentId,
|
|
||||||
frontendDefinition,
|
|
||||||
appId: $store.appId,
|
|
||||||
instanceId: $backendUiStore.selectedDatabase._id,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$: if (iframe)
|
// Refrech the preview when required
|
||||||
|
$: refreshContent(previewData)
|
||||||
|
|
||||||
|
// Initialise the app when mounted
|
||||||
|
onMount(() => {
|
||||||
iframe.contentWindow.addEventListener("bb-ready", refreshContent, {
|
iframe.contentWindow.addEventListener("bb-ready", refreshContent, {
|
||||||
once: true,
|
once: true,
|
||||||
})
|
})
|
||||||
|
})
|
||||||
$: if (iframe && frontendDefinition) {
|
|
||||||
refreshContent()
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="component-container">
|
<div class="component-container">
|
||||||
{#if hasComponent && $store.currentPreviewItem}
|
{#if $store.currentPreviewItem}
|
||||||
<iframe
|
<iframe
|
||||||
style="height: 100%; width: 100%"
|
style="height: 100%; width: 100%"
|
||||||
title="componentPreview"
|
title="componentPreview"
|
||||||
|
@ -152,7 +105,6 @@
|
||||||
margin: auto;
|
margin: auto;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.component-container iframe {
|
.component-container iframe {
|
||||||
border: 0;
|
border: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
|
|
@ -11,7 +11,7 @@ export default `<html>
|
||||||
*, *:before, *:after {
|
*, *:before, *:after {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
[data-bb-id="container-screenslot-placeholder"] {
|
[data-bb-id="screenslot-placeholder"] {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
@ -23,7 +23,7 @@ export default `<html>
|
||||||
background-color: rgba(0, 0, 0, 0.05);
|
background-color: rgba(0, 0, 0, 0.05);
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
}
|
}
|
||||||
[data-bb-id="container-screenslot-placeholder"] span {
|
[data-bb-id="screenslot-placeholder"] span {
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
@ -31,45 +31,46 @@ export default `<html>
|
||||||
<script src='/assets/budibase-client.js'></script>
|
<script src='/assets/budibase-client.js'></script>
|
||||||
<script>
|
<script>
|
||||||
function receiveMessage(event) {
|
function receiveMessage(event) {
|
||||||
|
if (!event.data) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (!event.data) return
|
// Extract data from message
|
||||||
|
const { selectedComponentId, page, screen } = JSON.parse(event.data)
|
||||||
const data = JSON.parse(event.data)
|
|
||||||
|
// Update selected component style
|
||||||
try {
|
if (selectedComponentStyle) {
|
||||||
if (styles) document.head.removeChild(styles)
|
document.head.removeChild(selectedComponentStyle)
|
||||||
} catch(_) { }
|
}
|
||||||
|
selectedComponentStyle = document.createElement("style");
|
||||||
try {
|
|
||||||
if (selectedComponentStyle) document.head.removeChild(selectedComponentStyle)
|
|
||||||
} catch(_) { }
|
|
||||||
|
|
||||||
selectedComponentStyle = document.createElement('style');
|
|
||||||
document.head.appendChild(selectedComponentStyle)
|
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))
|
selectedComponentStyle.appendChild(document.createTextNode(selectedCss))
|
||||||
|
|
||||||
styles = document.createElement('style')
|
// Set some flags so the app knows we're in the builder
|
||||||
document.head.appendChild(styles)
|
|
||||||
styles.appendChild(document.createTextNode(data.styles))
|
|
||||||
|
|
||||||
window["##BUDIBASE_IN_BUILDER##"] = true;
|
window["##BUDIBASE_IN_BUILDER##"] = true;
|
||||||
|
window["##BUDIBASE_PREVIEW_PAGE##"] = page;
|
||||||
|
window["##BUDIBASE_PREVIEW_SCREEN##"] = screen;
|
||||||
|
|
||||||
|
// Initialise app
|
||||||
if (window.loadBudibase) {
|
if (window.loadBudibase) {
|
||||||
loadBudibase()
|
loadBudibase()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let styles
|
|
||||||
let selectedComponentStyle
|
let selectedComponentStyle
|
||||||
|
|
||||||
document.addEventListener("click", function(e) {
|
// Ignore clicks
|
||||||
e.preventDefault()
|
["click", "mousedown"].forEach(type => {
|
||||||
e.stopPropagation()
|
document.addEventListener(type, function(e) {
|
||||||
return false;
|
e.preventDefault()
|
||||||
}, true)
|
e.stopPropagation()
|
||||||
|
return false
|
||||||
|
}, true)
|
||||||
|
})
|
||||||
|
|
||||||
window.addEventListener('message', receiveMessage)
|
window.addEventListener("message", receiveMessage)
|
||||||
window.dispatchEvent(new Event('bb-ready'))
|
window.dispatchEvent(new Event("bb-ready"))
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
|
@ -37,7 +37,6 @@
|
||||||
|
|
||||||
// Set contexts to be consumed by component
|
// Set contexts to be consumed by component
|
||||||
setContext("style", { ...definition._styles, id: definition._id })
|
setContext("style", { ...definition._styles, id: definition._id })
|
||||||
$: console.log("Rendering: " + componentName)
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if constructor}
|
{#if constructor}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
// Keep route params up to date
|
// Keep route params up to date
|
||||||
export let params
|
export let params
|
||||||
$: routeStore.actions.setRouteParams(params)
|
$: routeStore.actions.setRouteParams(params || {})
|
||||||
|
|
||||||
// Get the screen definition for the current route
|
// Get the screen definition for the current route
|
||||||
$: screenDefinition = $screenStore.activeScreen?.props
|
$: screenDefinition = $screenStore.activeScreen?.props
|
||||||
|
|
|
@ -24,11 +24,20 @@ const createScreenStore = () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
const fetchScreens = async () => {
|
const fetchScreens = async () => {
|
||||||
const appDefinition = await API.fetchAppDefinition(getAppId())
|
let screens
|
||||||
config.set({
|
let page
|
||||||
screens: appDefinition.screens,
|
const inBuilder = !!window["##BUDIBASE_IN_BUILDER##"]
|
||||||
page: appDefinition.page,
|
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 {
|
return {
|
||||||
|
|
|
@ -28,8 +28,5 @@ export const enrichDataBinding = (input, context) => {
|
||||||
if (!looksLikeMustache.test(input)) {
|
if (!looksLikeMustache.test(input)) {
|
||||||
return input
|
return input
|
||||||
}
|
}
|
||||||
console.log("====================================")
|
|
||||||
console.log(input)
|
|
||||||
console.log(context)
|
|
||||||
return mustache.render(input, context)
|
return mustache.render(input, context)
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,15 +130,6 @@
|
||||||
"form"
|
"form"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"select": {
|
|
||||||
"name": "Select",
|
|
||||||
"bindable": "value",
|
|
||||||
"description": "An HTML <select> (dropdown)",
|
|
||||||
"props": {
|
|
||||||
"value": "string",
|
|
||||||
"className": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"option": {
|
"option": {
|
||||||
"name": "Option",
|
"name": "Option",
|
||||||
"description": "An HTML <option>, to be used with <select>",
|
"description": "An HTML <option>, to be used with <select>",
|
||||||
|
@ -181,28 +172,6 @@
|
||||||
"value": "string"
|
"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": {
|
"icon": {
|
||||||
"description": "A HTML icon tag",
|
"description": "A HTML icon tag",
|
||||||
"props": {
|
"props": {
|
||||||
|
@ -687,7 +656,8 @@
|
||||||
"description": "Date Picker",
|
"description": "Date Picker",
|
||||||
"bindable": "value",
|
"bindable": "value",
|
||||||
"props": {
|
"props": {
|
||||||
"placeholder": "string"
|
"placeholder": "string",
|
||||||
|
"value": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"link": {
|
"link": {
|
||||||
|
|
|
@ -23,7 +23,6 @@
|
||||||
|
|
||||||
// if srcdoc, then we assume this is the builder preview
|
// if srcdoc, then we assume this is the builder preview
|
||||||
if ((pathParts.length === 0 || pathParts[0] === "srcdoc") && table) {
|
if ((pathParts.length === 0 || pathParts[0] === "srcdoc") && table) {
|
||||||
console.log("getting first row")
|
|
||||||
row = await fetchFirstRow()
|
row = await fetchFirstRow()
|
||||||
} else if (routeParamId) {
|
} else if (routeParamId) {
|
||||||
row = await API.fetchRow({ tableId: table, rowId: routeParamId })
|
row = await API.fetchRow({ tableId: table, rowId: routeParamId })
|
||||||
|
|
|
@ -97,4 +97,4 @@
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ApexChart {options} {styles} />
|
<ApexChart {options} />
|
||||||
|
|
Loading…
Reference in New Issue