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).

Budibase data 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) }