Migrated layouts to the class structure, number of fixes across the new stores and refactoring

This commit is contained in:
Dean 2023-11-01 17:27:18 +00:00
parent 15c030bcf6
commit d5a67ad23b
15 changed files with 340 additions and 246 deletions

View File

@ -11,6 +11,7 @@ import {
componentStore, componentStore,
screenStore, screenStore,
appStore, appStore,
layoutStore,
} from "stores/frontend" } from "stores/frontend"
import { import {
queries as queriesStores, queries as queriesStores,
@ -1048,7 +1049,7 @@ export const getAllStateVariables = () => {
export const getAllAssets = () => { export const getAllAssets = () => {
// Get all component containing assets // Get all component containing assets
let allAssets = [] let allAssets = []
allAssets = allAssets.concat(get(appStore).layouts || []) allAssets = allAssets.concat(get(layoutStore).layouts || [])
allAssets = allAssets.concat(get(screenStore).screens || []) allAssets = allAssets.concat(get(screenStore).screens || [])
return allAssets return allAssets

View File

@ -2,8 +2,8 @@
import DraggableList from "../DraggableList/DraggableList.svelte" import DraggableList from "../DraggableList/DraggableList.svelte"
import ButtonSetting from "./ButtonSetting.svelte" import ButtonSetting from "./ButtonSetting.svelte"
import { createEventDispatcher } from "svelte" import { createEventDispatcher } from "svelte"
import { store } from "builderStore"
import { Helpers } from "@budibase/bbui" import { Helpers } from "@budibase/bbui"
import { componentStore } from "stores/frontend"
export let componentBindings export let componentBindings
export let bindings export let bindings
@ -47,7 +47,7 @@
} }
const buildPseudoInstance = cfg => { const buildPseudoInstance = cfg => {
return store.actions.components.createInstance( return componentStore.createInstance(
`@budibase/standard-components/button`, `@budibase/standard-components/button`,
{ {
_instanceName: Helpers.uuid(), _instanceName: Helpers.uuid(),

View File

@ -1,7 +1,7 @@
<script> <script>
import EditComponentPopover from "../EditComponentPopover.svelte" import EditComponentPopover from "../EditComponentPopover.svelte"
import { Icon } from "@budibase/bbui" import { Icon } from "@budibase/bbui"
import { runtimeToReadableBinding } from "builderStore/dataBinding" import { runtimeToReadableBinding } from "builder/dataBinding"
import { isJSBinding } from "@budibase/string-templates" import { isJSBinding } from "@budibase/string-templates"
export let item export let item

View File

@ -30,9 +30,7 @@
open = true open = true
} }
$: componentDef = store.actions.components.getDefinition( $: componentDef = componentStore.getDefinition(componentInstance._component)
componentInstance._component
)
$: parsedComponentDef = processComponentDefinitionSettings(componentDef) $: parsedComponentDef = processComponentDefinitionSettings(componentDef)
const processComponentDefinitionSettings = componentDef => { const processComponentDefinitionSettings = componentDef => {

View File

@ -6,7 +6,7 @@
getBindableProperties, getBindableProperties,
getComponentBindableProperties, getComponentBindableProperties,
} from "builder/dataBinding" } from "builder/dataBinding"
import DraggableList from "../DraggableList.svelte" import DraggableList from "../DraggableList/DraggableList.svelte"
import { createEventDispatcher } from "svelte" import { createEventDispatcher } from "svelte"
import { selectedScreen, currentAsset, componentStore } from "stores/frontend" import { selectedScreen, currentAsset, componentStore } from "stores/frontend"
import FieldSetting from "./FieldSetting.svelte" import FieldSetting from "./FieldSetting.svelte"

View File

@ -3,9 +3,9 @@
import { Toggle, Icon } from "@budibase/bbui" import { Toggle, Icon } from "@budibase/bbui"
import { createEventDispatcher } from "svelte" import { createEventDispatcher } from "svelte"
import { cloneDeep } from "lodash/fp" import { cloneDeep } from "lodash/fp"
import { store } from "builderStore" import { runtimeToReadableBinding } from "builder/dataBinding"
import { runtimeToReadableBinding } from "builderStore/dataBinding"
import { isJSBinding } from "@budibase/string-templates" import { isJSBinding } from "@budibase/string-templates"
import { componentStore } from "stores/frontend"
export let item export let item
export let componentBindings export let componentBindings
@ -37,7 +37,7 @@
} }
$: readableText = getReadableText(item) $: readableText = getReadableText(item)
$: componentDef = store.actions.components.getDefinition(item._component) $: componentDef = componentStore.getDefinition(item._component)
</script> </script>
<div class="list-item-body"> <div class="list-item-body">

View File

@ -11,16 +11,7 @@ const registerNode = async (node, tourStepKey) => {
return return
} }
builderStore.update(state => { builderStore.registerTourNode(tourStepKey, node)
const update = {
...state,
tourNodes: {
...state.tourNodes,
[tourStepKey]: node,
},
}
return update
})
} }
export function tourHandler(node, tourStepKey) { export function tourHandler(node, tourStepKey) {
@ -29,19 +20,7 @@ export function tourHandler(node, tourStepKey) {
} }
return { return {
destroy: () => { destroy: () => {
const updatedTourNodes = get(builderStore).tourNodes builderStore.destroyTourNode(tourStepKey)
if (updatedTourNodes && updatedTourNodes[tourStepKey]) {
delete updatedTourNodes[tourStepKey]
builderStore.update(state => {
const update = {
...state,
tourNodes: {
...updatedTourNodes,
},
}
return update
})
}
}, },
} }
} }

View File

@ -91,20 +91,13 @@
// Check if onboarding is enabled. // Check if onboarding is enabled.
if (isEnabled(TENANT_FEATURE_FLAGS.ONBOARDING_TOUR)) { if (isEnabled(TENANT_FEATURE_FLAGS.ONBOARDING_TOUR)) {
if (!$auth.user?.onboardedAt) { if (!$auth.user?.onboardedAt) {
await builderStore.update(state => ({ builderStore.startBuilderOnboarding()
...state,
onboarding: true,
tourKey: TOUR_KEYS.TOUR_BUILDER_ONBOARDING,
}))
} else { } else {
// Feature tour date // Feature tour date
const release_date = new Date("2023-03-01T00:00:00.000Z") const release_date = new Date("2023-03-01T00:00:00.000Z")
const onboarded = new Date($auth.user?.onboardedAt) const onboarded = new Date($auth.user?.onboardedAt)
if (onboarded < release_date) { if (onboarded < release_date) {
await builderStore.update(state => ({ builderStore.startTour(TOUR_KEYS.FEATURE_ONBOARDING)
...state,
tourKey: TOUR_KEYS.FEATURE_ONBOARDING,
}))
} }
} }
} }

View File

@ -20,7 +20,7 @@
const onUpdateName = async value => { const onUpdateName = async value => {
try { try {
await store.actions.components.updateSetting("_instanceName", value) await componentStore.updateSetting("_instanceName", value)
} catch (error) { } catch (error) {
notifications.error("Error updating component name") notifications.error("Error updating component name")
} }

View File

@ -2,6 +2,7 @@ import { writable } from "svelte/store"
import { createBuilderWebsocket } from "./websocket.js" import { createBuilderWebsocket } from "./websocket.js"
import { BuilderSocketEvent } from "@budibase/shared-core" import { BuilderSocketEvent } from "@budibase/shared-core"
import BudiStore from "./BudiStore" import BudiStore from "./BudiStore"
import { TOUR_KEYS } from "components/portal/onboarding/tours.js"
export const INITIAL_BUILDER_STATE = { export const INITIAL_BUILDER_STATE = {
previousTopNavPath: {}, previousTopNavPath: {},
@ -69,11 +70,13 @@ export class BuilderStore extends BudiStore {
} }
setPreviousTopNavPath(route, url) { setPreviousTopNavPath(route, url) {
this.update(state => { this.update(state => ({
if (!state.previousTopNavPath) state.previousTopNavPath = {} ...state,
state.previousTopNavPath[route] = url previousTopNavPath: {
return state ...(state.previousTopNavPath || {}),
}) [route]: url,
},
}))
} }
selectResource(id) { selectResource(id) {
@ -82,9 +85,8 @@ export class BuilderStore extends BudiStore {
}) })
} }
/* registerTourNode(tourStepKey, node) {
register this.update(state => {
update(state => {
const update = { const update = {
...state, ...state,
tourNodes: { tourNodes: {
@ -94,7 +96,34 @@ export class BuilderStore extends BudiStore {
} }
return update 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() export const builderStore = new BuilderStore()

View File

@ -1,4 +1,4 @@
import { writable, get, derived } from "svelte/store" import { get, derived } from "svelte/store"
import { cloneDeep } from "lodash/fp" import { cloneDeep } from "lodash/fp"
import { API } from "api" import { API } from "api"
import { Helpers } from "@budibase/bbui" import { Helpers } from "@budibase/bbui"
@ -23,26 +23,93 @@ import {
DB_TYPE_INTERNAL, DB_TYPE_INTERNAL,
DB_TYPE_EXTERNAL, DB_TYPE_EXTERNAL,
} from "constants/backend" } from "constants/backend"
import BudiStore from "../BudiStore"
const INITIAL_COMPONENTS_STATE = { export const INITIAL_COMPONENTS_STATE = {
components: [], components: [],
customComponents: [], customComponents: [],
selectedComponentId: null, selectedComponentId: null,
componentToPaste: null, componentToPaste: null,
} }
export const createComponentStore = () => { export class ComponentStore extends BudiStore {
const store = writable({ constructor() {
...INITIAL_COMPONENTS_STATE, super(INITIAL_COMPONENTS_STATE)
})
const reset = () => { this.reset = this.reset.bind(this)
store.set({ ...INITIAL_COMPONENTS_STATE }) 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) { if (!appId) {
appId = get(store).appId appId = get(this.store).appId
} }
// Fetch definitions and filter out custom component definitions so we // Fetch definitions and filter out custom component definitions so we
@ -53,7 +120,7 @@ export const createComponentStore = () => {
) )
// Update store // Update store
store.update(state => ({ this.update(state => ({
...state, ...state,
components, components,
customComponents, customComponents,
@ -65,14 +132,25 @@ export const createComponentStore = () => {
return components return components
} }
const getDefinition = componentName => { /**
*
* @param {string} componentName
* @example
* '@budibase/standard-components/container'
* @returns {object}
*/
getDefinition(componentName) {
if (!componentName) { if (!componentName) {
return null return null
} }
return get(store).components[componentName] return get(this.store).components[componentName]
} }
const getDefaultDatasource = () => { /**
*
* @returns {object}
*/
getDefaultDatasource() {
// Ignore users table // Ignore users table
const validTables = get(tables).list.filter(x => x._id !== "ta_users") 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) 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) { if (!component?._component) {
return return
} }
const defaultDS = getDefaultDatasource() const defaultDS = this.getDefaultDatasource()
const settings = getComponentSettings(component._component) const settings = getComponentSettings(component._component)
const { parent, screen, useDefaultValues } = opts || {} const { parent, screen, useDefaultValues } = opts || {}
const treeId = parent?._id || component._id 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) { if (!definition) {
return null return null
} }
@ -218,7 +309,7 @@ export const createComponentStore = () => {
} }
// Enrich empty settings // Enrich empty settings
enrichEmptySettings(instance, { this.enrichEmptySettings(instance, {
parent, parent,
screen: get(selectedScreen), screen: get(selectedScreen),
useDefaultValues: true, 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) { if (!componentInstance) {
return return
} }
@ -286,7 +389,7 @@ export const createComponentStore = () => {
let parentComponent let parentComponent
if (currentComponent) { if (currentComponent) {
// Use selected component as parent if one is selected // Use selected component as parent if one is selected
const definition = getDefinition(currentComponent._component) const definition = this.getDefinition(currentComponent._component)
if (definition?.hasChildren) { if (definition?.hasChildren) {
// Use selected component if it allows children // Use selected component if it allows children
parentComponent = currentComponent parentComponent = currentComponent
@ -314,7 +417,7 @@ export const createComponentStore = () => {
} }
// Select new component // Select new component
store.update(state => { this.update(state => {
state.selectedComponentId = componentInstance._id state.selectedComponentId = componentInstance._id
return state return state
}) })
@ -327,10 +430,17 @@ export const createComponentStore = () => {
return componentInstance 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 // Use selected component by default
if (!componentId || !screenId) { if (!componentId || !screenId) {
const state = get(store) const state = get(this.store)
componentId = componentId || state.selectedComponentId componentId = componentId || state.selectedComponentId
const screenState = get(screenStore) const screenState = get(screenStore)
@ -350,18 +460,23 @@ export const createComponentStore = () => {
await screenStore.patch(patchScreen, screenId) await screenStore.patch(patchScreen, screenId)
} }
const deleteComponent = async component => { /**
*
* @param {object} component
* @returns
*/
async delete(component) {
if (!component) { if (!component) {
return return
} }
// Determine the next component to select after deletion // Determine the next component to select after deletion
const state = get(store) const state = get(this.store)
let nextSelectedComponentId let nextSelectedComponentId
if (state.selectedComponentId === component._id) { if (state.selectedComponentId === component._id) {
nextSelectedComponentId = getNext() nextSelectedComponentId = this.getNext()
if (!nextSelectedComponentId) { if (!nextSelectedComponentId) {
nextSelectedComponentId = getPrevious() nextSelectedComponentId = this.getPrevious()
} }
} }
@ -385,16 +500,16 @@ export const createComponentStore = () => {
// Update selected component if required // Update selected component if required
if (nextSelectedComponentId) { if (nextSelectedComponentId) {
store.update(state => { this.update(state => {
state.selectedComponentId = nextSelectedComponentId state.selectedComponentId = nextSelectedComponentId
return state return state
}) })
} }
} }
const copy = (component, cut = false, selectParent = true) => { copy(component, cut = false, selectParent = true) {
// Update store with copied component // Update store with copied component
store.update(state => { this.update(state => {
state.componentToPaste = cloneDeep(component) state.componentToPaste = cloneDeep(component)
state.componentToPaste.isCut = cut state.componentToPaste.isCut = cut
return state return state
@ -405,7 +520,7 @@ export const createComponentStore = () => {
const screen = get(selectedScreen) const screen = get(selectedScreen)
const parent = findComponentParent(screen?.props, component._id) const parent = findComponentParent(screen?.props, component._id)
if (parent) { if (parent) {
store.update(state => { this.update(state => {
state.selectedComponentId = parent._id state.selectedComponentId = parent._id
return state 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 state.selectedComponentId = componentId
return state 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) { if (!state.componentToPaste) {
return return
} }
@ -430,7 +556,7 @@ export const createComponentStore = () => {
// Remove copied component if cutting, regardless if pasting works // Remove copied component if cutting, regardless if pasting works
let componentToPaste = cloneDeep(state.componentToPaste) let componentToPaste = cloneDeep(state.componentToPaste)
if (componentToPaste.isCut) { if (componentToPaste.isCut) {
store.update(state => { this.update(state => {
delete state.componentToPaste delete state.componentToPaste
return state return state
}) })
@ -465,7 +591,7 @@ export const createComponentStore = () => {
// Check inside is valid // Check inside is valid
if (mode === "inside") { if (mode === "inside") {
const definition = getDefinition(targetComponent._component) const definition = this.getDefinition(targetComponent._component)
if (!definition.hasChildren) { if (!definition.hasChildren) {
mode = "below" mode = "below"
} }
@ -495,15 +621,15 @@ export const createComponentStore = () => {
await screenStore.patch(patch, targetScreenId) await screenStore.patch(patch, targetScreenId)
// Select the new component // Select the new component
store.update(state => { this.update(state => {
state.selectedScreenId = targetScreenId state.selectedScreenId = targetScreenId
state.selectedComponentId = newComponentId state.selectedComponentId = newComponentId
return state return state
}) })
} }
const getPrevious = () => { getPrevious() {
const state = get(store) const state = get(this.store)
const componentId = state.selectedComponentId const componentId = state.selectedComponentId
const screen = get(selectedScreen) const screen = get(selectedScreen)
const parent = findComponentParent(screen.props, componentId) const parent = findComponentParent(screen.props, componentId)
@ -542,8 +668,8 @@ export const createComponentStore = () => {
return parent._id return parent._id
} }
const getNext = () => { getNext() {
const state = get(store) const state = get(this.store)
const component = get(selectedComponent) const component = get(selectedComponent)
const componentId = component?._id const componentId = component?._id
const screen = get(selectedScreen) const screen = get(selectedScreen)
@ -593,27 +719,27 @@ export const createComponentStore = () => {
} }
} }
const selectPrevious = () => { selectPrevious() {
const previousId = getPrevious() const previousId = this.getPrevious()
if (previousId) { if (previousId) {
store.update(state => { this.update(state => {
state.selectedComponentId = previousId state.selectedComponentId = previousId
return state return state
}) })
} }
} }
const selectNext = () => { selectNext() {
const nextId = getNext() const nextId = this.getNext()
if (nextId) { if (nextId) {
store.update(state => { this.update(state => {
state.selectedComponentId = nextId state.selectedComponentId = nextId
return state return state
}) })
} }
} }
const moveUp = async component => { async moveUp(component) {
await screenStore.patch(screen => { await screenStore.patch(screen => {
const componentId = component?._id const componentId = component?._id
const parent = findComponentParent(screen.props, componentId) const parent = findComponentParent(screen.props, componentId)
@ -635,7 +761,7 @@ export const createComponentStore = () => {
// If sibling before us accepts children, move to last child of // If sibling before us accepts children, move to last child of
// sibling // sibling
const previousSibling = parent._children[index - 1] const previousSibling = parent._children[index - 1]
const definition = getDefinition(previousSibling._component) const definition = this.getDefinition(previousSibling._component)
if (definition.hasChildren) { if (definition.hasChildren) {
previousSibling._children.push(originalComponent) previousSibling._children.push(originalComponent)
} }
@ -658,7 +784,7 @@ export const createComponentStore = () => {
}) })
} }
const moveDown = async component => { async moveDown(component) {
await screenStore.patch(screen => { await screenStore.patch(screen => {
const componentId = component?._id const componentId = component?._id
const parent = findComponentParent(screen.props, componentId) const parent = findComponentParent(screen.props, componentId)
@ -687,7 +813,7 @@ export const createComponentStore = () => {
if (index < parent._children.length) { if (index < parent._children.length) {
// If the next sibling has children, become the first child // If the next sibling has children, become the first child
const nextSibling = parent._children[index] const nextSibling = parent._children[index]
const definition = getDefinition(nextSibling._component) const definition = this.getDefinition(nextSibling._component)
if (definition.hasChildren) { if (definition.hasChildren) {
nextSibling._children.splice(0, 0, originalComponent) nextSibling._children.splice(0, 0, originalComponent)
} }
@ -709,8 +835,8 @@ export const createComponentStore = () => {
}) })
} }
const updateStyle = async (name, value) => { async updateStyle(name, value) {
await patch(component => { await this.patch(component => {
if (value == null || value === "") { if (value == null || value === "") {
delete component._styles.normal[name] delete component._styles.normal[name]
} else { } else {
@ -719,33 +845,33 @@ export const createComponentStore = () => {
}) })
} }
const updateStyles = async (styles, id) => { async updateStyles(styles, id) {
const patchFn = component => { const patchFn = component => {
component._styles.normal = { component._styles.normal = {
...component._styles.normal, ...component._styles.normal,
...styles, ...styles,
} }
} }
await patch(patchFn, id) await this.patch(patchFn, id)
} }
const updateCustomStyle = async style => { async updateCustomStyle(style) {
await patch(component => { await this.patch(component => {
component._styles.custom = style component._styles.custom = style
}) })
} }
const updateConditions = async conditions => { async updateConditions(conditions) {
await patch(component => { await this.patch(component => {
component._conditions = conditions component._conditions = conditions
}) })
} }
const updateSetting = async (name, value) => { async updateSetting(name, value) {
await patch(updateComponentSetting(name, value)) await this.patch(this.updateComponentSetting(name, value))
} }
const updateComponentSetting = (name, value) => { updateComponentSetting(name, value) {
return component => { return component => {
if (!name || !component) { if (!name || !component) {
return false return false
@ -783,11 +909,11 @@ export const createComponentStore = () => {
} }
} }
const requestEjectBlock = componentId => { requestEjectBlock(componentId) {
previewStore.sendEvent("eject-block", componentId) previewStore.sendEvent("eject-block", componentId)
} }
const handleEjectBlock = async (componentId, ejectedDefinition) => { async handleEjectBlock(componentId, ejectedDefinition) {
let nextSelectedComponentId let nextSelectedComponentId
await screenStore.patch(screen => { await screenStore.patch(screen => {
@ -827,20 +953,20 @@ export const createComponentStore = () => {
// Select new root component // Select new root component
if (nextSelectedComponentId) { if (nextSelectedComponentId) {
store.update(state => { this.update(state => {
state.selectedComponentId = nextSelectedComponentId state.selectedComponentId = nextSelectedComponentId
return state return state
}) })
} }
} }
const addParent = async (componentId, parentType) => { async addParent(componentId, parentType) {
if (!componentId || !parentType) { if (!componentId || !parentType) {
return return
} }
// Create new parent instance // Create new parent instance
const newParentDefinition = createInstance(parentType, null, parent) const newParentDefinition = this.createInstance(parentType, null, parent)
if (!newParentDefinition) { if (!newParentDefinition) {
return return
} }
@ -868,71 +994,15 @@ export const createComponentStore = () => {
}) })
// Select the new parent // Select the new parent
store.update(state => { this.update(state => {
state.selectedComponentId = newParentDefinition._id state.selectedComponentId = newParentDefinition._id
return state 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( export const selectedComponent = componentStore.selected
[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 selectedComponentPath = derived( export const selectedComponentPath = componentStore.selectedComponentPath
[componentStore, selectedScreen],
([$componentStore, $selectedScreen]) => {
return findComponentPath(
$selectedScreen?.props,
$componentStore.selectedComponentId
).map(component => component._id)
}
)

View File

@ -1,3 +1,4 @@
import { layoutStore } from "./layouts.js"
import { appStore } from "./app.js" import { appStore } from "./app.js"
import { import {
componentStore, componentStore,
@ -14,7 +15,6 @@ import {
} from "./screens.js" } from "./screens.js"
import { builderStore, screensHeight } from "./builder.js" import { builderStore, screensHeight } from "./builder.js"
import { previewStore } from "./preview.js" import { previewStore } from "./preview.js"
import { layoutStore } from "./layouts.js"
import { import {
automationStore, automationStore,
selectedAutomation, selectedAutomation,
@ -25,6 +25,7 @@ import { deploymentStore } from "./deployments.js"
import { database } from "./database.js" import { database } from "./database.js"
export { export {
layoutStore,
database, database,
appStore, appStore,
componentStore, componentStore,
@ -40,7 +41,6 @@ export {
automationHistoryStore, automationHistoryStore,
currentAsset, currentAsset,
sortedScreens, sortedScreens,
layoutStore,
userStore, userStore,
isOnlyUser, isOnlyUser,
screensHeight, screensHeight,

View File

@ -1,32 +1,43 @@
import { writable, derived, get } from "svelte/store" import { derived, get } from "svelte/store"
import { componentStore } from "stores/frontend" import { componentStore } from "stores/frontend"
import BudiStore from "./BudiStore"
import { API } from "api" import { API } from "api"
// Review the purpose of these export const INITIAL_LAYOUT_STATE = {
const INITIAL_LAYOUT_STATE = {
layouts: [], layouts: [],
selectedLayoutId: null, selectedLayoutId: null,
} }
export const createLayoutStore = () => { export class LayoutStore extends BudiStore {
const store = writable({ constructor() {
...INITIAL_LAYOUT_STATE, super(INITIAL_LAYOUT_STATE)
})
const reset = () => { this.reset = this.reset.bind(this)
store.set({ ...INITIAL_LAYOUT_STATE }) 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 => { reset() {
store.update(state => ({ this.store.set({ ...INITIAL_LAYOUT_STATE })
}
syncAppLayouts(pkg) {
this.update(state => ({
...state, ...state,
layouts: [...pkg.layouts], layouts: [...pkg.layouts],
})) }))
} }
const select = layoutId => { select(layoutId) {
// Check this layout exists // Check this layout exists
const state = get(store) const state = get(this.store)
const componentState = get(componentStore) const componentState = get(componentStore)
const layout = state.layouts.find(layout => layout._id === layoutId) const layout = state.layouts.find(layout => layout._id === layoutId)
if (!layout) { if (!layout) {
@ -42,7 +53,7 @@ export const createLayoutStore = () => {
} }
// Select new layout // Select new layout
store.update(state => { this.update(state => {
state.selectedLayoutId = layout._id state.selectedLayoutId = layout._id
return state return state
}) })
@ -50,7 +61,7 @@ export const createLayoutStore = () => {
componentStore.select(layout.props?._id) componentStore.select(layout.props?._id)
} }
const deleteLayout = async layout => { async deleteLayout(layout) {
if (!layout?._id) { if (!layout?._id) {
return return
} }
@ -58,25 +69,13 @@ export const createLayoutStore = () => {
layoutId: layout._id, layoutId: layout._id,
layoutRev: layout._rev, layoutRev: layout._rev,
}) })
store.update(state => { this.update(state => {
state.layouts = state.layouts.filter(x => x._id !== layout._id) state.layouts = state.layouts.filter(x => x._id !== layout._id)
return state 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 => { export const selectedLayout = layoutStore.selectedLayout
return $layoutStore.layouts?.find(
layout => layout._id === $layoutStore.selectedLayoutId
)
})

View File

@ -4,6 +4,7 @@ import { Helpers } from "@budibase/bbui"
import { RoleUtils, Utils } from "@budibase/frontend-core" import { RoleUtils, Utils } from "@budibase/frontend-core"
import { findAllMatchingComponents } from "stores/frontend/components/utils" import { findAllMatchingComponents } from "stores/frontend/components/utils"
import { import {
layoutStore,
appStore, appStore,
componentStore, componentStore,
navigationStore, navigationStore,
@ -36,7 +37,7 @@ export class ScreenStore extends BudiStore {
this.syncScreenData = this.syncScreenData.bind(this) this.syncScreenData = this.syncScreenData.bind(this)
this.updateSetting = this.updateSetting.bind(this) this.updateSetting = this.updateSetting.bind(this)
this.sequentialScreenPatch = this.sequentialScreenPatch.bind(this) this.sequentialScreenPatch = this.sequentialScreenPatch.bind(this)
// this.removeCustomLayout = this.removeCustomLayout(this) this.removeCustomLayout = this.removeCustomLayout(this)
this.selected = derived(this.store, $store => { this.selected = derived(this.store, $store => {
return get(this.store).screens.find( return get(this.store).screens.find(
@ -452,16 +453,16 @@ export class ScreenStore extends BudiStore {
} }
// Move to layouts store // Move to layouts store
// async removeCustomLayout(screen) { async removeCustomLayout(screen) {
// // Pull relevant settings from old layout, if required // Pull relevant settings from old layout, if required
// const layout = get(this.store).layouts.find(x => x._id === screen.layoutId) const layout = get(layoutStore).layouts.find(x => x._id === screen.layoutId)
// const patchFn = screen => { const patchFn = screen => {
// screen.layoutId = null screen.layoutId = null
// screen.showNavigation = layout?.props.navigation !== "None" screen.showNavigation = layout?.props.navigation !== "None"
// screen.width = layout?.props.width || "Large" screen.width = layout?.props.width || "Large"
// } }
// await this.patch(patchFn, screen._id) await this.patch(patchFn, screen._id)
// } }
/** /**
* Parse the entire screen component tree and ensure settings are valid * Parse the entire screen component tree and ensure settings are valid

View File

@ -9,12 +9,13 @@ vi.mock("../websocket.js")
describe("Builder store", () => { describe("Builder store", () => {
beforeEach(ctx => { beforeEach(ctx => {
vi.clearAllMocks() vi.clearAllMocks()
const builderStorex = new BuilderStore() const builderStore = new BuilderStore()
ctx.test = {}
ctx.test = { ctx.test = {
get store() { get store() {
return get(builderStorex) return get(builderStore)
}, },
builderStorex, builderStore,
} }
}) })
@ -147,13 +148,13 @@ describe("Builder store", () => {
const designURL = const designURL =
"/builder/app/app_dev_123/design/screen_456/screen_456-screen" "/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({ expect(ctx.test.store.previousTopNavPath).toStrictEqual({
[dataRoute]: dataURL, [dataRoute]: dataURL,
}) })
ctx.test.builderStorex.setPreviousTopNavPath(designRoute, designURL) ctx.test.builderStore.setPreviousTopNavPath(designRoute, designURL)
expect(ctx.test.store.previousTopNavPath).toStrictEqual({ expect(ctx.test.store.previousTopNavPath).toStrictEqual({
[dataRoute]: dataURL, [dataRoute]: dataURL,
@ -161,24 +162,47 @@ describe("Builder store", () => {
}) })
}) })
it("Overrite an existing route/path mapping with a new URL", () => { it("Overrite an existing route/path mapping with a new URL", ctx => {
// expect(ctx.test.store.previousTopNavPath).toStrictEqual({}) expect(ctx.test.store.previousTopNavPath).toStrictEqual({})
// ctx.test.builderStore.refresh()
console.log(INITIAL_BUILDER_STATE) console.log(INITIAL_BUILDER_STATE)
// const dataRoute = "/builder/app/:application/data" const dataRoute = "/builder/app/:application/data"
// const dataURL = "/builder/app/app_dev_123/data/table/ta_users" const dataURL = "/builder/app/app_dev_123/data/table/ta_users"
// const updatedURL = "/builder/app/app_dev_123/data/table/ta_employees" 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({ expect(ctx.test.store.previousTopNavPath).toStrictEqual({
// [dataRoute]: dataURL, [dataRoute]: dataURL,
// }) })
// ctx.test.builderStore.setPreviousTopNavPath(dataRoute, updatedURL) ctx.test.builderStore.setPreviousTopNavPath(dataRoute, updatedURL)
// expect(ctx.test.store.previousTopNavPath).toStrictEqual({ expect(ctx.test.store.previousTopNavPath).toStrictEqual({
// [dataRoute]: updatedURL, [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({})
}) })
}) })