diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js
index 5aecd4aff7..47a02518cb 100644
--- a/packages/builder/src/builderStore/store/frontend.js
+++ b/packages/builder/src/builderStore/store/frontend.js
@@ -354,6 +354,16 @@ export const getFrontendStore = () => {
return state
})
},
+ sendEvent: (name, payload) => {
+ const { previewEventHandler } = get(store)
+ previewEventHandler?.(name, payload)
+ },
+ registerEventHandler: handler => {
+ store.update(state => {
+ state.previewEventHandler = handler
+ return state
+ })
+ },
},
layouts: {
select: layoutId => {
@@ -895,7 +905,12 @@ export const getFrontendStore = () => {
component[name] = value
})
},
- ejectBlock: async (componentId, ejectedDefinition) => {
+ requestEjectBlock: componentId => {
+ store.actions.preview.sendEvent("eject-block", componentId)
+ },
+ handleEjectBlock: async (componentId, ejectedDefinition) => {
+ let nextSelectedComponentId
+
await store.actions.screens.patch(screen => {
const parent = findComponentParent(screen.props, componentId)
@@ -908,8 +923,18 @@ export const getFrontendStore = () => {
const childIndex = parent._children.findIndex(
child => child._id === componentId
)
+ makeComponentUnique(ejectedDefinition)
parent._children[childIndex] = ejectedDefinition
+ nextSelectedComponentId = ejectedDefinition._id
})
+
+ // Select new root component
+ if (nextSelectedComponentId) {
+ store.update(state => {
+ state.selectedComponentId = nextSelectedComponentId
+ return state
+ })
+ }
},
},
links: {
diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/AppPreview.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/AppPreview.svelte
index eb0809f55b..3cbf1c639f 100644
--- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/AppPreview.svelte
+++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/AppPreview.svelte
@@ -96,11 +96,21 @@
`./components/${$selectedComponent?._id}/new`
)
+ // Register handler to send custom to the preview
+ $: store.actions.preview.registerEventHandler((name, payload) => {
+ iframe?.contentWindow.postMessage(
+ JSON.stringify({
+ name,
+ payload,
+ isBudibaseEvent: true,
+ runtimeEvent: true,
+ })
+ )
+ })
+
// Update the iframe with the builder info to render the correct preview
const refreshContent = message => {
- if (iframe) {
- iframe.contentWindow.postMessage(message)
- }
+ iframe?.contentWindow.postMessage(message)
}
const receiveMessage = message => {
@@ -198,7 +208,7 @@
}
} else if (type === "eject-block") {
const { id, definition } = data
- await store.actions.components.ejectBlock(id, definition)
+ await store.actions.components.handleEjectBlock(id, definition)
} else {
console.warn(`Client sent unknown event type: ${type}`)
}
diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/components/[componentId]/_components/navigation/ComponentListPanel.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/components/[componentId]/_components/navigation/ComponentListPanel.svelte
index b00a0fc94f..488f7fe587 100644
--- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/components/[componentId]/_components/navigation/ComponentListPanel.svelte
+++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/components/[componentId]/_components/navigation/ComponentListPanel.svelte
@@ -112,9 +112,9 @@
} else if (e.key === "Enter") {
e.preventDefault()
$goto("./new")
- } else if (e.key === "Enter") {
+ } else if (e.key === "e") {
e.preventDefault()
- await store.actions.components.ejectBlock()
+ await store.actions.components.requestEjectBlock(component?._id)
}
} else if (e.key === "Backspace" || e.key === "Delete") {
// Don't show confirmation for the screen itself
diff --git a/packages/client/src/components/Block.svelte b/packages/client/src/components/Block.svelte
index 10da66dc39..4c2c6b16c0 100644
--- a/packages/client/src/components/Block.svelte
+++ b/packages/client/src/components/Block.svelte
@@ -1,6 +1,7 @@
diff --git a/packages/client/src/index.js b/packages/client/src/index.js
index b582dab4d3..536d4abb3f 100644
--- a/packages/client/src/index.js
+++ b/packages/client/src/index.js
@@ -1,5 +1,5 @@
import ClientApp from "./components/ClientApp.svelte"
-import { builderStore, appStore, devToolsStore } from "./stores"
+import { builderStore, appStore, devToolsStore, blockStore } from "./stores"
import loadSpectrumIcons from "@budibase/bbui/spectrum-icons-rollup.js"
import { get } from "svelte/store"
@@ -32,6 +32,17 @@ const loadBudibase = () => {
const enableDevTools = !get(builderStore).inBuilder && get(appStore).isDevApp
devToolsStore.actions.setEnabled(enableDevTools)
+ // Register handler for runtime events from the builder
+ window.handleBuilderRuntimeEvent = (name, payload) => {
+ if (!window["##BUDIBASE_IN_BUILDER##"]) {
+ return
+ }
+ if (name === "eject-block") {
+ const block = blockStore.actions.getBlock(payload)
+ block?.eject()
+ }
+ }
+
// Create app if one hasn't been created yet
if (!app) {
app = new ClientApp({
diff --git a/packages/client/src/stores/blocks.js b/packages/client/src/stores/blocks.js
new file mode 100644
index 0000000000..98381ec79b
--- /dev/null
+++ b/packages/client/src/stores/blocks.js
@@ -0,0 +1,34 @@
+import { get, writable } from "svelte/store"
+
+const createBlockStore = () => {
+ const store = writable({})
+
+ const registerBlock = (id, instance) => {
+ store.update(state => ({
+ ...state,
+ [id]: instance,
+ }))
+ }
+
+ const unregisterBlock = id => {
+ store.update(state => {
+ delete state[id]
+ return state
+ })
+ }
+
+ const getBlock = id => {
+ return get(store)[id]
+ }
+
+ return {
+ subscribe: store.subscribe,
+ actions: {
+ registerBlock,
+ unregisterBlock,
+ getBlock,
+ },
+ }
+}
+
+export const blockStore = createBlockStore()
diff --git a/packages/client/src/stores/index.js b/packages/client/src/stores/index.js
index e28fbaee42..6a72f59db9 100644
--- a/packages/client/src/stores/index.js
+++ b/packages/client/src/stores/index.js
@@ -17,6 +17,7 @@ export { devToolsStore } from "./devTools"
export { componentStore } from "./components"
export { uploadStore } from "./uploads.js"
export { rowSelectionStore } from "./rowSelection.js"
+export { blockStore } from "./blocks.js"
// Context stores are layered and duplicated, so it is not a singleton
export { createContextStore } from "./context"
diff --git a/packages/server/src/api/controllers/static/templates/preview.hbs b/packages/server/src/api/controllers/static/templates/preview.hbs
index 28908df507..df9384c1d0 100644
--- a/packages/server/src/api/controllers/static/templates/preview.hbs
+++ b/packages/server/src/api/controllers/static/templates/preview.hbs
@@ -56,6 +56,16 @@
return
}
+ // If this is a custom event, try and handle it
+ if (parsed.runtimeEvent) {
+ const { name, payload } = parsed
+ if (window.handleBuilderRuntimeEvent) {
+ window.handleBuilderRuntimeEvent(name, payload)
+ }
+ return
+ }
+
+ // Otherwise this is a full reload message
// Extract data from message
const {
selectedComponentId,