Refactor app preview event sending to support async callbacks in client library
This commit is contained in:
parent
986141b8b1
commit
8d644c5a10
|
@ -103,7 +103,7 @@
|
||||||
)
|
)
|
||||||
|
|
||||||
// Register handler to send custom to the preview
|
// Register handler to send custom to the preview
|
||||||
$: store.actions.preview.registerEventHandler((name, payload) => {
|
$: sendPreviewEvent = (name, payload) => {
|
||||||
iframe?.contentWindow.postMessage(
|
iframe?.contentWindow.postMessage(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
name,
|
name,
|
||||||
|
@ -112,37 +112,30 @@
|
||||||
runtimeEvent: true,
|
runtimeEvent: true,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
})
|
}
|
||||||
|
$: store.actions.preview.registerEventHandler(sendPreviewEvent)
|
||||||
|
|
||||||
// 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 => {
|
||||||
iframe?.contentWindow.postMessage(message)
|
iframe?.contentWindow.postMessage(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
const receiveMessage = message => {
|
const receiveMessage = async message => {
|
||||||
const handlers = {
|
if (!message?.data?.type) {
|
||||||
[MessageTypes.READY]: () => {
|
return
|
||||||
// Initialise the app when mounted
|
|
||||||
if ($store.clientFeatures.messagePassing) {
|
|
||||||
if (!loading) return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display preview immediately if the intelligent loading feature
|
|
||||||
// is not supported
|
|
||||||
if (!$store.clientFeatures.intelligentLoading) {
|
|
||||||
loading = false
|
|
||||||
}
|
|
||||||
refreshContent(json)
|
|
||||||
},
|
|
||||||
[MessageTypes.ERROR]: event => {
|
|
||||||
// Catch any app errors
|
|
||||||
loading = false
|
|
||||||
error = event.error || "An unknown error occurred"
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const messageHandler = handlers[message.data.type] || handleBudibaseEvent
|
// Await the event handler
|
||||||
messageHandler(message)
|
try {
|
||||||
|
await handleBudibaseEvent(message)
|
||||||
|
} catch (error) {
|
||||||
|
notifications.error(error || "Error handling event from app preview")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply that the event has been completed
|
||||||
|
if (message.data?.id) {
|
||||||
|
sendPreviewEvent("event-completed", message.data?.id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleBudibaseEvent = async event => {
|
const handleBudibaseEvent = async event => {
|
||||||
|
@ -150,83 +143,94 @@
|
||||||
if (!type) {
|
if (!type) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (type === MessageTypes.READY) {
|
||||||
try {
|
// Initialise the app when mounted
|
||||||
if (type === "select-component" && data.id) {
|
if ($store.clientFeatures.messagePassing) {
|
||||||
$store.selectedComponentId = data.id
|
if (!loading) return
|
||||||
if (!$isActive("./components")) {
|
|
||||||
$goto("./components")
|
|
||||||
}
|
|
||||||
} else if (type === "update-prop") {
|
|
||||||
await store.actions.components.updateSetting(data.prop, data.value)
|
|
||||||
} else if (type === "update-styles") {
|
|
||||||
await store.actions.components.updateStyles(data.styles, data.id)
|
|
||||||
} else if (type === "delete-component" && data.id) {
|
|
||||||
// Legacy type, can be deleted in future
|
|
||||||
confirmDeleteComponent(data.id)
|
|
||||||
} else if (type === "key-down") {
|
|
||||||
const { key, ctrlKey } = data
|
|
||||||
document.dispatchEvent(new KeyboardEvent("keydown", { key, ctrlKey }))
|
|
||||||
} else if (type === "duplicate-component" && data.id) {
|
|
||||||
const rootComponent = get(currentAsset).props
|
|
||||||
const component = findComponent(rootComponent, data.id)
|
|
||||||
store.actions.components.copy(component)
|
|
||||||
await store.actions.components.paste(component)
|
|
||||||
} else if (type === "preview-loaded") {
|
|
||||||
// Wait for this event to show the client library if intelligent
|
|
||||||
// loading is supported
|
|
||||||
loading = false
|
|
||||||
} else if (type === "move-component") {
|
|
||||||
const { componentId, destinationComponentId } = data
|
|
||||||
const rootComponent = get(currentAsset).props
|
|
||||||
|
|
||||||
// Get source and destination components
|
|
||||||
const source = findComponent(rootComponent, componentId)
|
|
||||||
const destination = findComponent(rootComponent, destinationComponentId)
|
|
||||||
|
|
||||||
// Stop if the target is a child of source
|
|
||||||
const path = findComponentPath(source, destinationComponentId)
|
|
||||||
const ids = path.map(component => component._id)
|
|
||||||
if (ids.includes(data.destinationComponentId)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cut and paste the component to the new destination
|
|
||||||
if (source && destination) {
|
|
||||||
store.actions.components.copy(source, true, false)
|
|
||||||
await store.actions.components.paste(destination, data.mode)
|
|
||||||
}
|
|
||||||
} else if (type === "click-nav") {
|
|
||||||
if (!$isActive("./navigation")) {
|
|
||||||
$goto("./navigation")
|
|
||||||
}
|
|
||||||
} else if (type === "request-add-component") {
|
|
||||||
toggleAddComponent()
|
|
||||||
} else if (type === "highlight-setting") {
|
|
||||||
store.actions.settings.highlight(data.setting)
|
|
||||||
|
|
||||||
// Also scroll setting into view
|
|
||||||
const selector = `[data-cy="${data.setting}-prop-control"`
|
|
||||||
const element = document.querySelector(selector)?.parentElement
|
|
||||||
if (element) {
|
|
||||||
element.scrollIntoView({
|
|
||||||
behavior: "smooth",
|
|
||||||
block: "center",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else if (type === "eject-block") {
|
|
||||||
const { id, definition } = data
|
|
||||||
await store.actions.components.handleEjectBlock(id, definition)
|
|
||||||
} else if (type === "reload-plugin") {
|
|
||||||
await store.actions.components.refreshDefinitions()
|
|
||||||
} else if (type === "drop-new-component") {
|
|
||||||
const { component, parent, index } = data
|
|
||||||
await store.actions.components.create(component, null, parent, index)
|
|
||||||
} else {
|
|
||||||
console.warn(`Client sent unknown event type: ${type}`)
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
|
||||||
notifications.error(error || "Error handling event from app preview")
|
// Display preview immediately if the intelligent loading feature
|
||||||
|
// is not supported
|
||||||
|
if (!$store.clientFeatures.intelligentLoading) {
|
||||||
|
loading = false
|
||||||
|
}
|
||||||
|
refreshContent(json)
|
||||||
|
} else if (type === MessageTypes.ERROR) {
|
||||||
|
// Catch any app errors
|
||||||
|
loading = false
|
||||||
|
error = event.error || "An unknown error occurred"
|
||||||
|
} else if (type === "select-component" && data.id) {
|
||||||
|
$store.selectedComponentId = data.id
|
||||||
|
if (!$isActive("./components")) {
|
||||||
|
$goto("./components")
|
||||||
|
}
|
||||||
|
} else if (type === "update-prop") {
|
||||||
|
await store.actions.components.updateSetting(data.prop, data.value)
|
||||||
|
} else if (type === "update-styles") {
|
||||||
|
await store.actions.components.updateStyles(data.styles, data.id)
|
||||||
|
} else if (type === "delete-component" && data.id) {
|
||||||
|
// Legacy type, can be deleted in future
|
||||||
|
confirmDeleteComponent(data.id)
|
||||||
|
} else if (type === "key-down") {
|
||||||
|
const { key, ctrlKey } = data
|
||||||
|
document.dispatchEvent(new KeyboardEvent("keydown", { key, ctrlKey }))
|
||||||
|
} else if (type === "duplicate-component" && data.id) {
|
||||||
|
const rootComponent = get(currentAsset).props
|
||||||
|
const component = findComponent(rootComponent, data.id)
|
||||||
|
store.actions.components.copy(component)
|
||||||
|
await store.actions.components.paste(component)
|
||||||
|
} else if (type === "preview-loaded") {
|
||||||
|
// Wait for this event to show the client library if intelligent
|
||||||
|
// loading is supported
|
||||||
|
loading = false
|
||||||
|
} else if (type === "move-component") {
|
||||||
|
const { componentId, destinationComponentId } = data
|
||||||
|
const rootComponent = get(currentAsset).props
|
||||||
|
|
||||||
|
// Get source and destination components
|
||||||
|
const source = findComponent(rootComponent, componentId)
|
||||||
|
const destination = findComponent(rootComponent, destinationComponentId)
|
||||||
|
|
||||||
|
// Stop if the target is a child of source
|
||||||
|
const path = findComponentPath(source, destinationComponentId)
|
||||||
|
const ids = path.map(component => component._id)
|
||||||
|
if (ids.includes(data.destinationComponentId)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cut and paste the component to the new destination
|
||||||
|
if (source && destination) {
|
||||||
|
store.actions.components.copy(source, true, false)
|
||||||
|
await store.actions.components.paste(destination, data.mode)
|
||||||
|
}
|
||||||
|
} else if (type === "click-nav") {
|
||||||
|
if (!$isActive("./navigation")) {
|
||||||
|
$goto("./navigation")
|
||||||
|
}
|
||||||
|
} else if (type === "request-add-component") {
|
||||||
|
toggleAddComponent()
|
||||||
|
} else if (type === "highlight-setting") {
|
||||||
|
store.actions.settings.highlight(data.setting)
|
||||||
|
|
||||||
|
// Also scroll setting into view
|
||||||
|
const selector = `[data-cy="${data.setting}-prop-control"`
|
||||||
|
const element = document.querySelector(selector)?.parentElement
|
||||||
|
if (element) {
|
||||||
|
element.scrollIntoView({
|
||||||
|
behavior: "smooth",
|
||||||
|
block: "center",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else if (type === "eject-block") {
|
||||||
|
const { id, definition } = data
|
||||||
|
await store.actions.components.handleEjectBlock(id, definition)
|
||||||
|
} else if (type === "reload-plugin") {
|
||||||
|
await store.actions.components.refreshDefinitions()
|
||||||
|
} else if (type === "drop-new-component") {
|
||||||
|
const { component, parent, index } = data
|
||||||
|
await store.actions.components.create(component, null, parent, index)
|
||||||
|
} else {
|
||||||
|
console.warn(`Client sent unknown event type: ${type}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -259,42 +263,10 @@
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
window.addEventListener("message", receiveMessage)
|
window.addEventListener("message", receiveMessage)
|
||||||
if (!$store.clientFeatures.messagePassing) {
|
|
||||||
// Legacy - remove in later versions of BB
|
|
||||||
iframe.contentWindow.addEventListener(
|
|
||||||
"ready",
|
|
||||||
() => {
|
|
||||||
receiveMessage({ data: { type: MessageTypes.READY } })
|
|
||||||
},
|
|
||||||
{ once: true }
|
|
||||||
)
|
|
||||||
iframe.contentWindow.addEventListener(
|
|
||||||
"error",
|
|
||||||
event => {
|
|
||||||
receiveMessage({
|
|
||||||
data: { type: MessageTypes.ERROR, error: event.detail },
|
|
||||||
})
|
|
||||||
},
|
|
||||||
{ once: true }
|
|
||||||
)
|
|
||||||
// Add listener for events sent by client library in preview
|
|
||||||
iframe.contentWindow.addEventListener("bb-event", handleBudibaseEvent)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// Remove all iframe event listeners on component destroy
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
window.removeEventListener("message", receiveMessage)
|
window.removeEventListener("message", receiveMessage)
|
||||||
|
|
||||||
if (iframe.contentWindow) {
|
|
||||||
if (!$store.clientFeatures.messagePassing) {
|
|
||||||
// Legacy - remove in later versions of BB
|
|
||||||
iframe.contentWindow.removeEventListener(
|
|
||||||
"bb-event",
|
|
||||||
handleBudibaseEvent
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,9 @@
|
||||||
$: target = $dndStore.target
|
$: target = $dndStore.target
|
||||||
$: drop = $dndStore.drop
|
$: drop = $dndStore.drop
|
||||||
|
|
||||||
|
// Local flag for whether we are awaiting an async drop event
|
||||||
|
let dropping = false
|
||||||
|
|
||||||
const insideGrid = e => {
|
const insideGrid = e => {
|
||||||
return e.target
|
return e.target
|
||||||
?.closest?.(".component")
|
?.closest?.(".component")
|
||||||
|
@ -48,6 +51,10 @@
|
||||||
|
|
||||||
// Callback when drag stops (whether dropped or not)
|
// Callback when drag stops (whether dropped or not)
|
||||||
const stopDragging = () => {
|
const stopDragging = () => {
|
||||||
|
if (dropping) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Reset listener
|
// Reset listener
|
||||||
if (source?.id) {
|
if (source?.id) {
|
||||||
const component = document.getElementsByClassName(source?.id)[0]
|
const component = document.getElementsByClassName(source?.id)[0]
|
||||||
|
@ -57,9 +64,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset state
|
// Reset state
|
||||||
if (!$dndStore.dropped) {
|
dndStore.actions.reset()
|
||||||
dndStore.actions.reset()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Callback when initially starting a drag on a draggable component
|
// Callback when initially starting a drag on a draggable component
|
||||||
|
@ -253,18 +258,21 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// Callback when dropping a drag on top of some component
|
// Callback when dropping a drag on top of some component
|
||||||
const onDrop = () => {
|
const onDrop = async () => {
|
||||||
if (!source || !drop?.parent || drop?.index == null) {
|
if (!source || !drop?.parent || drop?.index == null) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if we're adding a new component rather than moving one
|
// Check if we're adding a new component rather than moving one
|
||||||
if (source.newComponentType) {
|
if (source.newComponentType) {
|
||||||
builderStore.actions.dropNewComponent(
|
dropping = true
|
||||||
|
await builderStore.actions.dropNewComponent(
|
||||||
source.newComponentType,
|
source.newComponentType,
|
||||||
drop.parent,
|
drop.parent,
|
||||||
drop.index
|
drop.index
|
||||||
)
|
)
|
||||||
|
dropping = false
|
||||||
|
stopDragging()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -301,12 +309,14 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
if (legacyDropTarget && legacyDropMode) {
|
if (legacyDropTarget && legacyDropMode) {
|
||||||
dndStore.actions.markDropped()
|
dropping = true
|
||||||
builderStore.actions.moveComponent(
|
await builderStore.actions.moveComponent(
|
||||||
source.id,
|
source.id,
|
||||||
legacyDropTarget,
|
legacyDropTarget,
|
||||||
legacyDropMode
|
legacyDropMode
|
||||||
)
|
)
|
||||||
|
dropping = false
|
||||||
|
stopDragging()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {
|
||||||
componentStore,
|
componentStore,
|
||||||
environmentStore,
|
environmentStore,
|
||||||
dndStore,
|
dndStore,
|
||||||
|
eventStore,
|
||||||
} from "./stores"
|
} 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"
|
||||||
|
@ -42,9 +43,9 @@ const loadBudibase = async () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Reset DND state if we completed a successful drop
|
// Reset DND state if we completed a successful drop
|
||||||
if (get(dndStore).dropped) {
|
// if (get(dndStore).dropped) {
|
||||||
dndStore.actions.reset()
|
// dndStore.actions.reset()
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Set app ID - this window flag is set by both the preview and the real
|
// Set app ID - this window flag is set by both the preview and the real
|
||||||
// server rendered app HTML
|
// server rendered app HTML
|
||||||
|
@ -59,15 +60,17 @@ const loadBudibase = async () => {
|
||||||
devToolsStore.actions.setEnabled(enableDevTools)
|
devToolsStore.actions.setEnabled(enableDevTools)
|
||||||
|
|
||||||
// Register handler for runtime events from the builder
|
// Register handler for runtime events from the builder
|
||||||
window.handleBuilderRuntimeEvent = (name, payload) => {
|
window.handleBuilderRuntimeEvent = (type, data) => {
|
||||||
if (!window["##BUDIBASE_IN_BUILDER##"]) {
|
if (!window["##BUDIBASE_IN_BUILDER##"]) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (name === "eject-block") {
|
if (type === "event-completed") {
|
||||||
const block = blockStore.actions.getBlock(payload)
|
eventStore.actions.resolveEvent(data)
|
||||||
|
} else if (type === "eject-block") {
|
||||||
|
const block = blockStore.actions.getBlock(data)
|
||||||
block?.eject()
|
block?.eject()
|
||||||
} else if (name === "dragging-new-component") {
|
} else if (type === "dragging-new-component") {
|
||||||
const { dragging, component } = payload
|
const { dragging, component } = data
|
||||||
if (dragging) {
|
if (dragging) {
|
||||||
const definition =
|
const definition =
|
||||||
componentStore.actions.getComponentDefinition(component)
|
componentStore.actions.getComponentDefinition(component)
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
import { writable, get } from "svelte/store"
|
import { writable, get } from "svelte/store"
|
||||||
import { API } from "api"
|
import { API } from "api"
|
||||||
import { devToolsStore } from "./devTools.js"
|
import { devToolsStore } from "./devTools.js"
|
||||||
|
import { eventStore } from "./events.js"
|
||||||
const dispatchEvent = (type, data = {}) => {
|
|
||||||
window.parent.postMessage({ type, data })
|
|
||||||
}
|
|
||||||
|
|
||||||
const createBuilderStore = () => {
|
const createBuilderStore = () => {
|
||||||
const initialState = {
|
const initialState = {
|
||||||
|
@ -19,6 +16,7 @@ const createBuilderStore = () => {
|
||||||
navigation: null,
|
navigation: null,
|
||||||
hiddenComponentIds: [],
|
hiddenComponentIds: [],
|
||||||
usedPlugins: null,
|
usedPlugins: null,
|
||||||
|
eventResolvers: {},
|
||||||
|
|
||||||
// Legacy - allow the builder to specify a layout
|
// Legacy - allow the builder to specify a layout
|
||||||
layout: null,
|
layout: null,
|
||||||
|
@ -35,25 +33,25 @@ const createBuilderStore = () => {
|
||||||
selectedComponentId: id,
|
selectedComponentId: id,
|
||||||
}))
|
}))
|
||||||
devToolsStore.actions.setAllowSelection(false)
|
devToolsStore.actions.setAllowSelection(false)
|
||||||
dispatchEvent("select-component", { id })
|
eventStore.actions.dispatchEvent("select-component", { id })
|
||||||
},
|
},
|
||||||
updateProp: (prop, value) => {
|
updateProp: (prop, value) => {
|
||||||
dispatchEvent("update-prop", { prop, value })
|
eventStore.actions.dispatchEvent("update-prop", { prop, value })
|
||||||
},
|
},
|
||||||
updateStyles: (styles, id) => {
|
updateStyles: (styles, id) => {
|
||||||
dispatchEvent("update-styles", { styles, id })
|
eventStore.actions.dispatchEvent("update-styles", { styles, id })
|
||||||
},
|
},
|
||||||
keyDown: (key, ctrlKey) => {
|
keyDown: (key, ctrlKey) => {
|
||||||
dispatchEvent("key-down", { key, ctrlKey })
|
eventStore.actions.dispatchEvent("key-down", { key, ctrlKey })
|
||||||
},
|
},
|
||||||
duplicateComponent: id => {
|
duplicateComponent: id => {
|
||||||
dispatchEvent("duplicate-component", { id })
|
eventStore.actions.dispatchEvent("duplicate-component", { id })
|
||||||
},
|
},
|
||||||
deleteComponent: id => {
|
deleteComponent: id => {
|
||||||
dispatchEvent("delete-component", { id })
|
eventStore.actions.dispatchEvent("delete-component", { id })
|
||||||
},
|
},
|
||||||
notifyLoaded: () => {
|
notifyLoaded: () => {
|
||||||
dispatchEvent("preview-loaded")
|
eventStore.actions.dispatchEvent("preview-loaded")
|
||||||
},
|
},
|
||||||
analyticsPing: async () => {
|
analyticsPing: async () => {
|
||||||
try {
|
try {
|
||||||
|
@ -62,15 +60,15 @@ const createBuilderStore = () => {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
moveComponent: (componentId, destinationComponentId, mode) => {
|
moveComponent: async (componentId, destinationComponentId, mode) => {
|
||||||
dispatchEvent("move-component", {
|
await eventStore.actions.dispatchEvent("move-component", {
|
||||||
componentId,
|
componentId,
|
||||||
destinationComponentId,
|
destinationComponentId,
|
||||||
mode,
|
mode,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
dropNewComponent: (component, parent, index) => {
|
dropNewComponent: (component, parent, index) => {
|
||||||
dispatchEvent("drop-new-component", {
|
eventStore.actions.dispatchEvent("drop-new-component", {
|
||||||
component,
|
component,
|
||||||
parent,
|
parent,
|
||||||
index,
|
index,
|
||||||
|
@ -83,16 +81,16 @@ const createBuilderStore = () => {
|
||||||
store.update(state => ({ ...state, editMode: enabled }))
|
store.update(state => ({ ...state, editMode: enabled }))
|
||||||
},
|
},
|
||||||
clickNav: () => {
|
clickNav: () => {
|
||||||
dispatchEvent("click-nav")
|
eventStore.actions.dispatchEvent("click-nav")
|
||||||
},
|
},
|
||||||
requestAddComponent: () => {
|
requestAddComponent: () => {
|
||||||
dispatchEvent("request-add-component")
|
eventStore.actions.dispatchEvent("request-add-component")
|
||||||
},
|
},
|
||||||
highlightSetting: setting => {
|
highlightSetting: setting => {
|
||||||
dispatchEvent("highlight-setting", { setting })
|
eventStore.actions.dispatchEvent("highlight-setting", { setting })
|
||||||
},
|
},
|
||||||
ejectBlock: (id, definition) => {
|
ejectBlock: (id, definition) => {
|
||||||
dispatchEvent("eject-block", { id, definition })
|
eventStore.actions.dispatchEvent("eject-block", { id, definition })
|
||||||
},
|
},
|
||||||
updateUsedPlugin: (name, hash) => {
|
updateUsedPlugin: (name, hash) => {
|
||||||
// Check if we used this plugin
|
// Check if we used this plugin
|
||||||
|
@ -109,7 +107,7 @@ const createBuilderStore = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify the builder so we can reload component definitions
|
// Notify the builder so we can reload component definitions
|
||||||
dispatchEvent("reload-plugin")
|
eventStore.actions.dispatchEvent("reload-plugin")
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -11,9 +11,6 @@ const createDndStore = () => {
|
||||||
|
|
||||||
// Info about where the component would be dropped
|
// Info about where the component would be dropped
|
||||||
drop: null,
|
drop: null,
|
||||||
|
|
||||||
// Whether the current drop has been completed successfully
|
|
||||||
dropped: false,
|
|
||||||
}
|
}
|
||||||
const store = writable(initialState)
|
const store = writable(initialState)
|
||||||
|
|
||||||
|
@ -63,13 +60,6 @@ const createDndStore = () => {
|
||||||
store.set(initialState)
|
store.set(initialState)
|
||||||
}
|
}
|
||||||
|
|
||||||
const markDropped = () => {
|
|
||||||
store.update(state => {
|
|
||||||
state.dropped = true
|
|
||||||
return state
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
subscribe: store.subscribe,
|
subscribe: store.subscribe,
|
||||||
actions: {
|
actions: {
|
||||||
|
@ -78,7 +68,6 @@ const createDndStore = () => {
|
||||||
updateTarget,
|
updateTarget,
|
||||||
updateDrop,
|
updateDrop,
|
||||||
reset,
|
reset,
|
||||||
markDropped,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,7 +80,6 @@ export const dndStore = createDndStore()
|
||||||
// or components which depend on DND state unless values actually change.
|
// or components which depend on DND state unless values actually change.
|
||||||
export const dndParent = computed(dndStore, x => x.drop?.parent)
|
export const dndParent = computed(dndStore, x => x.drop?.parent)
|
||||||
export const dndIndex = computed(dndStore, x => x.drop?.index)
|
export const dndIndex = computed(dndStore, x => x.drop?.index)
|
||||||
export const dndDropped = computed(dndStore, x => x.dropped)
|
|
||||||
export const dndBounds = computed(dndStore, x => x.source?.bounds)
|
export const dndBounds = computed(dndStore, x => x.source?.bounds)
|
||||||
export const dndIsDragging = computed(dndStore, x => !!x.source)
|
export const dndIsDragging = computed(dndStore, x => !!x.source)
|
||||||
export const dndIsNewComponent = computed(
|
export const dndIsNewComponent = computed(
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
import { writable, get } from "svelte/store"
|
||||||
|
|
||||||
|
const createEventStore = () => {
|
||||||
|
const initialState = {
|
||||||
|
eventResolvers: {},
|
||||||
|
}
|
||||||
|
const store = writable(initialState)
|
||||||
|
|
||||||
|
const actions = {
|
||||||
|
dispatchEvent: (type, data) => {
|
||||||
|
const id = Math.random()
|
||||||
|
return new Promise(resolve => {
|
||||||
|
window.parent.postMessage({ type, data, id })
|
||||||
|
store.update(state => {
|
||||||
|
state.eventResolvers[id] = resolve
|
||||||
|
return state
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
resolveEvent: data => {
|
||||||
|
get(store).eventResolvers[data]?.()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
subscribe: store.subscribe,
|
||||||
|
actions,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const eventStore = createEventStore()
|
|
@ -15,6 +15,7 @@ export { uploadStore } from "./uploads.js"
|
||||||
export { rowSelectionStore } from "./rowSelection.js"
|
export { rowSelectionStore } from "./rowSelection.js"
|
||||||
export { blockStore } from "./blocks.js"
|
export { blockStore } from "./blocks.js"
|
||||||
export { environmentStore } from "./environment"
|
export { environmentStore } from "./environment"
|
||||||
|
export { eventStore } from "./events.js"
|
||||||
export {
|
export {
|
||||||
dndStore,
|
dndStore,
|
||||||
dndIndex,
|
dndIndex,
|
||||||
|
@ -22,7 +23,6 @@ export {
|
||||||
dndBounds,
|
dndBounds,
|
||||||
dndIsNewComponent,
|
dndIsNewComponent,
|
||||||
dndIsDragging,
|
dndIsDragging,
|
||||||
dndDropped,
|
|
||||||
} from "./dnd"
|
} from "./dnd"
|
||||||
|
|
||||||
// Context stores are layered and duplicated, so it is not a singleton
|
// Context stores are layered and duplicated, so it is not a singleton
|
||||||
|
|
|
@ -2,13 +2,7 @@ import { derived } from "svelte/store"
|
||||||
import { routeStore } from "./routes"
|
import { routeStore } from "./routes"
|
||||||
import { builderStore } from "./builder"
|
import { builderStore } from "./builder"
|
||||||
import { appStore } from "./app"
|
import { appStore } from "./app"
|
||||||
import {
|
import { dndIndex, dndParent, dndIsNewComponent, dndBounds } from "./dnd.js"
|
||||||
dndIndex,
|
|
||||||
dndParent,
|
|
||||||
dndIsNewComponent,
|
|
||||||
dndBounds,
|
|
||||||
dndDropped,
|
|
||||||
} from "./dnd.js"
|
|
||||||
import { RoleUtils } from "@budibase/frontend-core"
|
import { RoleUtils } from "@budibase/frontend-core"
|
||||||
import { findComponentById, findComponentParent } from "../utils/components.js"
|
import { findComponentById, findComponentParent } from "../utils/components.js"
|
||||||
import { Helpers } from "@budibase/bbui"
|
import { Helpers } from "@budibase/bbui"
|
||||||
|
@ -24,7 +18,6 @@ const createScreenStore = () => {
|
||||||
dndIndex,
|
dndIndex,
|
||||||
dndIsNewComponent,
|
dndIsNewComponent,
|
||||||
dndBounds,
|
dndBounds,
|
||||||
dndDropped,
|
|
||||||
],
|
],
|
||||||
([
|
([
|
||||||
$appStore,
|
$appStore,
|
||||||
|
@ -34,7 +27,6 @@ const createScreenStore = () => {
|
||||||
$dndIndex,
|
$dndIndex,
|
||||||
$dndIsNewComponent,
|
$dndIsNewComponent,
|
||||||
$dndBounds,
|
$dndBounds,
|
||||||
$dndDropped,
|
|
||||||
]) => {
|
]) => {
|
||||||
let activeLayout, activeScreen
|
let activeLayout, activeScreen
|
||||||
let screens
|
let screens
|
||||||
|
@ -80,9 +72,6 @@ const createScreenStore = () => {
|
||||||
activeScreen.props,
|
activeScreen.props,
|
||||||
selectedComponentId
|
selectedComponentId
|
||||||
)
|
)
|
||||||
const selectedComponent = selectedParent?._children?.find(
|
|
||||||
x => x._id === selectedComponentId
|
|
||||||
)
|
|
||||||
|
|
||||||
// Remove selected component from tree if we are moving an existing
|
// Remove selected component from tree if we are moving an existing
|
||||||
// component
|
// component
|
||||||
|
@ -92,33 +81,25 @@ const createScreenStore = () => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert placeholder component if dragging, or artificially insert
|
// Insert placeholder component
|
||||||
// the dropped component in the new location if the drop completed
|
const componentToInsert = {
|
||||||
let componentToInsert
|
_component: "@budibase/standard-components/container",
|
||||||
if ($dndDropped && !$dndIsNewComponent) {
|
_id: DNDPlaceholderID,
|
||||||
componentToInsert = selectedComponent
|
_styles: {
|
||||||
} else {
|
normal: {
|
||||||
componentToInsert = {
|
width: `${$dndBounds?.width || 400}px`,
|
||||||
_component: "@budibase/standard-components/container",
|
height: `${$dndBounds?.height || 200}px`,
|
||||||
_id: DNDPlaceholderID,
|
opacity: 0,
|
||||||
_styles: {
|
|
||||||
normal: {
|
|
||||||
width: `${$dndBounds?.width || 400}px`,
|
|
||||||
height: `${$dndBounds?.height || 200}px`,
|
|
||||||
opacity: 0,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
static: true,
|
},
|
||||||
}
|
static: true,
|
||||||
}
|
}
|
||||||
if (componentToInsert) {
|
let parent = findComponentById(activeScreen.props, $dndParent)
|
||||||
let parent = findComponentById(activeScreen.props, $dndParent)
|
if (parent) {
|
||||||
if (parent) {
|
if (!parent._children?.length) {
|
||||||
if (!parent._children?.length) {
|
parent._children = [componentToInsert]
|
||||||
parent._children = [componentToInsert]
|
} else {
|
||||||
} else {
|
parent._children.splice($dndIndex, 0, componentToInsert)
|
||||||
parent._children.splice($dndIndex, 0, componentToInsert)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue