diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/AppPreview.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/AppPreview.svelte index cd86d2e87c..ff880cd5e8 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/AppPreview.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/AppPreview.svelte @@ -41,23 +41,10 @@ } // Construct iframe template - $: pluginLinks = generatePluginLinks($store.usedPlugins) - $: template = iframeTemplate - .replace(/\{\{ CLIENT_LIB_PATH }}/, $store.clientLibPath) - .replace(/\{\{ PLUGINS }}/, pluginLinks) - - const generatePluginLinks = plugins => { - if (!plugins?.length) { - return "" - } - return plugins - .map(plugin => { - // Split up like this as otherwise parsing fails because the script - // tags confuse svelte - return `<` + `script src="/plugins/${plugin.jsUrl}">` - }) - .join("") - } + $: template = iframeTemplate.replace( + /\{\{ CLIENT_LIB_PATH }}/, + $store.clientLibPath + ) const placeholderScreen = new Screen() .name("Screen Placeholder") diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/iframeTemplate.js b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/iframeTemplate.js index 2b24ee4fa0..8711f5d670 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/iframeTemplate.js +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/iframeTemplate.js @@ -37,7 +37,6 @@ export default ` } - {{ PLUGINS }} {#if $auth.isAdmin} @@ -106,6 +112,7 @@ } }} /> + diff --git a/packages/client/src/components/ClientApp.svelte b/packages/client/src/components/ClientApp.svelte index 64b1712b89..86fd56feb1 100644 --- a/packages/client/src/components/ClientApp.svelte +++ b/packages/client/src/components/ClientApp.svelte @@ -87,6 +87,14 @@ }) + + {#if $builderStore.usedPlugins?.length} + {#each $builderStore.usedPlugins as plugin} + + {/each} + {/if} + + {#if dataLoaded}
{ + const initialise = (instance, force = false) => { if (instance == null) { return } // Ensure we're processing a new instance const instanceKey = Helpers.hashString(JSON.stringify(instance)) - if (instanceKey === lastInstanceKey) { + if (instanceKey === lastInstanceKey && !force) { return } else { lastInstanceKey = instanceKey @@ -407,9 +407,11 @@ !componentStore.actions.isComponentRegistered(id) ) { componentStore.actions.registerInstance(id, { + component: instance._component, getSettings: () => cachedSettings, getRawSettings: () => ({ ...staticSettings, ...dynamicSettings }), getDataContext: () => get(context), + reload: () => initialise(instance, true), }) } }) diff --git a/packages/client/src/components/devtools/DevToolsStatsTab.svelte b/packages/client/src/components/devtools/DevToolsStatsTab.svelte index b20b9fafa0..24f587332c 100644 --- a/packages/client/src/components/devtools/DevToolsStatsTab.svelte +++ b/packages/client/src/components/devtools/DevToolsStatsTab.svelte @@ -19,7 +19,10 @@ label="Active screen" value={$screenStore.activeScreen?.routing.route} /> - + diff --git a/packages/client/src/index.js b/packages/client/src/index.js index 32b242bc69..bef3ab9c12 100644 --- a/packages/client/src/index.js +++ b/packages/client/src/index.js @@ -27,6 +27,7 @@ const loadBudibase = () => { previewDevice: window["##BUDIBASE_PREVIEW_DEVICE##"], navigation: window["##BUDIBASE_PREVIEW_NAVIGATION##"], hiddenComponentIds: window["##BUDIBASE_HIDDEN_COMPONENT_IDS##"], + usedPlugins: window["##BUDIBASE_USED_PLUGINS##"], }) // Set app ID - this window flag is set by both the preview and the real @@ -39,6 +40,11 @@ const loadBudibase = () => { devToolsStore.actions.setEnabled(enableDevTools) // Register any custom components + window.registerCustomComponent = plugin => { + componentStore.actions.registerCustomComponent(plugin) + console.log("registered!") + loadBudibase() + } if (window["##BUDIBASE_CUSTOM_COMPONENTS##"]) { window["##BUDIBASE_CUSTOM_COMPONENTS##"].forEach(component => { componentStore.actions.registerCustomComponent(component) diff --git a/packages/client/src/stores/components.js b/packages/client/src/stores/components.js index a07c9da996..94dd16a957 100644 --- a/packages/client/src/stores/components.js +++ b/packages/client/src/stores/components.js @@ -10,7 +10,11 @@ import * as AppComponents from "../components/app/index.js" const budibasePrefix = "@budibase/standard-components/" const createComponentStore = () => { - const store = writable({}) + const store = writable({ + customComponentManifest: {}, + componentsAwaitingConstructors: {}, + mountedComponents: {}, + }) const derivedStore = derived( [store, builderStore, devToolsStore, screenStore], @@ -29,9 +33,7 @@ const createComponentStore = () => { asset = $screenState.activeScreen } const component = findComponentById(asset?.props, selectedComponentId) - const prefix = "@budibase/standard-components/" - const type = component?._component?.replace(prefix, "") - const definition = type ? Manifest[type] : null + const definition = getComponentDefinition(component?._component) // Derive the selected component path const path = @@ -39,32 +41,50 @@ const createComponentStore = () => { return { customComponentManifest: $store.customComponentManifest, - selectedComponentInstance: $store[selectedComponentId], + selectedComponentInstance: + $store.mountedComponents[selectedComponentId], selectedComponent: component, selectedComponentDefinition: definition, selectedComponentPath: path?.map(component => component._id), - mountedComponents: Object.keys($store).length, + mountedComponentCount: Object.keys($store.mountedComponents).length, currentAsset: asset, } } ) const registerInstance = (id, instance) => { - store.update(state => ({ - ...state, - [id]: instance, - })) + store.update(state => { + // If this is a custom component and does not have an implementation yet, + // store so we can reload this component later + const component = instance.component + let cac = state.componentsAwaitingConstructors + if (!getComponentConstructor(component)) { + if (!cac[component]) { + cac[component] = [] + } + cac[component].push(id) + } + + return { + ...state, + componentsAwaitingConstructors: cac, + mountedComponents: { + ...state.mountedComponents, + [id]: instance, + }, + } + }) } const unregisterInstance = id => { store.update(state => { - delete state[id] + delete state.mountedComponents[id] return state }) } const isComponentRegistered = id => { - return get(store)[id] != null + return get(store).mountedComponents[id] != null } const getComponentById = id => { @@ -117,17 +137,32 @@ const createComponentStore = () => { if (!Component || !schema?.schema?.name) { return } + const componentName = `plugin/${schema.schema.name}/1.0.0` store.update(state => { if (!state.customComponentManifest) { state.customComponentManifest = {} } - const componentName = `plugin/${schema.schema.name}/1.0.0` state.customComponentManifest[componentName] = { schema, Component, } return state }) + + // Reload any mounted components which depend on this definition + const state = get(store) + if (state.componentsAwaitingConstructors[componentName]?.length) { + state.componentsAwaitingConstructors[componentName].forEach(id => { + const instance = state.mountedComponents[id] + if (instance) { + instance.reload() + } + }) + store.update(state => { + delete state.componentsAwaitingConstructors[componentName] + return state + }) + } } return {