Improve new component DND for grids and add mobile support

This commit is contained in:
Andrew Kingston 2024-09-05 14:54:16 +01:00
parent 5e98580b60
commit 6f8e669107
No known key found for this signature in database
6 changed files with 29 additions and 56 deletions

View File

@ -189,12 +189,6 @@
// Scroll the selected element into view // Scroll the selected element into view
$: selected && scrollIntoView() $: 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 // Themes
$: currentTheme = $context?.device?.theme $: currentTheme = $context?.device?.theme
$: darkMode = !currentTheme?.includes("light") $: darkMode = !currentTheme?.includes("light")
@ -206,8 +200,10 @@
} }
// Metadata to pass into grid action to apply CSS // Metadata to pass into grid action to apply CSS
const insideGrid = const checkGrid = x =>
parent?._component.endsWith("/container") && parent?.layout === "grid" x?._component.endsWith("/container") && x?.layout === "grid"
$: insideGrid = checkGrid(parent)
$: isGrid = checkGrid(instance)
$: gridMetadata = { $: gridMetadata = {
insideGrid, insideGrid,
ignoresLayout: definition?.ignoresLayout === true, ignoresLayout: definition?.ignoresLayout === true,
@ -219,6 +215,12 @@
errored: errorState, 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 // Update component context
$: store.set({ $: store.set({
id, id,

View File

@ -6,8 +6,11 @@
let left, top, height, width let left, top, height, width
const updatePosition = () => { const updatePosition = () => {
const node = let node = document.getElementsByClassName(DNDPlaceholderID)[0]
document.getElementsByClassName(DNDPlaceholderID)[0]?.childNodes[0] const insideGrid = node?.dataset.insideGrid === "true"
if (!insideGrid) {
node = document.getElementsByClassName(`${DNDPlaceholderID}-dom`)[0]
}
if (!node) { if (!node) {
height = 0 height = 0
width = 0 width = 0

View File

@ -1,5 +1,5 @@
import { writable } from "svelte/store" import { writable } from "svelte/store"
import { computed } from "../utils/computed.js" import { derivedMemo } from "@budibase/frontend-core"
const createDndStore = () => { const createDndStore = () => {
const initialState = { const initialState = {
@ -78,11 +78,11 @@ export const dndStore = createDndStore()
// performance by deriving any state that needs to be externally observed. // performance by deriving any state that needs to be externally observed.
// By doing this and using primitives, we can avoid invalidating other stores // By doing this and using primitives, we can avoid invalidating other stores
// or components which depend on DND state unless values actually change. // or components which depend on DND state unless values actually change.
export const dndParent = computed(dndStore, x => x.drop?.parent) export const dndParent = derivedMemo(dndStore, x => x.drop?.parent)
export const dndIndex = computed(dndStore, x => x.drop?.index) export const dndIndex = derivedMemo(dndStore, x => x.drop?.index)
export const dndBounds = computed(dndStore, x => x.source?.bounds) export const dndBounds = derivedMemo(dndStore, x => x.source?.bounds)
export const dndIsDragging = computed(dndStore, x => !!x.source) export const dndIsDragging = derivedMemo(dndStore, x => !!x.source)
export const dndIsNewComponent = computed( export const dndIsNewComponent = derivedMemo(
dndStore, dndStore,
x => x.source?.newComponentType != null x => x.source?.newComponentType != null
) )

View File

@ -92,6 +92,8 @@ const createScreenStore = () => {
width: `${$dndBounds?.width || 400}px`, width: `${$dndBounds?.width || 400}px`,
height: `${$dndBounds?.height || 200}px`, height: `${$dndBounds?.height || 200}px`,
opacity: 0, opacity: 0,
"--default-width": $dndBounds?.width || 400,
"--default-height": $dndBounds?.height || 200,
}, },
}, },
static: true, static: true,

View File

@ -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)
}
}

View File

@ -92,8 +92,12 @@ export const gridLayout = (node, metadata) => {
} }
// Determine default width and height of component // Determine default width and height of component
let width = errored ? 500 : definition?.size?.width || 200 let width = styles["--default-width"] ?? definition?.size?.width ?? 200
let height = errored ? 60 : definition?.size?.height || 200 let height = styles["--default-height"] ?? definition?.size?.height ?? 200
if (errored) {
width = 500
height = 60
}
width += 2 * GridSpacing width += 2 * GridSpacing
height += 2 * GridSpacing height += 2 * GridSpacing
let vars = { let vars = {