diff --git a/packages/client/src/components/Component.svelte b/packages/client/src/components/Component.svelte index 801080fea2..b17dd0d7d2 100644 --- a/packages/client/src/components/Component.svelte +++ b/packages/client/src/components/Component.svelte @@ -1,5 +1,6 @@ {#if constructor} {#if children && children.length} - {#each children as child} + {#each children as child (child._id)} {/each} {/if} diff --git a/packages/client/src/components/DataProvider.svelte b/packages/client/src/components/DataProvider.svelte index 48465599f2..08defe93b6 100644 --- a/packages/client/src/components/DataProvider.svelte +++ b/packages/client/src/components/DataProvider.svelte @@ -1,25 +1,19 @@ -{#if loaded} - -{/if} + diff --git a/packages/client/src/components/Router.svelte b/packages/client/src/components/Router.svelte index 693ec2dd28..4921e65c87 100644 --- a/packages/client/src/components/Router.svelte +++ b/packages/client/src/components/Router.svelte @@ -1,7 +1,7 @@ {#if routerConfig} -
+
{/if} diff --git a/packages/client/src/components/Screen.svelte b/packages/client/src/components/Screen.svelte index ac17556113..6a65e89afd 100644 --- a/packages/client/src/components/Screen.svelte +++ b/packages/client/src/components/Screen.svelte @@ -11,8 +11,11 @@ // Redirect to home page if no matching route $: screenDefinition == null && routeStore.actions.navigate("/") + + // Make a screen array so we can use keying to properly re-render each screen + $: screens = screenDefinition ? [screenDefinition] : [] -{#if screenDefinition} - -{/if} +{#each screens as screen (screen._id)} + +{/each} diff --git a/packages/client/src/index.js b/packages/client/src/index.js index e925c3e273..2925e950f6 100644 --- a/packages/client/src/index.js +++ b/packages/client/src/index.js @@ -1,16 +1,22 @@ import ClientApp from "./components/ClientApp.svelte" +import { builderStore } from "./store" let app const loadBudibase = () => { - // Destroy old app if one exists - if (app) { - app.$destroy() - } - // Create new app - app = new ClientApp({ - target: window.document.body, + // Update builder store with any builder flags + builderStore.set({ + inBuilder: !!window["##BUDIBASE_IN_BUILDER##"], + page: window["##BUDIBASE_PREVIEW_PAGE##"], + screen: window["##BUDIBASE_PREVIEW_SCREEN##"], }) + + // Create app if one hasn't been created yet + if (!app) { + app = new ClientApp({ + target: window.document.body, + }) + } } // Attach to window so the HTML template can call this when it loads diff --git a/packages/client/src/store/builder.js b/packages/client/src/store/builder.js new file mode 100644 index 0000000000..4b197596be --- /dev/null +++ b/packages/client/src/store/builder.js @@ -0,0 +1,12 @@ +import { writable } from "svelte/store" + +const createBuilderStore = () => { + const initialState = { + inBuilder: false, + page: null, + screen: null, + } + return writable(initialState) +} + +export const builderStore = createBuilderStore() diff --git a/packages/client/src/store/index.js b/packages/client/src/store/index.js index a0683ba47e..4416ca809a 100644 --- a/packages/client/src/store/index.js +++ b/packages/client/src/store/index.js @@ -2,3 +2,4 @@ export { authStore } from "./auth" export { routeStore } from "./routes" export { screenStore } from "./screens" export { createDataContextStore } from "./dataContext" +export { builderStore } from "./builder" diff --git a/packages/client/src/store/screens.js b/packages/client/src/store/screens.js index a39a67e3d4..f19736756a 100644 --- a/packages/client/src/store/screens.js +++ b/packages/client/src/store/screens.js @@ -1,5 +1,6 @@ import { writable, derived } from "svelte/store" import { routeStore } from "./routes" +import { builderStore } from "./builder" import * as API from "../api" import { getAppId } from "../utils" @@ -8,36 +9,37 @@ const createScreenStore = () => { screens: [], page: {}, }) - const store = derived([config, routeStore], ([$config, $routeStore]) => { - const { screens, page } = $config - const activeScreen = - screens.length === 1 - ? screens[0] - : screens.find( + const store = derived( + [config, routeStore, builderStore], + ([$config, $routeStore, $builderStore]) => { + let page + let activeScreen + if ($builderStore.inBuilder) { + // Use builder defined definitions if inside the builder preview + page = $builderStore.page + activeScreen = $builderStore.screen + } else { + // Otherwise find the correct screen by matching the current route + page = $config.page + const { screens } = $config + if (screens.length === 1) { + activeScreen = screens[0] + } else { + activeScreen = screens.find( screen => screen.routing.route === $routeStore.activeRoute ) - return { - screens, - page, - activeScreen, + } + } + return { page, activeScreen } } - }) + ) const fetchScreens = async () => { - let screens - let page - const inBuilder = !!window["##BUDIBASE_IN_BUILDER##"] - if (inBuilder) { - // Load screen and page from the window object if in the builder - screens = [window["##BUDIBASE_PREVIEW_SCREEN##"]] - page = window["##BUDIBASE_PREVIEW_PAGE##"] - } else { - // Otherwise load from API - const appDefinition = await API.fetchAppDefinition(getAppId()) - screens = appDefinition.screens - page = appDefinition.page - } - config.set({ screens, page }) + const appDefinition = await API.fetchAppDefinition(getAppId()) + config.set({ + screens: appDefinition.screens, + page: appDefinition.page, + }) } return { diff --git a/packages/client/src/utils/styleable.js b/packages/client/src/utils/styleable.js index 8f1d7ac3e1..fa81308cb2 100644 --- a/packages/client/src/utils/styleable.js +++ b/packages/client/src/utils/styleable.js @@ -1,7 +1,13 @@ +import { getContext } from "svelte" +import { get } from "svelte/store" + +/** + * Helper to build a CSS string from a style object + */ const buildStyleString = styles => { let str = "" Object.entries(styles).forEach(([style, value]) => { - if (style && value) { + if (style && value != null) { str += `${style}: ${value}; ` } }) @@ -12,35 +18,52 @@ const buildStyleString = styles => { * Svelte action to apply correct component styles. */ export const styleable = (node, styles = {}) => { - const normalStyles = styles.normal || {} - const hoverStyles = { - ...normalStyles, - ...styles.hover, + let applyNormalStyles + let applyHoverStyles + + // Creates event listeners and applies initial styles + const setupStyles = newStyles => { + const normalStyles = newStyles.normal || {} + const hoverStyles = { + ...normalStyles, + ...newStyles.hover, + } + + applyNormalStyles = () => { + node.style = buildStyleString(normalStyles) + } + + applyHoverStyles = () => { + node.style = buildStyleString(hoverStyles) + } + + // Add listeners to toggle hover styles + node.addEventListener("mouseover", applyHoverStyles) + node.addEventListener("mouseout", applyNormalStyles) + node.setAttribute("data-bb-id", newStyles.id) + + // Apply initial normal styles + applyNormalStyles() } - function applyNormalStyles() { - node.style = buildStyleString(normalStyles) + // Removes the current event listeners + const removeListeners = () => { + node.removeEventListener("mouseover", applyHoverStyles) + node.removeEventListener("mouseout", applyNormalStyles) } - function applyHoverStyles() { - node.style = buildStyleString(hoverStyles) - } - - // Add listeners to toggle hover styles - node.addEventListener("mouseover", applyHoverStyles) - node.addEventListener("mouseout", applyNormalStyles) - - // Apply normal styles initially - applyNormalStyles() - - // Also apply data tags so we know how to reference each component - node.setAttribute("data-bb-id", styles.id) + // Apply initial styles + setupStyles(styles) return { - // Clean up event listeners when component is destroyed + // Clean up old listeners and apply new ones on update + update: newStyles => { + removeListeners() + setupStyles(newStyles) + }, + // Clean up listeners when component is destroyed destroy: () => { - node.removeEventListener("mouseover", applyHoverStyles) - node.removeEventListener("mouseout", applyNormalStyles) + removeListeners() }, } } diff --git a/packages/standard-components/src/Button.svelte b/packages/standard-components/src/Button.svelte index e23adf6c15..6dbbcfc4cf 100644 --- a/packages/standard-components/src/Button.svelte +++ b/packages/standard-components/src/Button.svelte @@ -9,7 +9,7 @@ export let text - diff --git a/packages/standard-components/src/Card.svelte b/packages/standard-components/src/Card.svelte index cce45c0bf0..e4fcd4a7d6 100644 --- a/packages/standard-components/src/Card.svelte +++ b/packages/standard-components/src/Card.svelte @@ -26,7 +26,7 @@ $: showImage = !!imageUrl -
+
{#if showImage}{/if}

{heading}

diff --git a/packages/standard-components/src/CardHorizontal.svelte b/packages/standard-components/src/CardHorizontal.svelte index e681f08823..ea4fb3734e 100644 --- a/packages/standard-components/src/CardHorizontal.svelte +++ b/packages/standard-components/src/CardHorizontal.svelte @@ -29,7 +29,7 @@ $: showImage = !!imageUrl -
+
{#if showImage}{/if}
diff --git a/packages/standard-components/src/Container.svelte b/packages/standard-components/src/Container.svelte index 2a9f469701..546787dc5d 100644 --- a/packages/standard-components/src/Container.svelte +++ b/packages/standard-components/src/Container.svelte @@ -9,55 +9,55 @@ {#if type === 'div'} -
+
{:else if type === 'header'} -
+
{:else if type === 'main'} -
+
{:else if type === 'footer'} -