Make block ejection work properly via the builder rather than client app button hack

This commit is contained in:
Andrew Kingston 2022-08-23 19:05:38 +01:00
parent eb3f7ba810
commit 06eee0d386
8 changed files with 114 additions and 10 deletions

View File

@ -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: {

View File

@ -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}`)
}

View File

@ -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

View File

@ -1,6 +1,7 @@
<script>
import { getContext, setContext } from "svelte"
import { builderStore } from "../stores/builder.js"
import { getContext, onDestroy, onMount, setContext } from "svelte"
import { builderStore } from "stores/builder.js"
import { blockStore } from "stores/blocks.js"
import { Button } from "@budibase/bbui"
const component = getContext("component")
@ -55,6 +56,18 @@
// blocks later on
registerComponent: registerBlockComponent,
})
onMount(() => {
if ($builderStore.inBuilder) {
blockStore.actions.registerBlock($component.id, { eject })
}
})
onDestroy(() => {
if ($builderStore.inBuilder) {
blockStore.actions.unregisterBlock($component.id)
}
})
</script>
<slot />

View File

@ -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({

View File

@ -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()

View File

@ -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"

View File

@ -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,