From 1fb1e70b2872059480ad9d57acf4caaea9060ca8 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 28 Apr 2022 12:05:34 +0100 Subject: [PATCH] Improve urlStateSync utility, improve routing structure, improve component tree --- packages/builder/package.json | 2 +- packages/builder/src/helpers/urlStateSync.js | 147 +++++++++--------- .../app/[application]/_fallback.svelte | 5 + .../_components/AppPanel.svelte | 5 +- .../_components/AppPreview.svelte | 0 .../_components/DevicePreviewSelect.svelte | 0 .../_components/iframeTemplate.js | 0 .../design/[screenId]/_fallback.svelte | 5 + .../design/{ => [screenId]}/_layout.svelte | 24 ++- .../navigation/ComponentDropdownMenu.svelte | 0 .../ComponentNavigationPanel.svelte | 5 +- .../navigation/ComponentTree.svelte | 0 .../navigation/ScreenslotDropdownMenu.svelte | 0 .../_components/navigation/dragDropStore.js | 0 .../settings/ComponentSettingsPanel.svelte | 0 .../settings/ComponentSettingsSection.svelte | 0 .../settings/ConditionalUIDrawer.svelte | 0 .../settings/ConditionalUISection.svelte | 0 .../settings/CustomStylesSection.svelte | 0 .../_components/settings/DesignSection.svelte | 0 .../_components/settings/StyleSection.svelte | 0 .../_components/settings/componentStyles.js | 0 .../components}/[componentId]/_layout.svelte | 21 +-- .../components}/[componentId]/index.svelte | 0 .../components}/index.svelte | 0 .../new/_components/NewComponentPanel.svelte | 0 .../NewComponentTargetPanel.svelte | 0 .../new/_components/componentStructure.json | 0 .../components}/new/index.svelte | 0 .../design/[screenId]/index.svelte | 5 + .../{ => [screenId]}/layouts/index.svelte | 0 .../_components/NavigationDrawer.svelte | 0 .../_components/NavigationEditor.svelte | 0 .../{ => [screenId]}/navigation/index.svelte | 0 .../_components/DatasourceModal.svelte | 0 .../screens/_components/NewScreenModal.svelte | 0 .../_components/ScreenDetailsModal.svelte | 0 .../_components/ScreenDropdownMenu.svelte | 0 .../_components/ScreenNavigationPanel.svelte | 0 .../_components/ScreenSettingsPanel.svelte | 0 .../screens/_components/ScreenWizard.svelte | 0 .../design/[screenId]/screens/index.svelte | 7 + .../theme/_components/AppThemeSelect.svelte | 0 .../theme/_components/ThemeEditor.svelte | 0 .../{ => [screenId]}/theme/index.svelte | 0 .../components/[screenId]/_layout.svelte | 27 ---- .../design/components/index.svelte | 21 --- .../app/[application]/design/index.svelte | 18 ++- .../design/screens/[screenId].svelte | 29 ---- .../[application]/design/screens/index.svelte | 21 --- .../builder/app/[application]/index.svelte | 2 - packages/builder/yarn.lock | 8 +- 52 files changed, 145 insertions(+), 207 deletions(-) create mode 100644 packages/builder/src/pages/builder/app/[application]/_fallback.svelte rename packages/builder/src/pages/builder/app/[application]/design/{ => [screenId]}/_components/AppPanel.svelte (89%) rename packages/builder/src/pages/builder/app/[application]/design/{ => [screenId]}/_components/AppPreview.svelte (100%) rename packages/builder/src/pages/builder/app/[application]/design/{ => [screenId]}/_components/DevicePreviewSelect.svelte (100%) rename packages/builder/src/pages/builder/app/[application]/design/{ => [screenId]}/_components/iframeTemplate.js (100%) create mode 100644 packages/builder/src/pages/builder/app/[application]/design/[screenId]/_fallback.svelte rename packages/builder/src/pages/builder/app/[application]/design/{ => [screenId]}/_layout.svelte (74%) rename packages/builder/src/pages/builder/app/[application]/design/{components/[screenId] => [screenId]/components}/[componentId]/_components/navigation/ComponentDropdownMenu.svelte (100%) rename packages/builder/src/pages/builder/app/[application]/design/{components/[screenId] => [screenId]/components}/[componentId]/_components/navigation/ComponentNavigationPanel.svelte (94%) rename packages/builder/src/pages/builder/app/[application]/design/{components/[screenId] => [screenId]/components}/[componentId]/_components/navigation/ComponentTree.svelte (100%) rename packages/builder/src/pages/builder/app/[application]/design/{components/[screenId] => [screenId]/components}/[componentId]/_components/navigation/ScreenslotDropdownMenu.svelte (100%) rename packages/builder/src/pages/builder/app/[application]/design/{components/[screenId] => [screenId]/components}/[componentId]/_components/navigation/dragDropStore.js (100%) rename packages/builder/src/pages/builder/app/[application]/design/{components/[screenId] => [screenId]/components}/[componentId]/_components/settings/ComponentSettingsPanel.svelte (100%) rename packages/builder/src/pages/builder/app/[application]/design/{components/[screenId] => [screenId]/components}/[componentId]/_components/settings/ComponentSettingsSection.svelte (100%) rename packages/builder/src/pages/builder/app/[application]/design/{components/[screenId] => [screenId]/components}/[componentId]/_components/settings/ConditionalUIDrawer.svelte (100%) rename packages/builder/src/pages/builder/app/[application]/design/{components/[screenId] => [screenId]/components}/[componentId]/_components/settings/ConditionalUISection.svelte (100%) rename packages/builder/src/pages/builder/app/[application]/design/{components/[screenId] => [screenId]/components}/[componentId]/_components/settings/CustomStylesSection.svelte (100%) rename packages/builder/src/pages/builder/app/[application]/design/{components/[screenId] => [screenId]/components}/[componentId]/_components/settings/DesignSection.svelte (100%) rename packages/builder/src/pages/builder/app/[application]/design/{components/[screenId] => [screenId]/components}/[componentId]/_components/settings/StyleSection.svelte (100%) rename packages/builder/src/pages/builder/app/[application]/design/{components/[screenId] => [screenId]/components}/[componentId]/_components/settings/componentStyles.js (100%) rename packages/builder/src/pages/builder/app/[application]/design/{components/[screenId] => [screenId]/components}/[componentId]/_layout.svelte (51%) rename packages/builder/src/pages/builder/app/[application]/design/{components/[screenId] => [screenId]/components}/[componentId]/index.svelte (100%) rename packages/builder/src/pages/builder/app/[application]/design/{components/[screenId] => [screenId]/components}/index.svelte (100%) rename packages/builder/src/pages/builder/app/[application]/design/{components/[screenId] => [screenId]/components}/new/_components/NewComponentPanel.svelte (100%) rename packages/builder/src/pages/builder/app/[application]/design/{components/[screenId] => [screenId]/components}/new/_components/NewComponentTargetPanel.svelte (100%) rename packages/builder/src/pages/builder/app/[application]/design/{components/[screenId] => [screenId]/components}/new/_components/componentStructure.json (100%) rename packages/builder/src/pages/builder/app/[application]/design/{components/[screenId] => [screenId]/components}/new/index.svelte (100%) create mode 100644 packages/builder/src/pages/builder/app/[application]/design/[screenId]/index.svelte rename packages/builder/src/pages/builder/app/[application]/design/{ => [screenId]}/layouts/index.svelte (100%) rename packages/builder/src/pages/builder/app/[application]/design/{ => [screenId]}/navigation/_components/NavigationDrawer.svelte (100%) rename packages/builder/src/pages/builder/app/[application]/design/{ => [screenId]}/navigation/_components/NavigationEditor.svelte (100%) rename packages/builder/src/pages/builder/app/[application]/design/{ => [screenId]}/navigation/index.svelte (100%) rename packages/builder/src/pages/builder/app/[application]/design/{ => [screenId]}/screens/_components/DatasourceModal.svelte (100%) rename packages/builder/src/pages/builder/app/[application]/design/{ => [screenId]}/screens/_components/NewScreenModal.svelte (100%) rename packages/builder/src/pages/builder/app/[application]/design/{ => [screenId]}/screens/_components/ScreenDetailsModal.svelte (100%) rename packages/builder/src/pages/builder/app/[application]/design/{ => [screenId]}/screens/_components/ScreenDropdownMenu.svelte (100%) rename packages/builder/src/pages/builder/app/[application]/design/{ => [screenId]}/screens/_components/ScreenNavigationPanel.svelte (100%) rename packages/builder/src/pages/builder/app/[application]/design/{ => [screenId]}/screens/_components/ScreenSettingsPanel.svelte (100%) rename packages/builder/src/pages/builder/app/[application]/design/{ => [screenId]}/screens/_components/ScreenWizard.svelte (100%) create mode 100644 packages/builder/src/pages/builder/app/[application]/design/[screenId]/screens/index.svelte rename packages/builder/src/pages/builder/app/[application]/design/{ => [screenId]}/theme/_components/AppThemeSelect.svelte (100%) rename packages/builder/src/pages/builder/app/[application]/design/{ => [screenId]}/theme/_components/ThemeEditor.svelte (100%) rename packages/builder/src/pages/builder/app/[application]/design/{ => [screenId]}/theme/index.svelte (100%) delete mode 100644 packages/builder/src/pages/builder/app/[application]/design/components/[screenId]/_layout.svelte delete mode 100644 packages/builder/src/pages/builder/app/[application]/design/components/index.svelte delete mode 100644 packages/builder/src/pages/builder/app/[application]/design/screens/[screenId].svelte delete mode 100644 packages/builder/src/pages/builder/app/[application]/design/screens/index.svelte diff --git a/packages/builder/package.json b/packages/builder/package.json index fa64a8acb2..a2b96e4601 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -90,7 +90,7 @@ "@babel/preset-env": "^7.13.12", "@babel/runtime": "^7.13.10", "@rollup/plugin-replace": "^2.4.2", - "@roxi/routify": "2.18.0", + "@roxi/routify": "2.18.5", "@sveltejs/vite-plugin-svelte": "1.0.0-next.19", "@testing-library/jest-dom": "^5.11.10", "@testing-library/svelte": "^3.0.0", diff --git a/packages/builder/src/helpers/urlStateSync.js b/packages/builder/src/helpers/urlStateSync.js index 2e043fbcf5..f6357ae5d9 100644 --- a/packages/builder/src/helpers/urlStateSync.js +++ b/packages/builder/src/helpers/urlStateSync.js @@ -2,13 +2,26 @@ import { get } from "svelte/store" import { isChangingPage } from "@roxi/routify" export const syncURLToState = options => { - const { keys, params, store, goto, redirect, baseUrl = "." } = options || {} + const { + urlParam, + stateKey, + validate, + baseUrl = "..", + fallbackUrl, + store, + routify, + } = options || {} if ( - !keys?.length || - !params?.subscribe || + !urlParam || + !stateKey || + !baseUrl || + !urlParam || !store?.subscribe || - !goto?.subscribe || - !redirect?.subscribe + !routify || + !routify.params?.subscribe || + !routify.goto?.subscribe || + !routify.redirect?.subscribe || + !routify.page?.subscribe ) { console.warn("syncURLToState invoked with missing parameters") return @@ -18,96 +31,70 @@ export const syncURLToState = options => { // to just subscribe and cache the latest versions. // We can grab their initial values as this is during component // initialisation. - let cachedParams = get(params) - let cachedGoto = get(goto) - let cachedRedirect = get(redirect) - let hydrated = false + let cachedParams = get(routify.params) + let cachedGoto = get(routify.goto) + let cachedRedirect = get(routify.redirect) + let cachedPage = get(routify.page) + let previousParamsHash = null let debug = false const log = (...params) => debug && console.log(...params) // Navigate to a certain URL - const gotoUrl = url => { - if (get(isChangingPage) && hydrated) { - return - } - log("Navigating to", url) - cachedGoto(url) + const gotoUrl = (url, params) => { + log("Navigating to", url, "with params", params) + cachedGoto(url, params) } // Redirect to a certain URL const redirectUrl = url => { - if (get(isChangingPage) && hydrated) { - return - } log("Redirecting to", url) cachedRedirect(url) } // Updates state with new URL params const mapUrlToState = params => { - // Determine any required state updates - let stateUpdates = [] - const state = get(store) - for (let key of keys) { - const urlValue = params?.[key.url] - const stateValue = state?.[key.state] - if (urlValue && urlValue !== stateValue) { - log( - `state.${key.state} (${stateValue}) <= url.${key.url} (${urlValue})` - ) - stateUpdates.push(state => { - state[key.state] = urlValue - }) - if (key.validate && key.fallbackUrl) { - if (!key.validate(urlValue)) { - log("Invalid URL param!") - redirectUrl(key.fallbackUrl) - hydrated = true - return - } - } - } - } - - // Mark our initial hydration as completed - hydrated = true - - // Avoid updating the store at all if not necessary to prevent a wasted - // store invalidation - if (!stateUpdates.length) { + // Check if we have new URL params + const paramsHash = JSON.stringify(params) + const newParams = paramsHash !== previousParamsHash + previousParamsHash = paramsHash + const urlValue = params?.[urlParam] + const stateValue = get(store)?.[stateKey] + if (!newParams || !urlValue) { return } - // Apply the required state updates - log("Performing", stateUpdates.length, "state updates") - store.update(state => { - for (let update of stateUpdates) { - update(state) + // Check if new value is valid + if (validate && fallbackUrl) { + if (!validate(urlValue)) { + log("Invalid URL param!") + redirectUrl(fallbackUrl) + return } - return state - }) + } + + // Only update state if we have a new value + if (urlValue !== stateValue) { + log(`state.${stateKey} (${stateValue}) <= url.${urlParam} (${urlValue})`) + store.update(state => { + state[stateKey] = urlValue + return state + }) + } } // Updates the URL with new state values const mapStateToUrl = state => { - // Determine new URL while checking for changes - let url = baseUrl let needsUpdate = false - for (let key of keys) { - const urlValue = cachedParams?.[key.url] - const stateValue = state?.[key.state] - url += `/${stateValue}` - if (stateValue !== urlValue) { - needsUpdate = true - log( - `url.${key.url} (${urlValue}) <= state.${key.state} (${stateValue})` - ) - if (key.validate && key.fallbackUrl) { - if (!key.validate(stateValue)) { - log("Invalid state param!") - redirectUrl(key.fallbackUrl) - return - } + const urlValue = cachedParams?.[urlParam] + const stateValue = state?.[stateKey] + if (stateValue !== urlValue) { + needsUpdate = true + log(`url.${urlParam} (${urlValue}) <= state.${stateKey} (${stateValue})`) + if (validate && fallbackUrl) { + if (!validate(stateValue)) { + log("Invalid state param!") + redirectUrl(fallbackUrl) + return } } } @@ -120,7 +107,11 @@ export const syncURLToState = options => { // Navigate to the new URL if (!get(isChangingPage)) { - gotoUrl(url) + const newUrlParams = { + ...cachedParams, + [urlParam]: stateValue, + } + gotoUrl(cachedPage.path, newUrlParams) } } @@ -128,18 +119,21 @@ export const syncURLToState = options => { mapUrlToState(cachedParams) // Subscribe to URL changes and cache them - const unsubscribeParams = params.subscribe($urlParams => { + const unsubscribeParams = routify.params.subscribe($urlParams => { cachedParams = $urlParams mapUrlToState($urlParams) }) // Subscribe to routify store changes and cache them - const unsubscribeGoto = goto.subscribe($goto => { + const unsubscribeGoto = routify.goto.subscribe($goto => { cachedGoto = $goto }) - const unsubscribeRedirect = redirect.subscribe($redirect => { + const unsubscribeRedirect = routify.redirect.subscribe($redirect => { cachedRedirect = $redirect }) + const unsubscribePage = routify.page.subscribe($page => { + cachedPage = $page + }) // Subscribe to store changes and keep URL up to date const unsubscribeStore = store.subscribe(mapStateToUrl) @@ -149,6 +143,7 @@ export const syncURLToState = options => { unsubscribeParams() unsubscribeGoto() unsubscribeRedirect() + unsubscribePage() unsubscribeStore() } } diff --git a/packages/builder/src/pages/builder/app/[application]/_fallback.svelte b/packages/builder/src/pages/builder/app/[application]/_fallback.svelte new file mode 100644 index 0000000000..24c5c56780 --- /dev/null +++ b/packages/builder/src/pages/builder/app/[application]/_fallback.svelte @@ -0,0 +1,5 @@ + diff --git a/packages/builder/src/pages/builder/app/[application]/design/_components/AppPanel.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/AppPanel.svelte similarity index 89% rename from packages/builder/src/pages/builder/app/[application]/design/_components/AppPanel.svelte rename to packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/AppPanel.svelte index bb5db46e40..f83a5ada0f 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/_components/AppPanel.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/AppPanel.svelte @@ -11,9 +11,6 @@ $: roleColor = getRoleColor(roleId) $: roleName = $roles.find(x => x._id === roleId)?.name || "Unknown" - // Needs to be absolute as we embed this component from multiple different URLs - $: newComponentUrl = `/builder/app/${$store.appId}/design/components/${$selectedScreen?._id}/new` - const getRoleColor = roleId => { return RoleColours[roleId] || "#ffa500" } @@ -42,7 +39,7 @@ diff --git a/packages/builder/src/pages/builder/app/[application]/design/_components/AppPreview.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/AppPreview.svelte similarity index 100% rename from packages/builder/src/pages/builder/app/[application]/design/_components/AppPreview.svelte rename to packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/AppPreview.svelte diff --git a/packages/builder/src/pages/builder/app/[application]/design/_components/DevicePreviewSelect.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/DevicePreviewSelect.svelte similarity index 100% rename from packages/builder/src/pages/builder/app/[application]/design/_components/DevicePreviewSelect.svelte rename to packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/DevicePreviewSelect.svelte diff --git a/packages/builder/src/pages/builder/app/[application]/design/_components/iframeTemplate.js b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/iframeTemplate.js similarity index 100% rename from packages/builder/src/pages/builder/app/[application]/design/_components/iframeTemplate.js rename to packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/iframeTemplate.js diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_fallback.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_fallback.svelte new file mode 100644 index 0000000000..00165e4ee9 --- /dev/null +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_fallback.svelte @@ -0,0 +1,5 @@ + diff --git a/packages/builder/src/pages/builder/app/[application]/design/_layout.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_layout.svelte similarity index 74% rename from packages/builder/src/pages/builder/app/[application]/design/_layout.svelte rename to packages/builder/src/pages/builder/app/[application]/design/[screenId]/_layout.svelte index 90bdf616c7..79e4fecf52 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/_layout.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_layout.svelte @@ -1,7 +1,23 @@
@@ -41,8 +57,10 @@
- - + {#if $selectedScreen} + + + {/if}
diff --git a/packages/builder/src/pages/builder/app/[application]/design/components/[screenId]/[componentId]/_components/navigation/ComponentDropdownMenu.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/components/[componentId]/_components/navigation/ComponentDropdownMenu.svelte similarity index 100% rename from packages/builder/src/pages/builder/app/[application]/design/components/[screenId]/[componentId]/_components/navigation/ComponentDropdownMenu.svelte rename to packages/builder/src/pages/builder/app/[application]/design/[screenId]/components/[componentId]/_components/navigation/ComponentDropdownMenu.svelte diff --git a/packages/builder/src/pages/builder/app/[application]/design/components/[screenId]/[componentId]/_components/navigation/ComponentNavigationPanel.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/components/[componentId]/_components/navigation/ComponentNavigationPanel.svelte similarity index 94% rename from packages/builder/src/pages/builder/app/[application]/design/components/[screenId]/[componentId]/_components/navigation/ComponentNavigationPanel.svelte rename to packages/builder/src/pages/builder/app/[application]/design/[screenId]/components/[componentId]/_components/navigation/ComponentNavigationPanel.svelte index bc3c2f7f99..668e204e0d 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/components/[screenId]/[componentId]/_components/navigation/ComponentNavigationPanel.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/components/[componentId]/_components/navigation/ComponentNavigationPanel.svelte @@ -22,7 +22,7 @@ let newOffsets = {} // Calculate left offset - const offsetX = bounds.left + bounds.width + scrollLeft - 40 + const offsetX = bounds.left + bounds.width + scrollLeft - 58 if (offsetX > sidebarWidth) { newOffsets.left = offsetX - sidebarWidth } else { @@ -91,8 +91,7 @@