Make block ejection work properly via the builder rather than client app button hack
This commit is contained in:
parent
eb3f7ba810
commit
06eee0d386
8 changed files with 114 additions and 10 deletions
|
@ -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: {
|
||||
|
|
|
@ -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}`)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 />
|
||||
|
|
|
@ -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({
|
||||
|
|
34
packages/client/src/stores/blocks.js
Normal file
34
packages/client/src/stores/blocks.js
Normal 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()
|
|
@ -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"
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue