diff --git a/charts/budibase/README.md b/charts/budibase/README.md
index 342011bdb1..dea7d1dbae 100644
--- a/charts/budibase/README.md
+++ b/charts/budibase/README.md
@@ -140,7 +140,7 @@ $ helm install --create-namespace --namespace budibase budibase . -f values.yaml
| ingress.className | string | `""` | What ingress class to use. |
| ingress.enabled | bool | `true` | Whether to create an Ingress resource pointing to the Budibase proxy. |
| ingress.hosts | list | `[]` | Standard hosts block for the Ingress resource. Defaults to pointing to the Budibase proxy. |
-| nameOverride | string | `""` | Override the name of the deploymen. Defaults to {{ .Chart.Name }}. |
+| nameOverride | string | `""` | Override the name of the deployment. Defaults to {{ .Chart.Name }}. |
| service.port | int | `10000` | Port to expose on the service. |
| service.type | string | `"ClusterIP"` | Service type for the service that points to the main Budibase proxy pod. |
| serviceAccount.annotations | object | `{}` | Annotations to add to the service account |
diff --git a/charts/budibase/values.yaml b/charts/budibase/values.yaml
index 09262df463..19b6c22d6c 100644
--- a/charts/budibase/values.yaml
+++ b/charts/budibase/values.yaml
@@ -1,6 +1,6 @@
# -- Passed to all pods created by this chart. Should not ordinarily need to be changed.
imagePullSecrets: []
-# -- Override the name of the deploymen. Defaults to {{ .Chart.Name }}.
+# -- Override the name of the deployment. Defaults to {{ .Chart.Name }}.
nameOverride: ""
serviceAccount:
diff --git a/packages/builder/src/helpers/components.js b/packages/builder/src/helpers/components.js
index 4f4f3ed380..a03ebfdfa7 100644
--- a/packages/builder/src/helpers/components.js
+++ b/packages/builder/src/helpers/components.js
@@ -279,3 +279,11 @@ export const buildContextTreeLookupMap = rootComponent => {
})
return map
}
+
+// Get a flat list of ids for all descendants of a component
+export const getChildIdsForComponent = component => {
+ return [
+ component._id,
+ ...(component?._children ?? []).map(getChildIdsForComponent).flat(1),
+ ]
+}
diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/AppPreview.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/AppPreview.svelte
index 95e7a66be9..e74a05ff99 100644
--- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/AppPreview.svelte
+++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/AppPreview.svelte
@@ -10,6 +10,7 @@
navigationStore,
selectedScreen,
hoverStore,
+ componentTreeNodesStore,
snippets,
} from "stores/builder"
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
@@ -132,6 +133,7 @@
error = event.error || "An unknown error occurred"
} else if (type === "select-component" && data.id) {
componentStore.select(data.id)
+ componentTreeNodesStore.makeNodeVisible(data.id)
} else if (type === "hover-component") {
hoverStore.hover(data.id, false)
} else if (type === "update-prop") {
diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ComponentList/ComponentKeyHandler.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ComponentList/ComponentKeyHandler.svelte
index f6bbac39a5..7e9c113a77 100644
--- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ComponentList/ComponentKeyHandler.svelte
+++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ComponentList/ComponentKeyHandler.svelte
@@ -4,12 +4,12 @@
selectedScreen,
componentStore,
selectedComponent,
+ componentTreeNodesStore,
} from "stores/builder"
- import { findComponent } from "helpers/components"
+ import { findComponent, getChildIdsForComponent } from "helpers/components"
import { goto, isActive } from "@roxi/routify"
import { notifications } from "@budibase/bbui"
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
- import componentTreeNodesStore from "stores/portal/componentTreeNodesStore"
let confirmDeleteDialog
let confirmEjectDialog
@@ -63,38 +63,25 @@
componentStore.selectNext()
},
["ArrowRight"]: component => {
- componentTreeNodesStore.expandNode(component._id)
+ componentTreeNodesStore.expandNodes([component._id])
},
["ArrowLeft"]: component => {
- componentTreeNodesStore.collapseNode(component._id)
+ // Select the collapsing root component to ensure the currently selected component is not
+ // hidden in a collapsed node
+ componentStore.select(component._id)
+ componentTreeNodesStore.collapseNodes([component._id])
},
["Ctrl+ArrowRight"]: component => {
- componentTreeNodesStore.expandNode(component._id)
-
- const expandChildren = component => {
- const children = component._children ?? []
-
- children.forEach(child => {
- componentTreeNodesStore.expandNode(child._id)
- expandChildren(child)
- })
- }
-
- expandChildren(component)
+ const childIds = getChildIdsForComponent(component)
+ componentTreeNodesStore.expandNodes(childIds)
},
["Ctrl+ArrowLeft"]: component => {
- componentTreeNodesStore.collapseNode(component._id)
+ // Select the collapsing root component to ensure the currently selected component is not
+ // hidden in a collapsed node
+ componentStore.select(component._id)
- const collapseChildren = component => {
- const children = component._children ?? []
-
- children.forEach(child => {
- componentTreeNodesStore.collapseNode(child._id)
- collapseChildren(child)
- })
- }
-
- collapseChildren(component)
+ const childIds = getChildIdsForComponent(component)
+ componentTreeNodesStore.collapseNodes(childIds)
},
["Escape"]: () => {
if ($isActive(`./:componentId/new`)) {
diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ComponentList/ComponentTree.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ComponentList/ComponentTree.svelte
index f24235ad07..0219dc304d 100644
--- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ComponentList/ComponentTree.svelte
+++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ComponentList/ComponentTree.svelte
@@ -7,8 +7,8 @@
componentStore,
userSelectedResourceMap,
selectedComponent,
- selectedComponentPath,
hoverStore,
+ componentTreeNodesStore,
} from "stores/builder"
import {
findComponentPath,
@@ -17,7 +17,6 @@
} from "helpers/components"
import { get } from "svelte/store"
import { dndStore } from "./dndStore"
- import componentTreeNodesStore from "stores/portal/componentTreeNodesStore"
export let components = []
export let level = 0
@@ -64,14 +63,11 @@
}
}
- const isOpen = (component, selectedComponentPath, openNodes) => {
+ const isOpen = component => {
if (!component?._children?.length) {
return false
}
- if (selectedComponentPath.slice(0, -1).includes(component._id)) {
- return true
- }
- return openNodes[`nodeOpen-${component._id}`]
+ return componentTreeNodesStore.isNodeExpanded(component._id)
}
const isChildOfSelectedComponent = component => {
@@ -83,6 +79,11 @@
return findComponentPath($selectedComponent, component._id)?.length > 0
}
+ const handleIconClick = componentId => {
+ componentStore.select(componentId)
+ componentTreeNodesStore.toggleNode(componentId)
+ }
+
const hover = hoverStore.hover
@@ -90,7 +91,7 @@
{#each filteredComponents || [] as component, index (component._id)}
- {@const opened = isOpen(component, $selectedComponentPath, openNodes)}
+ {@const opened = isOpen(component, openNodes)}
- {
componentStore.select(component._id)
@@ -104,7 +105,7 @@
on:dragend={dndStore.actions.reset}
on:dragstart={() => dndStore.actions.dragstart(component)}
on:dragover={dragover(component, index)}
- on:iconClick={() => componentTreeNodesStore.toggleNode(component._id)}
+ on:iconClick={() => handleIconClick(component._id)}
on:drop={onDrop}
hovering={$hoverStore.componentId === component._id}
on:mouseenter={() => hover(component._id)}
diff --git a/packages/builder/src/stores/builder/componentTreeNodes.js b/packages/builder/src/stores/builder/componentTreeNodes.js
new file mode 100644
index 0000000000..a8e09a2051
--- /dev/null
+++ b/packages/builder/src/stores/builder/componentTreeNodes.js
@@ -0,0 +1,67 @@
+import { get } from "svelte/store"
+import { createSessionStorageStore } from "@budibase/frontend-core"
+import { selectedScreen as selectedScreenStore } from "./screens"
+import { findComponentPath } from "helpers/components"
+
+const baseStore = createSessionStorageStore("openNodes", {})
+
+const toggleNode = componentId => {
+ baseStore.update(openNodes => {
+ openNodes[`nodeOpen-${componentId}`] = !openNodes[`nodeOpen-${componentId}`]
+
+ return openNodes
+ })
+}
+
+const expandNodes = componentIds => {
+ baseStore.update(openNodes => {
+ const newNodes = Object.fromEntries(
+ componentIds.map(id => [`nodeOpen-${id}`, true])
+ )
+
+ return { ...openNodes, ...newNodes }
+ })
+}
+
+const collapseNodes = componentIds => {
+ baseStore.update(openNodes => {
+ const newNodes = Object.fromEntries(
+ componentIds.map(id => [`nodeOpen-${id}`, false])
+ )
+
+ return { ...openNodes, ...newNodes }
+ })
+}
+
+// Will ensure all parents of a node are expanded so that it is visible in the tree
+const makeNodeVisible = componentId => {
+ const selectedScreen = get(selectedScreenStore)
+
+ const path = findComponentPath(selectedScreen.props, componentId)
+
+ const componentIds = path.map(component => component._id)
+
+ baseStore.update(openNodes => {
+ const newNodes = Object.fromEntries(
+ componentIds.map(id => [`nodeOpen-${id}`, true])
+ )
+
+ return { ...openNodes, ...newNodes }
+ })
+}
+
+const isNodeExpanded = componentId => {
+ const openNodes = get(baseStore)
+ return !!openNodes[`nodeOpen-${componentId}`]
+}
+
+const store = {
+ subscribe: baseStore.subscribe,
+ toggleNode,
+ expandNodes,
+ makeNodeVisible,
+ collapseNodes,
+ isNodeExpanded,
+}
+
+export default store
diff --git a/packages/builder/src/stores/builder/components.js b/packages/builder/src/stores/builder/components.js
index bf8a1ef896..0d7da1ba58 100644
--- a/packages/builder/src/stores/builder/components.js
+++ b/packages/builder/src/stores/builder/components.js
@@ -19,6 +19,7 @@ import {
appStore,
previewStore,
tables,
+ componentTreeNodesStore,
} from "stores/builder/index"
import { buildFormSchema, getSchemaForDatasource } from "dataBinding"
import {
@@ -29,7 +30,6 @@ import {
} from "constants/backend"
import BudiStore from "../BudiStore"
import { Utils } from "@budibase/frontend-core"
-import componentTreeNodesStore from "stores/portal/componentTreeNodesStore"
export const INITIAL_COMPONENTS_STATE = {
components: {},
@@ -653,8 +653,11 @@ export class ComponentStore extends BudiStore {
this.update(state => {
state.selectedScreenId = targetScreenId
state.selectedComponentId = newComponentId
+
return state
})
+
+ componentTreeNodesStore.makeNodeVisible(newComponentId)
}
getPrevious() {
@@ -663,7 +666,6 @@ export class ComponentStore extends BudiStore {
const screen = get(selectedScreen)
const parent = findComponentParent(screen.props, componentId)
const index = parent?._children.findIndex(x => x._id === componentId)
- const componentTreeNodes = get(componentTreeNodesStore)
// Check for screen and navigation component edge cases
const screenComponentId = `${screen._id}-screen`
@@ -680,16 +682,16 @@ export class ComponentStore extends BudiStore {
// If we have siblings above us, choose the sibling or a descendant
if (index > 0) {
- // If sibling before us accepts children, select a descendant
+ // If sibling before us accepts children, and is not collapsed, select a descendant
const previousSibling = parent._children[index - 1]
if (
previousSibling._children?.length &&
- componentTreeNodes[`nodeOpen-${previousSibling._id}`]
+ componentTreeNodesStore.isNodeExpanded(previousSibling._id)
) {
let target = previousSibling
while (
target._children?.length &&
- componentTreeNodes[`nodeOpen-${target._id}`]
+ componentTreeNodesStore.isNodeExpanded(target._id)
) {
target = target._children[target._children.length - 1]
}
@@ -711,7 +713,6 @@ export class ComponentStore extends BudiStore {
const screen = get(selectedScreen)
const parent = findComponentParent(screen.props, componentId)
const index = parent?._children.findIndex(x => x._id === componentId)
- const componentTreeNodes = get(componentTreeNodesStore)
// Check for screen and navigation component edge cases
const screenComponentId = `${screen._id}-screen`
@@ -720,11 +721,11 @@ export class ComponentStore extends BudiStore {
return navComponentId
}
- // If we have children, select first child
+ // If we have children, select first child, and the node is not collapsed
if (
component._children?.length &&
(state.selectedComponentId === navComponentId ||
- componentTreeNodes[`nodeOpen-${component._id}`])
+ componentTreeNodesStore.isNodeExpanded(component._id))
) {
return component._children[0]._id
} else if (!parent) {
@@ -803,7 +804,10 @@ export class ComponentStore extends BudiStore {
// sibling
const previousSibling = parent._children[index - 1]
const definition = this.getDefinition(previousSibling._component)
- if (definition.hasChildren) {
+ if (
+ definition.hasChildren &&
+ componentTreeNodesStore.isNodeExpanded(previousSibling._id)
+ ) {
previousSibling._children.push(originalComponent)
}
@@ -852,10 +856,13 @@ export class ComponentStore extends BudiStore {
// Move below the next sibling if we are not the last sibling
if (index < parent._children.length) {
- // If the next sibling has children, become the first child
+ // If the next sibling has children, and is not collapsed, become the first child
const nextSibling = parent._children[index]
const definition = this.getDefinition(nextSibling._component)
- if (definition.hasChildren) {
+ if (
+ definition.hasChildren &&
+ componentTreeNodesStore.isNodeExpanded(nextSibling._id)
+ ) {
nextSibling._children.splice(0, 0, originalComponent)
}
@@ -1151,13 +1158,3 @@ export const selectedComponent = derived(
return clone
}
)
-
-export const selectedComponentPath = derived(
- [componentStore, selectedScreen],
- ([$store, $selectedScreen]) => {
- return findComponentPath(
- $selectedScreen?.props,
- $store.selectedComponentId
- ).map(component => component._id)
- }
-)
diff --git a/packages/builder/src/stores/builder/index.js b/packages/builder/src/stores/builder/index.js
index 4df4bc3c41..5e94ce0552 100644
--- a/packages/builder/src/stores/builder/index.js
+++ b/packages/builder/src/stores/builder/index.js
@@ -1,10 +1,6 @@
import { layoutStore } from "./layouts.js"
import { appStore } from "./app.js"
-import {
- componentStore,
- selectedComponent,
- selectedComponentPath,
-} from "./components"
+import { componentStore, selectedComponent } from "./components"
import { navigationStore } from "./navigation.js"
import { themeStore } from "./theme.js"
import { screenStore, selectedScreen, sortedScreens } from "./screens.js"
@@ -31,8 +27,10 @@ import { integrations } from "./integrations"
import { sortedIntegrations } from "./sortedIntegrations"
import { queries } from "./queries"
import { flags } from "./flags"
+import componentTreeNodesStore from "./componentTreeNodes"
export {
+ componentTreeNodesStore,
layoutStore,
appStore,
componentStore,
@@ -51,7 +49,6 @@ export {
isOnlyUser,
deploymentStore,
selectedComponent,
- selectedComponentPath,
tables,
views,
viewsV2,
diff --git a/packages/builder/src/stores/portal/componentTreeNodesStore.js b/packages/builder/src/stores/portal/componentTreeNodesStore.js
deleted file mode 100644
index a6c07ae03b..0000000000
--- a/packages/builder/src/stores/portal/componentTreeNodesStore.js
+++ /dev/null
@@ -1,36 +0,0 @@
-import { createSessionStorageStore } from "@budibase/frontend-core"
-
-const baseStore = createSessionStorageStore("openNodes", {})
-
-const toggleNode = componentId => {
- baseStore.update(openNodes => {
- openNodes[`nodeOpen-${componentId}`] = !openNodes[`nodeOpen-${componentId}`]
-
- return openNodes
- })
-}
-
-const expandNode = componentId => {
- baseStore.update(openNodes => {
- openNodes[`nodeOpen-${componentId}`] = true
-
- return openNodes
- })
-}
-
-const collapseNode = componentId => {
- baseStore.update(openNodes => {
- openNodes[`nodeOpen-${componentId}`] = false
-
- return openNodes
- })
-}
-
-const store = {
- subscribe: baseStore.subscribe,
- toggleNode,
- expandNode,
- collapseNode,
-}
-
-export default store