diff --git a/packages/builder/src/builder/dataBinding.js b/packages/builder/src/builder/dataBinding.js index ca0c51af98..1f33b64b8c 100644 --- a/packages/builder/src/builder/dataBinding.js +++ b/packages/builder/src/builder/dataBinding.js @@ -11,6 +11,7 @@ import { componentStore, screenStore, appStore, + layoutStore, } from "stores/frontend" import { queries as queriesStores, @@ -1048,7 +1049,7 @@ export const getAllStateVariables = () => { export const getAllAssets = () => { // Get all component containing assets let allAssets = [] - allAssets = allAssets.concat(get(appStore).layouts || []) + allAssets = allAssets.concat(get(layoutStore).layouts || []) allAssets = allAssets.concat(get(screenStore).screens || []) return allAssets diff --git a/packages/builder/src/components/design/settings/controls/ButtonConfiguration/ButtonConfiguration.svelte b/packages/builder/src/components/design/settings/controls/ButtonConfiguration/ButtonConfiguration.svelte index 324418511b..f3cddf3e09 100644 --- a/packages/builder/src/components/design/settings/controls/ButtonConfiguration/ButtonConfiguration.svelte +++ b/packages/builder/src/components/design/settings/controls/ButtonConfiguration/ButtonConfiguration.svelte @@ -2,8 +2,8 @@ import DraggableList from "../DraggableList/DraggableList.svelte" import ButtonSetting from "./ButtonSetting.svelte" import { createEventDispatcher } from "svelte" - import { store } from "builderStore" import { Helpers } from "@budibase/bbui" + import { componentStore } from "stores/frontend" export let componentBindings export let bindings @@ -47,7 +47,7 @@ } const buildPseudoInstance = cfg => { - return store.actions.components.createInstance( + return componentStore.createInstance( `@budibase/standard-components/button`, { _instanceName: Helpers.uuid(), diff --git a/packages/builder/src/components/design/settings/controls/ButtonConfiguration/ButtonSetting.svelte b/packages/builder/src/components/design/settings/controls/ButtonConfiguration/ButtonSetting.svelte index a05fd9a39b..c8dd0558f6 100644 --- a/packages/builder/src/components/design/settings/controls/ButtonConfiguration/ButtonSetting.svelte +++ b/packages/builder/src/components/design/settings/controls/ButtonConfiguration/ButtonSetting.svelte @@ -1,7 +1,7 @@
diff --git a/packages/builder/src/components/portal/onboarding/tourHandler.js b/packages/builder/src/components/portal/onboarding/tourHandler.js index 80abf3bc01..cbb967164e 100644 --- a/packages/builder/src/components/portal/onboarding/tourHandler.js +++ b/packages/builder/src/components/portal/onboarding/tourHandler.js @@ -11,16 +11,7 @@ const registerNode = async (node, tourStepKey) => { return } - builderStore.update(state => { - const update = { - ...state, - tourNodes: { - ...state.tourNodes, - [tourStepKey]: node, - }, - } - return update - }) + builderStore.registerTourNode(tourStepKey, node) } export function tourHandler(node, tourStepKey) { @@ -29,19 +20,7 @@ export function tourHandler(node, tourStepKey) { } return { destroy: () => { - const updatedTourNodes = get(builderStore).tourNodes - if (updatedTourNodes && updatedTourNodes[tourStepKey]) { - delete updatedTourNodes[tourStepKey] - builderStore.update(state => { - const update = { - ...state, - tourNodes: { - ...updatedTourNodes, - }, - } - return update - }) - } + builderStore.destroyTourNode(tourStepKey) }, } } diff --git a/packages/builder/src/pages/builder/app/[application]/_layout.svelte b/packages/builder/src/pages/builder/app/[application]/_layout.svelte index 3bd608b5a0..57af165c2b 100644 --- a/packages/builder/src/pages/builder/app/[application]/_layout.svelte +++ b/packages/builder/src/pages/builder/app/[application]/_layout.svelte @@ -91,20 +91,13 @@ // Check if onboarding is enabled. if (isEnabled(TENANT_FEATURE_FLAGS.ONBOARDING_TOUR)) { if (!$auth.user?.onboardedAt) { - await builderStore.update(state => ({ - ...state, - onboarding: true, - tourKey: TOUR_KEYS.TOUR_BUILDER_ONBOARDING, - })) + builderStore.startBuilderOnboarding() } else { // Feature tour date const release_date = new Date("2023-03-01T00:00:00.000Z") const onboarded = new Date($auth.user?.onboardedAt) if (onboarded < release_date) { - await builderStore.update(state => ({ - ...state, - tourKey: TOUR_KEYS.FEATURE_ONBOARDING, - })) + builderStore.startTour(TOUR_KEYS.FEATURE_ONBOARDING) } } } diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Component/ComponentSettingsPanel.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Component/ComponentSettingsPanel.svelte index bbc038dffc..f76d924123 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Component/ComponentSettingsPanel.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Component/ComponentSettingsPanel.svelte @@ -20,7 +20,7 @@ const onUpdateName = async value => { try { - await store.actions.components.updateSetting("_instanceName", value) + await componentStore.updateSetting("_instanceName", value) } catch (error) { notifications.error("Error updating component name") } diff --git a/packages/builder/src/stores/frontend/builder.js b/packages/builder/src/stores/frontend/builder.js index 68a0a714a1..16e3d0370b 100644 --- a/packages/builder/src/stores/frontend/builder.js +++ b/packages/builder/src/stores/frontend/builder.js @@ -2,6 +2,7 @@ import { writable } from "svelte/store" import { createBuilderWebsocket } from "./websocket.js" import { BuilderSocketEvent } from "@budibase/shared-core" import BudiStore from "./BudiStore" +import { TOUR_KEYS } from "components/portal/onboarding/tours.js" export const INITIAL_BUILDER_STATE = { previousTopNavPath: {}, @@ -69,11 +70,13 @@ export class BuilderStore extends BudiStore { } setPreviousTopNavPath(route, url) { - this.update(state => { - if (!state.previousTopNavPath) state.previousTopNavPath = {} - state.previousTopNavPath[route] = url - return state - }) + this.update(state => ({ + ...state, + previousTopNavPath: { + ...(state.previousTopNavPath || {}), + [route]: url, + }, + })) } selectResource(id) { @@ -82,9 +85,8 @@ export class BuilderStore extends BudiStore { }) } - /* - register - update(state => { + registerTourNode(tourStepKey, node) { + this.update(state => { const update = { ...state, tourNodes: { @@ -94,7 +96,34 @@ export class BuilderStore extends BudiStore { } return update }) - */ + } + + destroyTourNode(tourStepKey) { + if (this.tourNodes[tourStepKey]) { + this.update(state => ({ + ...state, + tourNodes: { + [tourStepKey]: _, + ...this.tourNodes, + }, + })) + } + } + + startBuilderOnboarding() { + this.update(state => ({ + ...state, + onboarding: true, + tourKey: TOUR_KEYS.TOUR_BUILDER_ONBOARDING, + })) + } + + startTour(tourKey) { + this.update(state => ({ + ...state, + tourKey: tourKey, + })) + } } export const builderStore = new BuilderStore() diff --git a/packages/builder/src/stores/frontend/components/index.js b/packages/builder/src/stores/frontend/components/index.js index 72a774fd60..d58a1dc6b7 100644 --- a/packages/builder/src/stores/frontend/components/index.js +++ b/packages/builder/src/stores/frontend/components/index.js @@ -1,4 +1,4 @@ -import { writable, get, derived } from "svelte/store" +import { get, derived } from "svelte/store" import { cloneDeep } from "lodash/fp" import { API } from "api" import { Helpers } from "@budibase/bbui" @@ -23,26 +23,93 @@ import { DB_TYPE_INTERNAL, DB_TYPE_EXTERNAL, } from "constants/backend" +import BudiStore from "../BudiStore" -const INITIAL_COMPONENTS_STATE = { +export const INITIAL_COMPONENTS_STATE = { components: [], customComponents: [], selectedComponentId: null, componentToPaste: null, } -export const createComponentStore = () => { - const store = writable({ - ...INITIAL_COMPONENTS_STATE, - }) +export class ComponentStore extends BudiStore { + constructor() { + super(INITIAL_COMPONENTS_STATE) - const reset = () => { - store.set({ ...INITIAL_COMPONENTS_STATE }) + this.reset = this.reset.bind(this) + this.refreshDefinitions = this.refreshDefinitions.bind(this) + this.getDefinition = this.getDefinition.bind(this) + this.getDefaultDatasource = this.getDefaultDatasource.bind(this) + this.enrichEmptySettings = this.enrichEmptySettings.bind(this) + this.createInstance = this.createInstance.bind(this) + this.create = this.create.bind(this) + this.patch = this.patch.bind(this) + this.delete = this.delete.bind(this) + this.copy = this.copy.bind(this) + this.paste = this.paste.bind(this) + this.select = this.select.bind(this) + this.getPrevious = this.getPrevious.bind(this) + this.getNext = this.getNext.bind(this) + this.selectPrevious = this.selectPrevious.bind(this) + this.selectNext = this.selectNext.bind(this) + this.moveUp = this.moveUp.bind(this) + this.moveDown = this.moveDown.bind(this) + this.updateStyle = this.updateStyle.bind(this) + this.updateStyles = this.updateStyles.bind(this) + this.updateCustomStyle = this.updateCustomStyle.bind(this) + this.updateConditions = this.updateConditions.bind(this) + this.requestEjectBlock = this.requestEjectBlock.bind(this) + this.handleEjectBlock = this.handleEjectBlock.bind(this) + this.updateSetting = this.updateSetting.bind(this) + this.updateComponentSetting = this.updateComponentSetting.bind(this) + this.addParent = this.addParent.bind(this) + + this.selected = derived( + [this.store, selectedScreen], + ([$store, $selectedScreen]) => { + if ( + $selectedScreen && + $store.selectedComponentId?.startsWith(`${$selectedScreen._id}-`) + ) { + return $selectedScreen?.props + } + if (!$selectedScreen || !$store.selectedComponentId) { + return null + } + return findComponent($selectedScreen?.props, $store.selectedComponentId) + } + ) + + this.selectedComponentPath = derived( + [this.store, selectedScreen], + ([$store, $selectedScreen]) => { + return findComponentPath( + $selectedScreen?.props, + $store.selectedComponentId + ).map(component => component._id) + } + ) + + this.subscribe(state => { + console.log("debug ", state) + }) } - const refreshDefinitions = async appId => { + /** + * Reset the component store to default values + */ + reset() { + this.store.set({ ...INITIAL_COMPONENTS_STATE }) + } + + /** + * + * @param {string} appId + * @returns + */ + async refreshDefinitions(appId) { if (!appId) { - appId = get(store).appId + appId = get(this.store).appId } // Fetch definitions and filter out custom component definitions so we @@ -53,7 +120,7 @@ export const createComponentStore = () => { ) // Update store - store.update(state => ({ + this.update(state => ({ ...state, components, customComponents, @@ -65,14 +132,25 @@ export const createComponentStore = () => { return components } - const getDefinition = componentName => { + /** + * + * @param {string} componentName + * @example + * '@budibase/standard-components/container' + * @returns {object} + */ + getDefinition(componentName) { if (!componentName) { return null } - return get(store).components[componentName] + return get(this.store).components[componentName] } - const getDefaultDatasource = () => { + /** + * + * @returns {object} + */ + getDefaultDatasource() { // Ignore users table const validTables = get(tables).list.filter(x => x._id !== "ta_users") @@ -102,11 +180,17 @@ export const createComponentStore = () => { return validTables.find(table => table.type === DB_TYPE_EXTERNAL) } - const enrichEmptySettings = (component, opts) => { + /** + * + * @param {object} component + * @param {object} opts + * @returns + */ + enrichEmptySettings(component, opts) { if (!component?._component) { return } - const defaultDS = getDefaultDatasource() + const defaultDS = this.getDefaultDatasource() const settings = getComponentSettings(component._component) const { parent, screen, useDefaultValues } = opts || {} const treeId = parent?._id || component._id @@ -198,8 +282,15 @@ export const createComponentStore = () => { }) } - const createInstance = (componentName, presetProps, parent) => { - const definition = getDefinition(componentName) + /** + * + * @param {string} componentName + * @param {object} presetProps + * @param {object} parent + * @returns + */ + createInstance(componentName, presetProps, parent) { + const definition = this.getDefinition(componentName) if (!definition) { return null } @@ -218,7 +309,7 @@ export const createComponentStore = () => { } // Enrich empty settings - enrichEmptySettings(instance, { + this.enrichEmptySettings(instance, { parent, screen: get(selectedScreen), useDefaultValues: true, @@ -247,9 +338,21 @@ export const createComponentStore = () => { } } - const create = async (componentName, presetProps, parent, index) => { - const state = get(store) - const componentInstance = createInstance(componentName, presetProps, parent) + /** + * + * @param {string} componentName + * @param {object} presetProps + * @param {object} parent + * @param {number} index + * @returns + */ + async create(componentName, presetProps, parent, index) { + const state = get(this.store) + const componentInstance = this.createInstance( + componentName, + presetProps, + parent + ) if (!componentInstance) { return } @@ -286,7 +389,7 @@ export const createComponentStore = () => { let parentComponent if (currentComponent) { // Use selected component as parent if one is selected - const definition = getDefinition(currentComponent._component) + const definition = this.getDefinition(currentComponent._component) if (definition?.hasChildren) { // Use selected component if it allows children parentComponent = currentComponent @@ -314,7 +417,7 @@ export const createComponentStore = () => { } // Select new component - store.update(state => { + this.update(state => { state.selectedComponentId = componentInstance._id return state }) @@ -327,10 +430,17 @@ export const createComponentStore = () => { return componentInstance } - const patch = async (patchFn, componentId, screenId) => { + /** + * + * @param {function} patchFn + * @param {string} componentId + * @param {string} screenId + * @returns + */ + async patch(patchFn, componentId, screenId) { // Use selected component by default if (!componentId || !screenId) { - const state = get(store) + const state = get(this.store) componentId = componentId || state.selectedComponentId const screenState = get(screenStore) @@ -350,18 +460,23 @@ export const createComponentStore = () => { await screenStore.patch(patchScreen, screenId) } - const deleteComponent = async component => { + /** + * + * @param {object} component + * @returns + */ + async delete(component) { if (!component) { return } // Determine the next component to select after deletion - const state = get(store) + const state = get(this.store) let nextSelectedComponentId if (state.selectedComponentId === component._id) { - nextSelectedComponentId = getNext() + nextSelectedComponentId = this.getNext() if (!nextSelectedComponentId) { - nextSelectedComponentId = getPrevious() + nextSelectedComponentId = this.getPrevious() } } @@ -385,16 +500,16 @@ export const createComponentStore = () => { // Update selected component if required if (nextSelectedComponentId) { - store.update(state => { + this.update(state => { state.selectedComponentId = nextSelectedComponentId return state }) } } - const copy = (component, cut = false, selectParent = true) => { + copy(component, cut = false, selectParent = true) { // Update store with copied component - store.update(state => { + this.update(state => { state.componentToPaste = cloneDeep(component) state.componentToPaste.isCut = cut return state @@ -405,7 +520,7 @@ export const createComponentStore = () => { const screen = get(selectedScreen) const parent = findComponentParent(screen?.props, component._id) if (parent) { - store.update(state => { + this.update(state => { state.selectedComponentId = parent._id return state }) @@ -413,15 +528,26 @@ export const createComponentStore = () => { } } - const select = componentId => { - store.update(state => { + /** + * + * @param {string} componentId + */ + select(componentId) { + this.update(state => { state.selectedComponentId = componentId return state }) } - const paste = async (targetComponent, mode, targetScreen) => { - const state = get(store) + /** + * + * @param {object} targetComponent + * @param {string} mode + * @param {object} targetScreen + * @returns + */ + async paste(targetComponent, mode, targetScreen) { + const state = get(this.store) if (!state.componentToPaste) { return } @@ -430,7 +556,7 @@ export const createComponentStore = () => { // Remove copied component if cutting, regardless if pasting works let componentToPaste = cloneDeep(state.componentToPaste) if (componentToPaste.isCut) { - store.update(state => { + this.update(state => { delete state.componentToPaste return state }) @@ -465,7 +591,7 @@ export const createComponentStore = () => { // Check inside is valid if (mode === "inside") { - const definition = getDefinition(targetComponent._component) + const definition = this.getDefinition(targetComponent._component) if (!definition.hasChildren) { mode = "below" } @@ -495,15 +621,15 @@ export const createComponentStore = () => { await screenStore.patch(patch, targetScreenId) // Select the new component - store.update(state => { + this.update(state => { state.selectedScreenId = targetScreenId state.selectedComponentId = newComponentId return state }) } - const getPrevious = () => { - const state = get(store) + getPrevious() { + const state = get(this.store) const componentId = state.selectedComponentId const screen = get(selectedScreen) const parent = findComponentParent(screen.props, componentId) @@ -542,8 +668,8 @@ export const createComponentStore = () => { return parent._id } - const getNext = () => { - const state = get(store) + getNext() { + const state = get(this.store) const component = get(selectedComponent) const componentId = component?._id const screen = get(selectedScreen) @@ -593,27 +719,27 @@ export const createComponentStore = () => { } } - const selectPrevious = () => { - const previousId = getPrevious() + selectPrevious() { + const previousId = this.getPrevious() if (previousId) { - store.update(state => { + this.update(state => { state.selectedComponentId = previousId return state }) } } - const selectNext = () => { - const nextId = getNext() + selectNext() { + const nextId = this.getNext() if (nextId) { - store.update(state => { + this.update(state => { state.selectedComponentId = nextId return state }) } } - const moveUp = async component => { + async moveUp(component) { await screenStore.patch(screen => { const componentId = component?._id const parent = findComponentParent(screen.props, componentId) @@ -635,7 +761,7 @@ export const createComponentStore = () => { // If sibling before us accepts children, move to last child of // sibling const previousSibling = parent._children[index - 1] - const definition = getDefinition(previousSibling._component) + const definition = this.getDefinition(previousSibling._component) if (definition.hasChildren) { previousSibling._children.push(originalComponent) } @@ -658,7 +784,7 @@ export const createComponentStore = () => { }) } - const moveDown = async component => { + async moveDown(component) { await screenStore.patch(screen => { const componentId = component?._id const parent = findComponentParent(screen.props, componentId) @@ -687,7 +813,7 @@ export const createComponentStore = () => { if (index < parent._children.length) { // If the next sibling has children, become the first child const nextSibling = parent._children[index] - const definition = getDefinition(nextSibling._component) + const definition = this.getDefinition(nextSibling._component) if (definition.hasChildren) { nextSibling._children.splice(0, 0, originalComponent) } @@ -709,8 +835,8 @@ export const createComponentStore = () => { }) } - const updateStyle = async (name, value) => { - await patch(component => { + async updateStyle(name, value) { + await this.patch(component => { if (value == null || value === "") { delete component._styles.normal[name] } else { @@ -719,33 +845,33 @@ export const createComponentStore = () => { }) } - const updateStyles = async (styles, id) => { + async updateStyles(styles, id) { const patchFn = component => { component._styles.normal = { ...component._styles.normal, ...styles, } } - await patch(patchFn, id) + await this.patch(patchFn, id) } - const updateCustomStyle = async style => { - await patch(component => { + async updateCustomStyle(style) { + await this.patch(component => { component._styles.custom = style }) } - const updateConditions = async conditions => { - await patch(component => { + async updateConditions(conditions) { + await this.patch(component => { component._conditions = conditions }) } - const updateSetting = async (name, value) => { - await patch(updateComponentSetting(name, value)) + async updateSetting(name, value) { + await this.patch(this.updateComponentSetting(name, value)) } - const updateComponentSetting = (name, value) => { + updateComponentSetting(name, value) { return component => { if (!name || !component) { return false @@ -783,11 +909,11 @@ export const createComponentStore = () => { } } - const requestEjectBlock = componentId => { + requestEjectBlock(componentId) { previewStore.sendEvent("eject-block", componentId) } - const handleEjectBlock = async (componentId, ejectedDefinition) => { + async handleEjectBlock(componentId, ejectedDefinition) { let nextSelectedComponentId await screenStore.patch(screen => { @@ -827,20 +953,20 @@ export const createComponentStore = () => { // Select new root component if (nextSelectedComponentId) { - store.update(state => { + this.update(state => { state.selectedComponentId = nextSelectedComponentId return state }) } } - const addParent = async (componentId, parentType) => { + async addParent(componentId, parentType) { if (!componentId || !parentType) { return } // Create new parent instance - const newParentDefinition = createInstance(parentType, null, parent) + const newParentDefinition = this.createInstance(parentType, null, parent) if (!newParentDefinition) { return } @@ -868,71 +994,15 @@ export const createComponentStore = () => { }) // Select the new parent - store.update(state => { + this.update(state => { state.selectedComponentId = newParentDefinition._id return state }) } - return { - subscribe: store.subscribe, - reset, - update: store.update, - refreshDefinitions, - getDefinition, - getDefaultDatasource, - enrichEmptySettings, - createInstance, - create, - patch, - delete: deleteComponent, - copy, - paste, - select, - getPrevious, - getNext, - selectPrevious, - selectNext, - moveUp, - moveDown, - updateStyle, - updateStyles, - updateCustomStyle, - updateConditions, - requestEjectBlock, - handleEjectBlock, - updateSetting, - updateComponentSetting, - addParent, - } } -export const componentStore = createComponentStore() +export const componentStore = new ComponentStore() -export const selectedComponent = derived( - [componentStore, selectedScreen], - ([$componentStore, $selectedScreen]) => { - if ( - $selectedScreen && - $componentStore.selectedComponentId?.startsWith(`${$selectedScreen._id}-`) - ) { - return $selectedScreen?.props - } - if (!$selectedScreen || !$componentStore.selectedComponentId) { - return null - } - return findComponent( - $selectedScreen?.props, - $componentStore.selectedComponentId - ) - } -) +export const selectedComponent = componentStore.selected -export const selectedComponentPath = derived( - [componentStore, selectedScreen], - ([$componentStore, $selectedScreen]) => { - return findComponentPath( - $selectedScreen?.props, - $componentStore.selectedComponentId - ).map(component => component._id) - } -) +export const selectedComponentPath = componentStore.selectedComponentPath diff --git a/packages/builder/src/stores/frontend/index.js b/packages/builder/src/stores/frontend/index.js index a0d02270b8..e161ddb6e5 100644 --- a/packages/builder/src/stores/frontend/index.js +++ b/packages/builder/src/stores/frontend/index.js @@ -1,3 +1,4 @@ +import { layoutStore } from "./layouts.js" import { appStore } from "./app.js" import { componentStore, @@ -14,7 +15,6 @@ import { } from "./screens.js" import { builderStore, screensHeight } from "./builder.js" import { previewStore } from "./preview.js" -import { layoutStore } from "./layouts.js" import { automationStore, selectedAutomation, @@ -25,6 +25,7 @@ import { deploymentStore } from "./deployments.js" import { database } from "./database.js" export { + layoutStore, database, appStore, componentStore, @@ -40,7 +41,6 @@ export { automationHistoryStore, currentAsset, sortedScreens, - layoutStore, userStore, isOnlyUser, screensHeight, diff --git a/packages/builder/src/stores/frontend/layouts.js b/packages/builder/src/stores/frontend/layouts.js index fc2e11d1a4..51fbb99588 100644 --- a/packages/builder/src/stores/frontend/layouts.js +++ b/packages/builder/src/stores/frontend/layouts.js @@ -1,32 +1,43 @@ -import { writable, derived, get } from "svelte/store" +import { derived, get } from "svelte/store" import { componentStore } from "stores/frontend" +import BudiStore from "./BudiStore" import { API } from "api" -// Review the purpose of these -const INITIAL_LAYOUT_STATE = { +export const INITIAL_LAYOUT_STATE = { layouts: [], selectedLayoutId: null, } -export const createLayoutStore = () => { - const store = writable({ - ...INITIAL_LAYOUT_STATE, - }) +export class LayoutStore extends BudiStore { + constructor() { + super(INITIAL_LAYOUT_STATE) - const reset = () => { - store.set({ ...INITIAL_LAYOUT_STATE }) + this.reset = this.reset.bind(this) + this.syncAppLayouts = this.syncAppLayouts.bind(this) + this.select = this.select.bind(this) + this.deleteLayout = this.deleteLayout.bind(this) + + this.selectedLayout = derived(this.store, $store => { + return $store.layouts?.find( + layout => layout._id === $store.selectedLayoutId + ) + }) } - const syncAppLayouts = pkg => { - store.update(state => ({ + reset() { + this.store.set({ ...INITIAL_LAYOUT_STATE }) + } + + syncAppLayouts(pkg) { + this.update(state => ({ ...state, layouts: [...pkg.layouts], })) } - const select = layoutId => { + select(layoutId) { // Check this layout exists - const state = get(store) + const state = get(this.store) const componentState = get(componentStore) const layout = state.layouts.find(layout => layout._id === layoutId) if (!layout) { @@ -42,7 +53,7 @@ export const createLayoutStore = () => { } // Select new layout - store.update(state => { + this.update(state => { state.selectedLayoutId = layout._id return state }) @@ -50,7 +61,7 @@ export const createLayoutStore = () => { componentStore.select(layout.props?._id) } - const deleteLayout = async layout => { + async deleteLayout(layout) { if (!layout?._id) { return } @@ -58,25 +69,13 @@ export const createLayoutStore = () => { layoutId: layout._id, layoutRev: layout._rev, }) - store.update(state => { + this.update(state => { state.layouts = state.layouts.filter(x => x._id !== layout._id) return state }) } - - return { - subscribe: store.subscribe, - syncAppLayouts, - select, - delete: deleteLayout, - reset, - } } -export const layoutStore = createLayoutStore() +export const layoutStore = new LayoutStore() -export const selectedLayout = derived(layoutStore, $layoutStore => { - return $layoutStore.layouts?.find( - layout => layout._id === $layoutStore.selectedLayoutId - ) -}) +export const selectedLayout = layoutStore.selectedLayout diff --git a/packages/builder/src/stores/frontend/screens.js b/packages/builder/src/stores/frontend/screens.js index 2acff8b521..ae29238785 100644 --- a/packages/builder/src/stores/frontend/screens.js +++ b/packages/builder/src/stores/frontend/screens.js @@ -4,6 +4,7 @@ import { Helpers } from "@budibase/bbui" import { RoleUtils, Utils } from "@budibase/frontend-core" import { findAllMatchingComponents } from "stores/frontend/components/utils" import { + layoutStore, appStore, componentStore, navigationStore, @@ -36,7 +37,7 @@ export class ScreenStore extends BudiStore { this.syncScreenData = this.syncScreenData.bind(this) this.updateSetting = this.updateSetting.bind(this) this.sequentialScreenPatch = this.sequentialScreenPatch.bind(this) - // this.removeCustomLayout = this.removeCustomLayout(this) + this.removeCustomLayout = this.removeCustomLayout(this) this.selected = derived(this.store, $store => { return get(this.store).screens.find( @@ -452,16 +453,16 @@ export class ScreenStore extends BudiStore { } // Move to layouts store - // async removeCustomLayout(screen) { - // // Pull relevant settings from old layout, if required - // const layout = get(this.store).layouts.find(x => x._id === screen.layoutId) - // const patchFn = screen => { - // screen.layoutId = null - // screen.showNavigation = layout?.props.navigation !== "None" - // screen.width = layout?.props.width || "Large" - // } - // await this.patch(patchFn, screen._id) - // } + async removeCustomLayout(screen) { + // Pull relevant settings from old layout, if required + const layout = get(layoutStore).layouts.find(x => x._id === screen.layoutId) + const patchFn = screen => { + screen.layoutId = null + screen.showNavigation = layout?.props.navigation !== "None" + screen.width = layout?.props.width || "Large" + } + await this.patch(patchFn, screen._id) + } /** * Parse the entire screen component tree and ensure settings are valid diff --git a/packages/builder/src/stores/frontend/tests/builder.test.js b/packages/builder/src/stores/frontend/tests/builder.test.js index 68657ca2fe..6e0bc608e3 100644 --- a/packages/builder/src/stores/frontend/tests/builder.test.js +++ b/packages/builder/src/stores/frontend/tests/builder.test.js @@ -9,12 +9,13 @@ vi.mock("../websocket.js") describe("Builder store", () => { beforeEach(ctx => { vi.clearAllMocks() - const builderStorex = new BuilderStore() + const builderStore = new BuilderStore() + ctx.test = {} ctx.test = { get store() { - return get(builderStorex) + return get(builderStore) }, - builderStorex, + builderStore, } }) @@ -147,13 +148,13 @@ describe("Builder store", () => { const designURL = "/builder/app/app_dev_123/design/screen_456/screen_456-screen" - ctx.test.builderStorex.setPreviousTopNavPath(dataRoute, dataURL) + ctx.test.builderStore.setPreviousTopNavPath(dataRoute, dataURL) expect(ctx.test.store.previousTopNavPath).toStrictEqual({ [dataRoute]: dataURL, }) - ctx.test.builderStorex.setPreviousTopNavPath(designRoute, designURL) + ctx.test.builderStore.setPreviousTopNavPath(designRoute, designURL) expect(ctx.test.store.previousTopNavPath).toStrictEqual({ [dataRoute]: dataURL, @@ -161,24 +162,47 @@ describe("Builder store", () => { }) }) - it("Overrite an existing route/path mapping with a new URL", () => { - // expect(ctx.test.store.previousTopNavPath).toStrictEqual({}) - // ctx.test.builderStore.refresh() + it("Overrite an existing route/path mapping with a new URL", ctx => { + expect(ctx.test.store.previousTopNavPath).toStrictEqual({}) console.log(INITIAL_BUILDER_STATE) - // const dataRoute = "/builder/app/:application/data" - // const dataURL = "/builder/app/app_dev_123/data/table/ta_users" - // const updatedURL = "/builder/app/app_dev_123/data/table/ta_employees" + const dataRoute = "/builder/app/:application/data" + const dataURL = "/builder/app/app_dev_123/data/table/ta_users" + const updatedURL = "/builder/app/app_dev_123/data/table/ta_employees" - // ctx.test.builderStore.setPreviousTopNavPath(dataRoute, dataURL) + ctx.test.builderStore.setPreviousTopNavPath(dataRoute, dataURL) - // expect(ctx.test.store.previousTopNavPath).toStrictEqual({ - // [dataRoute]: dataURL, - // }) + expect(ctx.test.store.previousTopNavPath).toStrictEqual({ + [dataRoute]: dataURL, + }) - // ctx.test.builderStore.setPreviousTopNavPath(dataRoute, updatedURL) + ctx.test.builderStore.setPreviousTopNavPath(dataRoute, updatedURL) - // expect(ctx.test.store.previousTopNavPath).toStrictEqual({ - // [dataRoute]: updatedURL, - // }) + expect(ctx.test.store.previousTopNavPath).toStrictEqual({ + [dataRoute]: updatedURL, + }) + }) + + it("Register a builder tour node", ctx => { + const fakeNode = { name: "node" } + ctx.test.builderStore.registerTourNode("sampleKey", fakeNode) + + const registeredNodes = ctx.test.store.tourNodes + + expect(registeredNodes).not.toBeNull() + expect(Object.keys(registeredNodes).length).toBe(1) + expect(registeredNodes["sampleKey"]).toStrictEqual(fakeNode) + }) + + it("Clear a destroyed tour node", ctx => { + const fakeNode = { name: "node" } + ctx.test.builderStore.registerTourNode("sampleKey", fakeNode) + + const registeredNodes = ctx.test.store.tourNodes + + expect(registeredNodes).not.toBeNull() + expect(Object.keys(registeredNodes).length).toBe(1) + + ctx.test.builderStore.destroyTourNode("sampleKey") + expect(registeredNodes).toBe({}) }) })