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 25454bff9d
commit 80bcfd2692
8 changed files with 114 additions and 10 deletions

View File

@ -354,6 +354,16 @@ export const getFrontendStore = () => {
return state return state
}) })
}, },
sendEvent: (name, payload) => {
const { previewEventHandler } = get(store)
previewEventHandler?.(name, payload)
},
registerEventHandler: handler => {
store.update(state => {
state.previewEventHandler = handler
return state
})
},
}, },
layouts: { layouts: {
select: layoutId => { select: layoutId => {
@ -895,7 +905,12 @@ export const getFrontendStore = () => {
component[name] = value 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 => { await store.actions.screens.patch(screen => {
const parent = findComponentParent(screen.props, componentId) const parent = findComponentParent(screen.props, componentId)
@ -908,8 +923,18 @@ export const getFrontendStore = () => {
const childIndex = parent._children.findIndex( const childIndex = parent._children.findIndex(
child => child._id === componentId child => child._id === componentId
) )
makeComponentUnique(ejectedDefinition)
parent._children[childIndex] = ejectedDefinition parent._children[childIndex] = ejectedDefinition
nextSelectedComponentId = ejectedDefinition._id
}) })
// Select new root component
if (nextSelectedComponentId) {
store.update(state => {
state.selectedComponentId = nextSelectedComponentId
return state
})
}
}, },
}, },
links: { links: {

View File

@ -96,11 +96,21 @@
`./components/${$selectedComponent?._id}/new` `./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 // Update the iframe with the builder info to render the correct preview
const refreshContent = message => { const refreshContent = message => {
if (iframe) { iframe?.contentWindow.postMessage(message)
iframe.contentWindow.postMessage(message)
}
} }
const receiveMessage = message => { const receiveMessage = message => {
@ -198,7 +208,7 @@
} }
} else if (type === "eject-block") { } else if (type === "eject-block") {
const { id, definition } = data const { id, definition } = data
await store.actions.components.ejectBlock(id, definition) await store.actions.components.handleEjectBlock(id, definition)
} else { } else {
console.warn(`Client sent unknown event type: ${type}`) console.warn(`Client sent unknown event type: ${type}`)
} }

View File

@ -112,9 +112,9 @@
} else if (e.key === "Enter") { } else if (e.key === "Enter") {
e.preventDefault() e.preventDefault()
$goto("./new") $goto("./new")
} else if (e.key === "Enter") { } else if (e.key === "e") {
e.preventDefault() e.preventDefault()
await store.actions.components.ejectBlock() await store.actions.components.requestEjectBlock(component?._id)
} }
} else if (e.key === "Backspace" || e.key === "Delete") { } else if (e.key === "Backspace" || e.key === "Delete") {
// Don't show confirmation for the screen itself // Don't show confirmation for the screen itself

View File

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

View File

@ -1,5 +1,5 @@
import ClientApp from "./components/ClientApp.svelte" 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 loadSpectrumIcons from "@budibase/bbui/spectrum-icons-rollup.js"
import { get } from "svelte/store" import { get } from "svelte/store"
@ -32,6 +32,17 @@ const loadBudibase = () => {
const enableDevTools = !get(builderStore).inBuilder && get(appStore).isDevApp const enableDevTools = !get(builderStore).inBuilder && get(appStore).isDevApp
devToolsStore.actions.setEnabled(enableDevTools) 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 // Create app if one hasn't been created yet
if (!app) { if (!app) {
app = new ClientApp({ 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 { componentStore } from "./components"
export { uploadStore } from "./uploads.js" export { uploadStore } from "./uploads.js"
export { rowSelectionStore } from "./rowSelection.js" export { rowSelectionStore } from "./rowSelection.js"
export { blockStore } from "./blocks.js"
// Context stores are layered and duplicated, so it is not a singleton // Context stores are layered and duplicated, so it is not a singleton
export { createContextStore } from "./context" export { createContextStore } from "./context"

View File

@ -56,6 +56,16 @@
return 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 // Extract data from message
const { const {
selectedComponentId, selectedComponentId,