Don't clear drop target on invalid selection
This commit is contained in:
parent
a6802e921f
commit
9c82a9d073
|
@ -96,6 +96,7 @@
|
|||
$: selected =
|
||||
$builderStore.inBuilder && $builderStore.selectedComponentId === id
|
||||
$: inSelectedPath = $componentStore.selectedComponentPath?.includes(id)
|
||||
$: inDropPath = $componentStore.dropPath?.includes(id)
|
||||
$: inDragPath = inSelectedPath && $builderStore.editMode
|
||||
|
||||
// Derive definition properties which can all be optional, so need to be
|
||||
|
@ -108,7 +109,7 @@
|
|||
// Interactive components can be selected, dragged and highlighted inside
|
||||
// the builder preview
|
||||
$: builderInteractive =
|
||||
$builderStore.inBuilder && insideScreenslot && !isBlock
|
||||
$builderStore.inBuilder && insideScreenslot && !isBlock && !instance.static
|
||||
$: devToolsInteractive = $devToolsStore.allowSelection && !isBlock
|
||||
$: interactive = builderInteractive || devToolsInteractive
|
||||
$: editing = editable && selected && $builderStore.editMode
|
||||
|
@ -453,10 +454,11 @@
|
|||
class:interactive
|
||||
class:editing
|
||||
class:block={isBlock}
|
||||
class:explode={children.length && !isLayout && $builderStore.isDragging}
|
||||
class:explode={children.length && !isLayout && inDropPath && false}
|
||||
data-id={id}
|
||||
data-name={name}
|
||||
data-icon={icon}
|
||||
data-placeholder={id === "placeholder"}
|
||||
>
|
||||
<svelte:component this={constructor} bind:this={ref} {...initialSettings}>
|
||||
{#if hasMissingRequiredSettings}
|
||||
|
|
|
@ -77,19 +77,16 @@
|
|||
builderStore.actions.setDragging(false)
|
||||
}
|
||||
|
||||
// Callback when on top of a component
|
||||
const onDragOver = e => {
|
||||
// Skip if we aren't validly dragging currently
|
||||
if (!dragInfo || !dropInfo) {
|
||||
return
|
||||
const validateDrop = (dropInfo, e) => {
|
||||
if (!dropInfo) {
|
||||
return null
|
||||
}
|
||||
|
||||
e.preventDefault()
|
||||
const { droppableInside, bounds } = dropInfo
|
||||
const { top, left, height, width } = bounds
|
||||
const mouseY = e.clientY
|
||||
const mouseX = e.clientX
|
||||
const snapFactor = droppableInside ? 0.33 : 0.5
|
||||
const snapFactor = droppableInside ? 0.25 : 0.5
|
||||
const snapLimitV = Math.min(40, height * snapFactor)
|
||||
const snapLimitH = Math.min(40, width * snapFactor)
|
||||
|
||||
|
@ -108,20 +105,32 @@
|
|||
|
||||
// When no edges match, drop inside if possible
|
||||
if (!sides.length) {
|
||||
dropInfo.mode = droppableInside ? "inside" : null
|
||||
dropInfo.side = null
|
||||
return
|
||||
if (droppableInside) {
|
||||
return {
|
||||
...dropInfo,
|
||||
mode: "inside",
|
||||
side: null,
|
||||
}
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
// When one edge matches, use that edge
|
||||
if (sides.length === 1) {
|
||||
dropInfo.side = sides[0]
|
||||
if ([Sides.Top, Sides.Left].includes(sides[0])) {
|
||||
dropInfo.mode = "above"
|
||||
} else {
|
||||
dropInfo.mode = "below"
|
||||
return {
|
||||
...dropInfo,
|
||||
mode: "above",
|
||||
side: sides[0],
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
...dropInfo,
|
||||
mode: "below",
|
||||
side: sides[0],
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// When 2 edges match, work out which is closer
|
||||
|
@ -134,9 +143,34 @@
|
|||
const edge = delta1 < delta2 ? sides[0] : sides[1]
|
||||
dropInfo.side = edge
|
||||
if ([Sides.Top, Sides.Left].includes(edge)) {
|
||||
dropInfo.mode = "above"
|
||||
return {
|
||||
...dropInfo,
|
||||
mode: "above",
|
||||
side: edge,
|
||||
}
|
||||
} else {
|
||||
dropInfo.mode = "below"
|
||||
return {
|
||||
...dropInfo,
|
||||
mode: "below",
|
||||
side: edge,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Callback when on top of a component
|
||||
const onDragOver = e => {
|
||||
// Skip if we aren't validly dragging currently
|
||||
if (!dragInfo || !dropInfo) {
|
||||
return
|
||||
}
|
||||
|
||||
e.preventDefault()
|
||||
|
||||
const nextDropInfo = validateDrop(dropInfo, e)
|
||||
if (nextDropInfo) {
|
||||
console.log("set from over")
|
||||
dropInfo = nextDropInfo
|
||||
console.log(dropInfo.mode, dropInfo.target)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -147,6 +181,16 @@
|
|||
return
|
||||
}
|
||||
|
||||
// Update drop target
|
||||
const dropTarget = e.target.closest(".component")
|
||||
builderStore.actions.setDropTarget(dropTarget?.dataset.id)
|
||||
|
||||
// // Do nothing if this is the placeholder
|
||||
// if (element.dataset.id === "placeholder") {
|
||||
// console.log("placeholder")
|
||||
// return
|
||||
// }
|
||||
|
||||
const element = e.target.closest(".component:not(.block)")
|
||||
if (
|
||||
element &&
|
||||
|
@ -172,15 +216,20 @@
|
|||
// dragOver
|
||||
const child = getDOMNodeForComponent(e.target)
|
||||
const bounds = child.getBoundingClientRect()
|
||||
dropInfo = {
|
||||
let nextDropInfo = {
|
||||
target,
|
||||
name: element.dataset.name,
|
||||
icon: element.dataset.icon,
|
||||
droppableInside: element.classList.contains("empty"),
|
||||
bounds,
|
||||
}
|
||||
nextDropInfo = validateDrop(nextDropInfo, e)
|
||||
if (nextDropInfo) {
|
||||
console.log("set from enter")
|
||||
dropInfo = nextDropInfo
|
||||
}
|
||||
} else {
|
||||
dropInfo = null
|
||||
// dropInfo = null
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -200,6 +249,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
$: mode = dropInfo?.mode
|
||||
$: target = dropInfo?.target
|
||||
$: builderStore.actions.updateDNDPlaceholder(mode, target)
|
||||
|
||||
onMount(() => {
|
||||
// Events fired on the draggable target
|
||||
document.addEventListener("dragstart", onDragStart, false)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { writable, get } from "svelte/store"
|
||||
import { API } from "api"
|
||||
import { devToolsStore } from "./devTools.js"
|
||||
import { findComponentPathById } from "../utils/components.js"
|
||||
|
||||
const dispatchEvent = (type, data = {}) => {
|
||||
window.parent.postMessage({ type, data })
|
||||
|
@ -20,6 +21,9 @@ const createBuilderStore = () => {
|
|||
navigation: null,
|
||||
hiddenComponentIds: [],
|
||||
usedPlugins: null,
|
||||
dndMode: null,
|
||||
dndTarget: null,
|
||||
dropTarget: null,
|
||||
|
||||
// Legacy - allow the builder to specify a layout
|
||||
layout: null,
|
||||
|
@ -102,6 +106,20 @@ const createBuilderStore = () => {
|
|||
// Notify the builder so we can reload component definitions
|
||||
dispatchEvent("reload-plugin")
|
||||
},
|
||||
updateDNDPlaceholder: (mode, target) => {
|
||||
console.log(mode, target)
|
||||
store.update(state => {
|
||||
state.dndMode = mode
|
||||
state.dndTarget = target
|
||||
return state
|
||||
})
|
||||
},
|
||||
setDropTarget: target => {
|
||||
store.update(state => {
|
||||
state.dropTarget = target
|
||||
return state
|
||||
})
|
||||
},
|
||||
}
|
||||
return {
|
||||
...store,
|
||||
|
|
|
@ -36,8 +36,13 @@ const createComponentStore = () => {
|
|||
const definition = getComponentDefinition(component?._component)
|
||||
|
||||
// Derive the selected component path
|
||||
const path =
|
||||
const selectedPath =
|
||||
findComponentPathById(asset?.props, selectedComponentId) || []
|
||||
let dropPath = []
|
||||
if ($builderState.isDragging) {
|
||||
dropPath =
|
||||
findComponentPathById(asset?.props, $builderState.dropTarget) || []
|
||||
}
|
||||
|
||||
return {
|
||||
customComponentManifest: $store.customComponentManifest,
|
||||
|
@ -45,9 +50,10 @@ const createComponentStore = () => {
|
|||
$store.mountedComponents[selectedComponentId],
|
||||
selectedComponent: component,
|
||||
selectedComponentDefinition: definition,
|
||||
selectedComponentPath: path?.map(component => component._id),
|
||||
selectedComponentPath: selectedPath?.map(component => component._id),
|
||||
mountedComponentCount: Object.keys($store.mountedComponents).length,
|
||||
currentAsset: asset,
|
||||
dropPath: dropPath?.map(component => component._id),
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
@ -3,6 +3,11 @@ import { routeStore } from "./routes"
|
|||
import { builderStore } from "./builder"
|
||||
import { appStore } from "./app"
|
||||
import { RoleUtils } from "@budibase/frontend-core"
|
||||
import {
|
||||
findComponentById,
|
||||
findComponentPathById,
|
||||
} from "../utils/components.js"
|
||||
import { Helpers } from "@budibase/bbui"
|
||||
|
||||
const createScreenStore = () => {
|
||||
const store = derived(
|
||||
|
@ -13,7 +18,7 @@ const createScreenStore = () => {
|
|||
|
||||
if ($builderStore.inBuilder) {
|
||||
// Use builder defined definitions if inside the builder preview
|
||||
activeScreen = $builderStore.screen
|
||||
activeScreen = Helpers.cloneDeep($builderStore.screen)
|
||||
screens = [activeScreen]
|
||||
|
||||
// Legacy - allow the builder to specify a layout
|
||||
|
@ -24,9 +29,11 @@ const createScreenStore = () => {
|
|||
// Find the correct screen by matching the current route
|
||||
screens = $appStore.screens || []
|
||||
if ($routeStore.activeRoute) {
|
||||
activeScreen = screens.find(
|
||||
activeScreen = Helpers.cloneDeep(
|
||||
screens.find(
|
||||
screen => screen._id === $routeStore.activeRoute.screenId
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// Legacy - find the custom layout for the selected screen
|
||||
|
@ -40,6 +47,34 @@ const createScreenStore = () => {
|
|||
}
|
||||
}
|
||||
|
||||
// Insert DND placeholder if required
|
||||
const { dndTarget, dndMode, selectedComponentId } = $builderStore
|
||||
const insert = false
|
||||
if (insert && activeScreen && dndTarget && dndMode) {
|
||||
let selectedComponent = findComponentById(
|
||||
activeScreen.props,
|
||||
selectedComponentId
|
||||
)
|
||||
const placeholder = {
|
||||
...selectedComponent,
|
||||
_id: "placeholder",
|
||||
static: true,
|
||||
}
|
||||
// delete selectedComponent._component
|
||||
if (dndMode === "inside") {
|
||||
const target = findComponentById(activeScreen.props, dndTarget)
|
||||
target._children = [placeholder]
|
||||
} else {
|
||||
const path = findComponentPathById(activeScreen.props, dndTarget)
|
||||
const parent = path?.[path.length - 2]
|
||||
if (parent) {
|
||||
const idx = parent._children.findIndex(x => x._id === dndTarget)
|
||||
const delta = dndMode === "below" ? 1 : -1
|
||||
parent._children.splice(idx + delta, 0, placeholder)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Assign ranks to screens, preferring higher roles and home screens
|
||||
screens.forEach(screen => {
|
||||
const roleId = screen.routing.roleId
|
||||
|
|
Loading…
Reference in New Issue