diff --git a/README.md b/README.md
index 64492b97e4..26ad9f80c2 100644
--- a/README.md
+++ b/README.md
@@ -65,7 +65,7 @@ Budibase is open-source - licensed as GPL v3. This should fill you with confiden
### Load data or start from scratch
-Budibase pulls data from multiple sources, including MongoDB, CouchDB, PostgreSQL, MySQL, Airtable, S3, DynamoDB, or a REST API. And unlike other platforms, with Budibase you can start from scratch and create business apps with no data sources. [Request new datasources](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas).
+Budibase pulls data from multiple sources, including MongoDB, CouchDB, PostgreSQL, MariaDB, MySQL, Airtable, S3, DynamoDB, or a REST API. And unlike other platforms, with Budibase you can start from scratch and create business apps with no data sources. [Request new datasources](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas).
diff --git a/packages/client/src/components/Component.svelte b/packages/client/src/components/Component.svelte
index 7b87040c4f..b65d7b0cdc 100644
--- a/packages/client/src/components/Component.svelte
+++ b/packages/client/src/components/Component.svelte
@@ -153,7 +153,7 @@
$: builderInteractive =
$builderStore.inBuilder && insideScreenslot && !isBlock && !instance.static
$: devToolsInteractive = $devToolsStore.allowSelection && !isBlock
- $: interactive = !isRoot && (builderInteractive || devToolsInteractive)
+ $: interactive = builderInteractive || devToolsInteractive
$: editing = editable && selected && $builderStore.editMode
$: draggable =
!inDragPath &&
@@ -189,12 +189,6 @@
// Scroll the selected element into view
$: selected && scrollIntoView()
- // When dragging and dropping, pad components to allow dropping between
- // nested layers. Only reset this when dragging stops.
- let pad = false
- $: pad = pad || (interactive && hasChildren && inDndPath)
- $: $dndIsDragging, (pad = false)
-
// Themes
$: currentTheme = $context?.device?.theme
$: darkMode = !currentTheme?.includes("light")
@@ -206,8 +200,10 @@
}
// Metadata to pass into grid action to apply CSS
- const insideGrid =
- parent?._component.endsWith("/container") && parent?.layout === "grid"
+ const checkGrid = x =>
+ x?._component.endsWith("/container") && x?.layout === "grid"
+ $: insideGrid = checkGrid(parent)
+ $: isGrid = checkGrid(instance)
$: gridMetadata = {
insideGrid,
ignoresLayout: definition?.ignoresLayout === true,
@@ -219,6 +215,12 @@
errored: errorState,
}
+ // When dragging and dropping, pad components to allow dropping between
+ // nested layers. Only reset this when dragging stops.
+ let pad = false
+ $: pad = pad || (!isGrid && interactive && hasChildren && inDndPath)
+ $: $dndIsDragging, (pad = false)
+
// Update component context
$: store.set({
id,
@@ -231,12 +233,14 @@
empty: emptyState,
selected,
interactive,
+ isRoot,
draggable,
editable,
isBlock,
},
empty: emptyState,
selected,
+ isRoot,
inSelectedPath,
name,
editing,
@@ -672,6 +676,7 @@
class:parent={hasChildren}
class:block={isBlock}
class:error={errorState}
+ class:root={isRoot}
data-id={id}
data-name={name}
data-icon={icon}
diff --git a/packages/client/src/components/app/EmptyPlaceholder.svelte b/packages/client/src/components/app/EmptyPlaceholder.svelte
index 2c3596aadf..b93bd01457 100644
--- a/packages/client/src/components/app/EmptyPlaceholder.svelte
+++ b/packages/client/src/components/app/EmptyPlaceholder.svelte
@@ -18,9 +18,11 @@
display: flex;
flex-direction: row;
justify-content: flex-start;
- align-items: center;
+ align-items: flex-start;
color: var(--spectrum-global-color-gray-600);
font-size: var(--font-size-s);
gap: var(--spacing-s);
+ grid-column: 1 / -1;
+ grid-row: 1 / -1;
}
diff --git a/packages/client/src/components/app/ScreenPlaceholder.svelte b/packages/client/src/components/app/ScreenPlaceholder.svelte
index 7635f55054..27d08c20ff 100644
--- a/packages/client/src/components/app/ScreenPlaceholder.svelte
+++ b/packages/client/src/components/app/ScreenPlaceholder.svelte
@@ -23,6 +23,8 @@
align-items: center;
gap: var(--spacing-s);
flex: 1 1 auto;
+ grid-column: 1 / -1;
+ grid-row: 1 / -1;
}
.placeholder :global(.spectrum-Button) {
margin-top: var(--spacing-m);
diff --git a/packages/client/src/components/app/container/GridContainer.svelte b/packages/client/src/components/app/container/GridContainer.svelte
index 1f68926658..bbcdbbf689 100644
--- a/packages/client/src/components/app/container/GridContainer.svelte
+++ b/packages/client/src/components/app/container/GridContainer.svelte
@@ -27,7 +27,6 @@
$: availableRows = Math.floor(height / GridRowHeight)
$: rows = Math.max(requiredRows, availableRows)
$: mobile = $context.device.mobile
- $: empty = $component.empty
$: colSize = width / GridColumns
$: styles.set({
...$component.styles,
@@ -40,7 +39,6 @@
"--col-size": colSize,
"--row-size": GridRowHeight,
},
- empty: false,
})
// Calculates the minimum number of rows required to render all child
@@ -145,9 +143,7 @@
{/each}
{/if}
-
-
- {#if !empty && mounted}
+ {#if mounted}
{/if}
diff --git a/packages/client/src/components/preview/DNDPlaceholderOverlay.svelte b/packages/client/src/components/preview/DNDPlaceholderOverlay.svelte
index 0ad4280e07..61cecc885b 100644
--- a/packages/client/src/components/preview/DNDPlaceholderOverlay.svelte
+++ b/packages/client/src/components/preview/DNDPlaceholderOverlay.svelte
@@ -6,8 +6,11 @@
let left, top, height, width
const updatePosition = () => {
- const node =
- document.getElementsByClassName(DNDPlaceholderID)[0]?.childNodes[0]
+ let node = document.getElementsByClassName(DNDPlaceholderID)[0]
+ const insideGrid = node?.dataset.insideGrid === "true"
+ if (!insideGrid) {
+ node = document.getElementsByClassName(`${DNDPlaceholderID}-dom`)[0]
+ }
if (!node) {
height = 0
width = 0
diff --git a/packages/client/src/components/preview/HoverIndicator.svelte b/packages/client/src/components/preview/HoverIndicator.svelte
index d204d77f49..f1c151ce16 100644
--- a/packages/client/src/components/preview/HoverIndicator.svelte
+++ b/packages/client/src/components/preview/HoverIndicator.svelte
@@ -19,7 +19,7 @@
newId = e.target.dataset.id
} else {
// Handle normal components
- const element = e.target.closest(".interactive.component")
+ const element = e.target.closest(".interactive.component:not(.root)")
newId = element?.dataset?.id
}
diff --git a/packages/client/src/stores/dnd.js b/packages/client/src/stores/dnd.js
index 1bdd510eb7..43d4eeeed8 100644
--- a/packages/client/src/stores/dnd.js
+++ b/packages/client/src/stores/dnd.js
@@ -1,5 +1,5 @@
import { writable } from "svelte/store"
-import { computed } from "../utils/computed.js"
+import { derivedMemo } from "@budibase/frontend-core"
const createDndStore = () => {
const initialState = {
@@ -78,11 +78,11 @@ export const dndStore = createDndStore()
// performance by deriving any state that needs to be externally observed.
// By doing this and using primitives, we can avoid invalidating other stores
// or components which depend on DND state unless values actually change.
-export const dndParent = computed(dndStore, x => x.drop?.parent)
-export const dndIndex = computed(dndStore, x => x.drop?.index)
-export const dndBounds = computed(dndStore, x => x.source?.bounds)
-export const dndIsDragging = computed(dndStore, x => !!x.source)
-export const dndIsNewComponent = computed(
+export const dndParent = derivedMemo(dndStore, x => x.drop?.parent)
+export const dndIndex = derivedMemo(dndStore, x => x.drop?.index)
+export const dndBounds = derivedMemo(dndStore, x => x.source?.bounds)
+export const dndIsDragging = derivedMemo(dndStore, x => !!x.source)
+export const dndIsNewComponent = derivedMemo(
dndStore,
x => x.source?.newComponentType != null
)
diff --git a/packages/client/src/stores/screens.js b/packages/client/src/stores/screens.js
index 3c5ece0a6c..bc87216660 100644
--- a/packages/client/src/stores/screens.js
+++ b/packages/client/src/stores/screens.js
@@ -92,6 +92,8 @@ const createScreenStore = () => {
width: `${$dndBounds?.width || 400}px`,
height: `${$dndBounds?.height || 200}px`,
opacity: 0,
+ "--default-width": $dndBounds?.width || 400,
+ "--default-height": $dndBounds?.height || 200,
},
},
static: true,
diff --git a/packages/client/src/utils/computed.js b/packages/client/src/utils/computed.js
deleted file mode 100644
index aa89e7ad1b..0000000000
--- a/packages/client/src/utils/computed.js
+++ /dev/null
@@ -1,38 +0,0 @@
-import { writable } from "svelte/store"
-
-/**
- * Extension of Svelte's built in "derived" stores, which the addition of deep
- * comparison of non-primitives. Falls back to using shallow comparison for
- * primitive types to avoid performance penalties.
- * Useful for instances where a deep comparison is cheaper than an additional
- * store invalidation.
- * @param store the store to observer
- * @param deriveValue the derivation function
- * @returns {Writable<*>} a derived svelte store containing just the derived value
- */
-export const computed = (store, deriveValue) => {
- const initialValue = deriveValue(store)
- const computedStore = writable(initialValue)
- let lastKey = getKey(initialValue)
-
- store.subscribe(state => {
- const value = deriveValue(state)
- const key = getKey(value)
- if (key !== lastKey) {
- lastKey = key
- computedStore.set(value)
- }
- })
-
- return computedStore
-}
-
-// Helper function to serialise any value into a primitive which can be cheaply
-// and shallowly compared
-const getKey = value => {
- if (value == null || typeof value !== "object") {
- return value
- } else {
- return JSON.stringify(value)
- }
-}
diff --git a/packages/client/src/utils/grid.js b/packages/client/src/utils/grid.js
index 1727b904ca..142a7ed55a 100644
--- a/packages/client/src/utils/grid.js
+++ b/packages/client/src/utils/grid.js
@@ -92,8 +92,12 @@ export const gridLayout = (node, metadata) => {
}
// Determine default width and height of component
- let width = errored ? 500 : definition?.size?.width || 200
- let height = errored ? 60 : definition?.size?.height || 200
+ let width = styles["--default-width"] ?? definition?.size?.width ?? 200
+ let height = styles["--default-height"] ?? definition?.size?.height ?? 200
+ if (errored) {
+ width = 500
+ height = 60
+ }
width += 2 * GridSpacing
height += 2 * GridSpacing
let vars = {
diff --git a/packages/client/src/utils/styleable.js b/packages/client/src/utils/styleable.js
index 0f484a9ab9..884420a1fd 100644
--- a/packages/client/src/utils/styleable.js
+++ b/packages/client/src/utils/styleable.js
@@ -93,7 +93,7 @@ export const styleable = (node, styles = {}) => {
node.addEventListener("mouseout", applyNormalStyles)
// Add builder preview click listener
- if (newStyles.interactive) {
+ if (newStyles.interactive && !newStyles.isRoot) {
node.addEventListener("click", selectComponent, false)
node.addEventListener("dblclick", editComponent, false)
}