refactor switch into a key value handler object

tidy up - remove logs and comments

update windowed modals to use postMessage
This commit is contained in:
Martin McKeaveney 2021-10-28 19:40:46 +02:00
parent 969d2f5377
commit 193d40bbf3
6 changed files with 84 additions and 58 deletions

View File

@ -33,6 +33,16 @@
.instanceName("Content Placeholder") .instanceName("Content Placeholder")
.json() .json()
// Messages that can be sent from the iframe preview to the builder
// Budibase events are and initalisation events
const MessageTypes = {
IFRAME_LOADED: "iframe-loaded",
READY: "ready",
ERROR: "error",
BUDIBASE: "type",
KEYDOWN: "keydown"
}
// Construct iframe template // Construct iframe template
$: template = iframeTemplate.replace( $: template = iframeTemplate.replace(
/\{\{ CLIENT_LIB_PATH }}/, /\{\{ CLIENT_LIB_PATH }}/,
@ -80,46 +90,44 @@
// Refresh the preview when required // Refresh the preview when required
$: refreshContent(strippedJson) $: refreshContent(strippedJson)
onMount(() => { function receiveMessage(message) {
const handlers = {
[MessageTypes.READY]: () => {
// Initialise the app when mounted // Initialise the app when mounted
iframe.contentWindow.addEventListener(
"ready",
() => {
// Display preview immediately if the intelligent loading feature // Display preview immediately if the intelligent loading feature
// is not supported // is not supported
if (!loading) return
if (!$store.clientFeatures.intelligentLoading) { if (!$store.clientFeatures.intelligentLoading) {
loading = false loading = false
} }
refreshContent(strippedJson) refreshContent(strippedJson)
}, },
{ once: true } [MessageTypes.ERROR]: event => {
)
// Catch any app errors // Catch any app errors
iframe.contentWindow.addEventListener(
"error",
event => {
loading = false loading = false
error = event.detail || "An unknown error occurred" error = event.error || "An unknown error occurred"
}, },
{ once: true } [MessageTypes.KEYDOWN]: handleKeydownEvent
) }
// Add listener for events sent by client library in preview const messageHandler = handlers[message.data.type] || handleBudibaseEvent
iframe.contentWindow.addEventListener("bb-event", handleBudibaseEvent) messageHandler(message)
iframe.contentWindow.addEventListener("keydown", handleKeydownEvent) }
onMount(() => {
window.addEventListener("message", receiveMessage)
}) })
// Remove all iframe event listeners on component destroy // Remove all iframe event listeners on component destroy
onDestroy(() => { onDestroy(() => {
if (iframe.contentWindow) { if (iframe.contentWindow) {
iframe.contentWindow.removeEventListener("bb-event", handleBudibaseEvent) window.removeEventListener("message", receiveMessage) //
iframe.contentWindow.removeEventListener("keydown", handleKeydownEvent)
} }
}) })
const handleBudibaseEvent = event => { const handleBudibaseEvent = event => {
const { type, data } = event.detail const { type, data } = event.data
if (type === "select-component" && data.id) { if (type === "select-component" && data.id) {
store.actions.components.select({ _id: data.id }) store.actions.components.select({ _id: data.id })
} else if (type === "update-prop") { } else if (type === "update-prop") {
@ -151,13 +159,14 @@
store.actions.components.paste(destination, data.mode) store.actions.components.paste(destination, data.mode)
} }
} else { } else {
console.warning(`Client sent unknown event type: ${type}`) console.warn(`Client sent unknown event type: ${type}`)
} }
} }
const handleKeydownEvent = event => { const handleKeydownEvent = event => {
const { key } = event.data
if ( if (
(event.key === "Delete" || event.key === "Backspace") && (key === "Delete" || key === "Backspace") &&
selectedComponentId && selectedComponentId &&
["input", "textarea"].indexOf( ["input", "textarea"].indexOf(
iframe.contentWindow.document.activeElement?.tagName.toLowerCase() iframe.contentWindow.document.activeElement?.tagName.toLowerCase()

View File

@ -84,17 +84,20 @@ export default `
if (window.loadBudibase) { if (window.loadBudibase) {
window.loadBudibase() window.loadBudibase()
document.documentElement.classList.add("loaded") document.documentElement.classList.add("loaded")
window.dispatchEvent(new Event("iframe-loaded")) window.parent.postMessage({ type: "iframe-loaded" })
} else { } else {
throw "The client library couldn't be loaded" throw "The client library couldn't be loaded"
} }
} catch (error) { } catch (error) {
window.dispatchEvent(new CustomEvent("error", { detail: error })) window.parent.postMessage({ type: "error", error })
} }
} }
window.addEventListener("message", receiveMessage) window.addEventListener("message", receiveMessage)
window.dispatchEvent(new Event("ready")) window.addEventListener("keydown", evt => {
window.parent.postMessage({ type: "keydown", key: event.key })
})
window.parent.postMessage({ type: "ready" })
</script> </script>
</head> </head>
<body/> <body/>

View File

@ -8,6 +8,12 @@
import { Modal, ModalContent, ActionButton } from "@budibase/bbui" import { Modal, ModalContent, ActionButton } from "@budibase/bbui"
import { onDestroy } from "svelte" import { onDestroy } from "svelte"
const MessageTypes = {
NOTIFICATION: "notification",
CLOSE_SCREEN_MODAL: "close-screen-modal",
INVALIDATE_DATASOURCE: "invalidate-datasource",
}
let iframe let iframe
let listenersAttached = false let listenersAttached = false
@ -21,32 +27,33 @@
notificationStore.actions.send(message, type, icon) notificationStore.actions.send(message, type, icon)
} }
function receiveMessage(message) {
const handlers = {
[MessageTypes.NOTIFICATION]: () => {
proxyNotification(message.data)
},
[MessageTypes.CLOSE_SCREEN_MODAL]: peekStore.actions.hidePeek,
[MessageTypes.INVALIDATE_DATASOURCE]: () => {
invalidateDataSource(message.data)
},
}
const messageHandler = handlers[message.data.type]
if (messageHandler) {
messageHandler(message)
} else {
console.warning("Unknown event type", message?.data?.type)
}
}
const attachListeners = () => { const attachListeners = () => {
// Mirror datasource invalidation to keep the parent window up to date // Mirror datasource invalidation to keep the parent window up to date
iframe.contentWindow.addEventListener( window.addEventListener("message", receiveMessage)
"invalidate-datasource",
invalidateDataSource
)
// Listen for a close event to close the screen peek
iframe.contentWindow.addEventListener(
"close-screen-modal",
peekStore.actions.hidePeek
)
// Proxy notifications back to the parent window instead of iframe
iframe.contentWindow.addEventListener("notification", proxyNotification)
} }
const handleCancel = () => { const handleCancel = () => {
peekStore.actions.hidePeek() peekStore.actions.hidePeek()
iframe.contentWindow.removeEventListener( window.removeEventListener("message", receiveMessage)
"invalidate-datasource",
invalidateDataSource
)
iframe.contentWindow.removeEventListener(
"close-screen-modal",
peekStore.actions.hidePeek
)
iframe.contentWindow.removeEventListener("notification", proxyNotification)
} }
const handleFullscreen = () => { const handleFullscreen = () => {

View File

@ -4,11 +4,7 @@ import { findComponentById, findComponentPathById } from "../utils/components"
import { pingEndUser } from "../api" import { pingEndUser } from "../api"
const dispatchEvent = (type, data = {}) => { const dispatchEvent = (type, data = {}) => {
window.dispatchEvent( window.parent.postMessage({ type, data })
new CustomEvent("bb-event", {
detail: { type, data },
})
)
} }
const createBuilderStore = () => { const createBuilderStore = () => {

View File

@ -26,11 +26,19 @@ const createNotificationStore = () => {
// If peeking, pass notifications back to parent window // If peeking, pass notifications back to parent window
if (get(routeStore).queryParams?.peek) { if (get(routeStore).queryParams?.peek) {
window.dispatchEvent( window.parent.postMessage({
new CustomEvent("notification", { type: "notification",
detail: { message, type, icon }, detail: {
message,
type,
icon,
},
}) })
) // window.dispatchEvent(
// new CustomEvent("notification", {
// detail: { message, type, icon },
// })
// )
return return
} }

View File

@ -124,7 +124,10 @@ function copyExistingPropsOver(
return table return table
} }
export function finaliseExternalTables(tables: { [key: string]: any }, entities: { [key: string]: any }) { export function finaliseExternalTables(
tables: { [key: string]: any },
entities: { [key: string]: any }
) {
const finalTables: { [key: string]: any } = {} const finalTables: { [key: string]: any } = {}
const errors: { [key: string]: string } = {} const errors: { [key: string]: string } = {}
for (let [name, table] of Object.entries(tables)) { for (let [name, table] of Object.entries(tables)) {