diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js
index c90ab10c9a..4122954303 100644
--- a/packages/builder/src/builderStore/store/frontend.js
+++ b/packages/builder/src/builderStore/store/frontend.js
@@ -1,6 +1,6 @@
import { get, writable } from "svelte/store"
import { cloneDeep } from "lodash/fp"
-import { selectedScreen, selectedComponent } from "builderStore"
+import { selectedScreen, selectedComponent, store } from "builderStore"
import {
datasources,
integrations,
@@ -451,7 +451,7 @@ export const getFrontendStore = () => {
...extras,
}
},
- create: async (componentName, presetProps) => {
+ create: async (componentName, presetProps, parent, index) => {
const state = get(store)
const componentInstance = store.actions.components.createInstance(
componentName,
@@ -461,48 +461,62 @@ export const getFrontendStore = () => {
return
}
- // Patch selected screen
- await store.actions.screens.patch(screen => {
- // Find the selected component
- const currentComponent = findComponent(
- screen.props,
- state.selectedComponentId
- )
- if (!currentComponent) {
- return false
- }
-
- // Find parent node to attach this component to
- let parentComponent
- if (currentComponent) {
- // Use selected component as parent if one is selected
- const definition = store.actions.components.getDefinition(
- currentComponent._component
- )
- if (definition?.hasChildren) {
- // Use selected component if it allows children
- parentComponent = currentComponent
+ // Insert in position if specified
+ if (parent && index != null) {
+ await store.actions.screens.patch(screen => {
+ let parentComponent = findComponent(screen.props, parent)
+ if (!parentComponent._children?.length) {
+ parentComponent._children = [componentInstance]
} else {
- // Otherwise we need to use the parent of this component
- parentComponent = findComponentParent(
- screen.props,
- currentComponent._id
- )
+ parentComponent._children.splice(index, 0, componentInstance)
}
- } else {
- // Use screen or layout if no component is selected
- parentComponent = screen.props
- }
+ })
+ }
- // Attach new component
- if (!parentComponent) {
- return false
- }
- if (!parentComponent._children) {
- parentComponent._children = []
- }
- parentComponent._children.push(componentInstance)
- })
+ // Otherwise we work out where this component should be inserted
+ else {
+ await store.actions.screens.patch(screen => {
+ // Find the selected component
+ const currentComponent = findComponent(
+ screen.props,
+ state.selectedComponentId
+ )
+ if (!currentComponent) {
+ return false
+ }
+
+ // Find parent node to attach this component to
+ let parentComponent
+ if (currentComponent) {
+ // Use selected component as parent if one is selected
+ const definition = store.actions.components.getDefinition(
+ currentComponent._component
+ )
+ if (definition?.hasChildren) {
+ // Use selected component if it allows children
+ parentComponent = currentComponent
+ } else {
+ // Otherwise we need to use the parent of this component
+ parentComponent = findComponentParent(
+ screen.props,
+ currentComponent._id
+ )
+ }
+ } else {
+ // Use screen or layout if no component is selected
+ parentComponent = screen.props
+ }
+
+ // Attach new component
+ if (!parentComponent) {
+ return false
+ }
+ if (!parentComponent._children) {
+ parentComponent._children = []
+ }
+ parentComponent._children.push(componentInstance)
+ })
+ }
// Select new component
store.update(state => {
@@ -990,6 +1004,19 @@ export const getFrontendStore = () => {
}))
},
},
+ dnd: {
+ start: component => {
+ store.actions.preview.sendEvent("dragging-new-component", {
+ dragging: true,
+ component,
+ })
+ },
+ stop: () => {
+ store.actions.preview.sendEvent("dragging-new-component", {
+ dragging: false,
+ })
+ },
+ },
}
return store
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 309b676a70..a4c4c5b839 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
@@ -213,6 +213,9 @@
await store.actions.components.handleEjectBlock(id, definition)
} else if (type === "reload-plugin") {
await store.actions.components.refreshDefinitions()
+ } else if (type === "drop-new-component") {
+ const { component, parent, index } = data
+ await store.actions.components.create(component, null, parent, index)
} else {
console.warn(`Client sent unknown event type: ${type}`)
}
diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/components/[componentId]/new/_components/NewComponentPanel.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/components/[componentId]/new/_components/NewComponentPanel.svelte
index 8cef10fb26..07781441c5 100644
--- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/components/[componentId]/new/_components/NewComponentPanel.svelte
+++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/components/[componentId]/new/_components/NewComponentPanel.svelte
@@ -169,6 +169,14 @@
window.removeEventListener("keydown", handleKeyDown)
}
})
+
+ const onDragStart = component => {
+ store.actions.dnd.start(component)
+ }
+
+ const onDragEnd = () => {
+ store.actions.dnd.stop()
+ }
@@ -206,6 +214,9 @@
{category.name}
{#each category.children as component}
onDragStart(component.component)}
+ on:dragend={onDragEnd}
data-cy={`component-${component.name}`}
class="component"
class:selected={selectedIndex ===
diff --git a/packages/client/src/components/preview/DNDHandler.svelte b/packages/client/src/components/preview/DNDHandler.svelte
index 40f52dd1ae..361f11dff2 100644
--- a/packages/client/src/components/preview/DNDHandler.svelte
+++ b/packages/client/src/components/preview/DNDHandler.svelte
@@ -2,21 +2,22 @@
import { onMount, onDestroy } from "svelte"
import { get } from "svelte/store"
import IndicatorSet from "./IndicatorSet.svelte"
- import { builderStore, componentStore } from "stores"
+ import {
+ builderStore,
+ componentStore,
+ dndStore,
+ dndParent,
+ isDragging,
+ } from "stores"
import DNDPlaceholderOverlay from "./DNDPlaceholderOverlay.svelte"
import { Utils } from "@budibase/frontend-core"
import { findComponentById } from "utils/components.js"
- let sourceInfo
- let targetInfo
- let dropInfo
-
- // These reactive statements are just a trick to only update the store when
- // the value of one of the properties actually changes
- $: parent = dropInfo?.parent
- $: index = dropInfo?.index
- $: bounds = sourceInfo?.bounds
- $: builderStore.actions.updateDNDPlaceholder(parent, index, bounds)
+ // Cache some dnd store state as local variables as it massively helps
+ // performance. It lets us avoid calling svelte getters on every DOM action.
+ $: source = $dndStore.source
+ $: target = $dndStore.target
+ $: drop = $dndStore.drop
// Util to get the inner DOM node by a component ID
const getDOMNode = id => {
@@ -37,19 +38,16 @@
// Callback when drag stops (whether dropped or not)
const stopDragging = () => {
- // Reset state
- sourceInfo = null
- targetInfo = null
- dropInfo = null
- builderStore.actions.setDragging(false)
-
// Reset listener
- if (sourceInfo) {
- const component = document.getElementsByClassName(sourceInfo.id)[0]
+ if (source?.id) {
+ const component = document.getElementsByClassName(source?.id)[0]
if (component) {
component.removeEventListener("dragend", stopDragging)
}
}
+
+ // Reset state
+ dndStore.actions.reset()
}
// Callback when initially starting a drag on a draggable component
@@ -66,6 +64,7 @@
component.addEventListener("dragend", stopDragging)
// Update state
+ const id = component.dataset.id
const parentId = component.dataset.parent
const parent = findComponentById(
get(componentStore).currentAsset.props,
@@ -74,14 +73,13 @@
const index = parent._children.findIndex(
x => x._id === component.dataset.id
)
- sourceInfo = {
- id: component.dataset.id,
+ dndStore.actions.startDragging({
+ id,
bounds: component.children[0].getBoundingClientRect(),
parent: parentId,
index,
- }
- builderStore.actions.selectComponent(sourceInfo.id)
- builderStore.actions.setDragging(true)
+ })
+ builderStore.actions.selectComponent(id)
// Set initial drop info to show placeholder exactly where the dragged
// component is.
@@ -89,20 +87,20 @@
// the same handler as selecting a new component (which causes a client
// re-initialisation).
setTimeout(() => {
- dropInfo = {
+ dndStore.actions.updateDrop({
parent: parentId,
index,
- }
+ })
}, 0)
}
// Core logic for handling drop events and determining where to render the
// drop target placeholder
const processEvent = (mouseX, mouseY) => {
- if (!targetInfo) {
+ if (!target) {
return null
}
- let { id, parent, node, acceptsChildren, empty } = targetInfo
+ let { id, parent, node, acceptsChildren, empty } = target
// If we're over something that does not accept children then we go up a
// level and consider the mouse position relative to the parent
@@ -115,10 +113,10 @@
// We're now hovering over something which does accept children.
// If it is empty, just go inside it.
if (empty) {
- dropInfo = {
+ dndStore.actions.updateDrop({
parent: id,
index: 0,
- }
+ })
return
}
@@ -188,10 +186,10 @@
while (idx < breakpoints.length && breakpoints[idx] < mousePosition) {
idx++
}
- dropInfo = {
+ dndStore.actions.updateDrop({
parent: id,
index: idx,
- }
+ })
}
const throttledProcessEvent = Utils.throttle(processEvent, 130)
@@ -202,7 +200,7 @@
// Callback when on top of a component
const onDragOver = e => {
- if (!sourceInfo || !targetInfo) {
+ if (!source || !target) {
return
}
handleEvent(e)
@@ -210,69 +208,80 @@
// Callback when entering a potential drop target
const onDragEnter = e => {
- if (!sourceInfo) {
+ if (!source) {
return
}
// Find the next valid component to consider dropping over, ignoring nested
// block components
const component = e.target?.closest?.(
- `.component:not(.block):not(.${sourceInfo.id})`
+ `.component:not(.block):not(.${source.id})`
)
if (component && component.classList.contains("droppable")) {
- targetInfo = {
+ dndStore.actions.updateTarget({
id: component.dataset.id,
parent: component.dataset.parent,
node: getDOMNode(component.dataset.id),
empty: component.classList.contains("empty"),
acceptsChildren: component.classList.contains("parent"),
- }
+ })
handleEvent(e)
}
}
// Callback when dropping a drag on top of some component
const onDrop = () => {
- let target, mode
-
- // Convert parent + index into target + mode
- if (sourceInfo && dropInfo?.parent && dropInfo.index != null) {
- const parent = findComponentById(
- get(componentStore).currentAsset?.props,
- dropInfo.parent
- )
- if (!parent) {
- return
- }
-
- // Do nothing if we didn't change the location
- if (
- sourceInfo.parent === dropInfo.parent &&
- sourceInfo.index === dropInfo.index
- ) {
- return
- }
-
- // Filter out source component and placeholder from consideration
- const children = parent._children?.filter(
- x => x._id !== "placeholder" && x._id !== sourceInfo.id
- )
-
- // Use inside if no existing children
- if (!children?.length) {
- target = parent._id
- mode = "inside"
- } else if (dropInfo.index === 0) {
- target = children[0]?._id
- mode = "above"
- } else {
- target = children[dropInfo.index - 1]?._id
- mode = "below"
- }
+ if (!source || !drop?.parent || drop?.index == null) {
+ return
}
- if (target && mode) {
- builderStore.actions.moveComponent(sourceInfo.id, target, mode)
+ // Check if we're adding a new component rather than moving one
+ if (source.newComponentType) {
+ builderStore.actions.dropNewComponent(
+ source.newComponentType,
+ drop.parent,
+ drop.index
+ )
+ }
+
+ // Convert parent + index into target + mode
+ let legacyDropTarget, legacyDropMode
+ const parent = findComponentById(
+ get(componentStore).currentAsset?.props,
+ drop.parent
+ )
+ if (!parent) {
+ return
+ }
+
+ // Do nothing if we didn't change the location
+ if (source.parent === drop.parent && source.index === drop.index) {
+ return
+ }
+
+ // Filter out source component and placeholder from consideration
+ const children = parent._children?.filter(
+ x => x._id !== "placeholder" && x._id !== source.id
+ )
+
+ // Use inside if no existing children
+ if (!children?.length) {
+ legacyDropTarget = parent._id
+ legacyDropMode = "inside"
+ } else if (drop.index === 0) {
+ legacyDropTarget = children[0]?._id
+ legacyDropMode = "above"
+ } else {
+ legacyDropTarget = children[drop.index - 1]?._id
+ legacyDropMode = "below"
+ }
+
+ if (legacyDropTarget && legacyDropMode) {
+ builderStore.actions.moveComponent(
+ source.id,
+ legacyDropTarget,
+ legacyDropMode
+ )
}
}
@@ -298,13 +307,13 @@
-{#if $builderStore.isDragging}
+{#if $isDragging}
{/if}
diff --git a/packages/client/src/components/preview/DNDPlaceholder.svelte b/packages/client/src/components/preview/DNDPlaceholder.svelte
index 9efabdbc27..3725f9e06e 100644
--- a/packages/client/src/components/preview/DNDPlaceholder.svelte
+++ b/packages/client/src/components/preview/DNDPlaceholder.svelte
@@ -1,8 +1,8 @@
- import { builderStore } from "stores"
+ import { builderStore, isDragging } from "stores"
import IndicatorSet from "./IndicatorSet.svelte"
$: color = $builderStore.editMode
@@ -8,7 +8,7 @@
{
diff --git a/packages/client/src/index.js b/packages/client/src/index.js
index 526ed1c10d..17f1c94359 100644
--- a/packages/client/src/index.js
+++ b/packages/client/src/index.js
@@ -6,6 +6,7 @@ import {
blockStore,
componentStore,
environmentStore,
+ dndStore,
} from "./stores"
import loadSpectrumIcons from "@budibase/bbui/spectrum-icons-rollup.js"
import { get } from "svelte/store"
@@ -60,6 +61,24 @@ const loadBudibase = async () => {
if (name === "eject-block") {
const block = blockStore.actions.getBlock(payload)
block?.eject()
+ } else if (name === "dragging-new-component") {
+ const { dragging, component } = payload
+ if (dragging) {
+ dndStore.actions.startDragging({
+ id: null,
+ parent: null,
+ bounds: {
+ height: 64,
+ width: 64,
+ },
+ index: null,
+ newComponentType: component,
+ })
+ builderStore.actions.setDraggingNewComponent(true)
+ } else {
+ dndStore.actions.reset()
+ builderStore.actions.setDraggingNewComponent(false)
+ }
}
}
diff --git a/packages/client/src/stores/builder.js b/packages/client/src/stores/builder.js
index 0bb5693edb..135c08343e 100644
--- a/packages/client/src/stores/builder.js
+++ b/packages/client/src/stores/builder.js
@@ -16,13 +16,10 @@ const createBuilderStore = () => {
theme: null,
customTheme: null,
previewDevice: "desktop",
- isDragging: false,
+ draggingNewComponent: false,
navigation: null,
hiddenComponentIds: [],
usedPlugins: null,
- dndParent: null,
- dndIndex: null,
- dndBounds: null,
// Legacy - allow the builder to specify a layout
layout: null,
@@ -70,11 +67,19 @@ const createBuilderStore = () => {
mode,
})
},
- setDragging: dragging => {
- if (dragging === get(store).isDragging) {
+ dropNewComponent: (component, parent, index) => {
+ console.log("dispatch", component, parent, index)
+ dispatchEvent("drop-new-component", {
+ component,
+ parent,
+ index,
+ })
+ },
+ setDraggingNewComponent: draggingNewComponent => {
+ if (draggingNewComponent === get(store).draggingNewComponent) {
return
}
- store.update(state => ({ ...state, isDragging: dragging }))
+ store.update(state => ({ ...state, draggingNewComponent }))
},
setEditMode: enabled => {
if (enabled === get(store).editMode) {
@@ -111,14 +116,6 @@ const createBuilderStore = () => {
// Notify the builder so we can reload component definitions
dispatchEvent("reload-plugin")
},
- updateDNDPlaceholder: (parent, index, bounds) => {
- store.update(state => {
- state.dndParent = parent
- state.dndIndex = index
- state.dndBounds = bounds
- return state
- })
- },
}
return {
...store,
diff --git a/packages/client/src/stores/components.js b/packages/client/src/stores/components.js
index e1d6012150..71b7deb5a6 100644
--- a/packages/client/src/stores/components.js
+++ b/packages/client/src/stores/components.js
@@ -4,6 +4,7 @@ import { findComponentById, findComponentPathById } from "../utils/components"
import { devToolsStore } from "./devTools"
import { screenStore } from "./screens"
import { builderStore } from "./builder"
+import { dndParent } from "./dnd.js"
import Router from "../components/Router.svelte"
import DNDPlaceholder from "../components/preview/DNDPlaceholder.svelte"
import * as AppComponents from "../components/app/index.js"
@@ -19,8 +20,8 @@ const createComponentStore = () => {
})
const derivedStore = derived(
- [store, builderStore, devToolsStore, screenStore],
- ([$store, $builderState, $devToolsState, $screenState]) => {
+ [store, builderStore, devToolsStore, screenStore, dndParent],
+ ([$store, $builderState, $devToolsState, $screenState, $dndParent]) => {
// Avoid any of this logic if we aren't in the builder preview
if (!$builderState.inBuilder && !$devToolsState.visible) {
return {}
@@ -40,8 +41,7 @@ const createComponentStore = () => {
// Derive the selected component path
const selectedPath =
findComponentPathById(asset?.props, selectedComponentId) || []
- const dndPath =
- findComponentPathById(asset?.props, $builderState.dndParent) || []
+ const dndPath = findComponentPathById(asset?.props, $dndParent) || []
return {
customComponentManifest: $store.customComponentManifest,
diff --git a/packages/client/src/stores/derived/currentRole.js b/packages/client/src/stores/derived/currentRole.js
new file mode 100644
index 0000000000..28287e1ea4
--- /dev/null
+++ b/packages/client/src/stores/derived/currentRole.js
@@ -0,0 +1,11 @@
+import { derived } from "svelte/store"
+import { devToolsStore } from "../devTools.js"
+import { authStore } from "../auth.js"
+
+// Derive the current role of the logged-in user
+export const currentRole = derived(
+ [devToolsStore, authStore],
+ ([$devToolsStore, $authStore]) => {
+ return ($devToolsStore.enabled && $devToolsStore.role) || $authStore?.roleId
+ }
+)
diff --git a/packages/client/src/stores/derived/index.js b/packages/client/src/stores/derived/index.js
new file mode 100644
index 0000000000..85df1aaafa
--- /dev/null
+++ b/packages/client/src/stores/derived/index.js
@@ -0,0 +1,2 @@
+export { isDragging } from "./isDragging.js"
+export { currentRole } from "./currentRole.js"
diff --git a/packages/client/src/stores/derived/isDragging.js b/packages/client/src/stores/derived/isDragging.js
new file mode 100644
index 0000000000..d3edd586c6
--- /dev/null
+++ b/packages/client/src/stores/derived/isDragging.js
@@ -0,0 +1,10 @@
+import { derived } from "svelte/store"
+import { dndStore } from "../dnd"
+import { builderStore } from "../builder.js"
+
+// Derive whether we are dragging or not
+export const isDragging = derived(
+ [dndStore, builderStore],
+ ([$dndStore, $builderStore]) =>
+ $dndStore.source != null || $builderStore.draggingNewComponent
+)
diff --git a/packages/client/src/stores/dnd.js b/packages/client/src/stores/dnd.js
new file mode 100644
index 0000000000..805ec8c339
--- /dev/null
+++ b/packages/client/src/stores/dnd.js
@@ -0,0 +1,60 @@
+import { writable, derived } from "svelte/store"
+
+const createDndStore = () => {
+ const initialState = {
+ // Info about the dragged component
+ source: null,
+
+ // Info about the target component being hovered over
+ target: null,
+
+ // Info about where the component would be dropped
+ drop: null,
+ }
+ const store = writable(initialState)
+
+ // newComponentType is an optional field to signify we are creating a new
+ // component rather than moving an existing one
+ const startDragging = ({ id, parent, bounds, index, newComponentType }) => {
+ store.set({
+ ...initialState,
+ source: { id, parent, bounds, index, newComponentType },
+ })
+ }
+
+ const updateTarget = ({ id, parent, node, empty, acceptsChildren }) => {
+ store.update(state => {
+ state.target = { id, parent, node, empty, acceptsChildren }
+ return state
+ })
+ }
+
+ const updateDrop = ({ parent, index }) => {
+ store.update(state => {
+ state.drop = { parent, index }
+ return state
+ })
+ }
+
+ const reset = () => {
+ store.set(initialState)
+ }
+
+ return {
+ subscribe: store.subscribe,
+ actions: {
+ startDragging,
+ updateTarget,
+ updateDrop,
+ reset,
+ },
+ }
+}
+
+export const dndStore = createDndStore()
+export const dndParent = derived(dndStore, $dndStore => $dndStore.drop?.parent)
+export const dndIndex = derived(dndStore, $dndStore => $dndStore.drop?.index)
+export const dndBounds = derived(
+ dndStore,
+ $dndStore => $dndStore.source?.bounds
+)
diff --git a/packages/client/src/stores/index.js b/packages/client/src/stores/index.js
index 5b77762223..f86c0a0517 100644
--- a/packages/client/src/stores/index.js
+++ b/packages/client/src/stores/index.js
@@ -1,7 +1,3 @@
-import { derived } from "svelte/store"
-import { devToolsStore } from "./devTools.js"
-import { authStore } from "./auth.js"
-
export { authStore } from "./auth"
export { appStore } from "./app"
export { notificationStore } from "./notification"
@@ -19,6 +15,7 @@ export { uploadStore } from "./uploads.js"
export { rowSelectionStore } from "./rowSelection.js"
export { blockStore } from "./blocks.js"
export { environmentStore } from "./environment"
+export { dndStore, dndIndex, dndParent, dndBounds } from "./dnd"
// Context stores are layered and duplicated, so it is not a singleton
export { createContextStore } from "./context"
@@ -26,10 +23,5 @@ export { createContextStore } from "./context"
// Initialises an app by loading screens and routes
export { initialise } from "./initialise"
-// Derive the current role of the logged-in user
-export const currentRole = derived(
- [devToolsStore, authStore],
- ([$devToolsStore, $authStore]) => {
- return ($devToolsStore.enabled && $devToolsStore.role) || $authStore?.roleId
- }
-)
+// Derived state
+export * from "./derived"
diff --git a/packages/client/src/stores/screens.js b/packages/client/src/stores/screens.js
index 5c630d2adf..b0bc06eee9 100644
--- a/packages/client/src/stores/screens.js
+++ b/packages/client/src/stores/screens.js
@@ -2,6 +2,7 @@ import { derived } from "svelte/store"
import { routeStore } from "./routes"
import { builderStore } from "./builder"
import { appStore } from "./app"
+import { dndIndex, dndParent } from "./dnd.js"
import { RoleUtils } from "@budibase/frontend-core"
import { findComponentById, findComponentParent } from "../utils/components.js"
import { Helpers } from "@budibase/bbui"
@@ -9,8 +10,8 @@ import { DNDPlaceholderID, DNDPlaceholderType } from "constants"
const createScreenStore = () => {
const store = derived(
- [appStore, routeStore, builderStore],
- ([$appStore, $routeStore, $builderStore]) => {
+ [appStore, routeStore, builderStore, dndParent, dndIndex],
+ ([$appStore, $routeStore, $builderStore, $dndParent, $dndIndex]) => {
let activeLayout, activeScreen
let screens
@@ -46,31 +47,33 @@ const createScreenStore = () => {
}
// Insert DND placeholder if required
- const { dndParent, dndIndex, selectedComponentId } = $builderStore
- if (activeScreen && dndParent && dndIndex != null) {
- // Remove selected component from tree
- let selectedParent = findComponentParent(
- activeScreen.props,
- selectedComponentId
- )
- selectedParent._children = selectedParent._children?.filter(
- x => x._id !== selectedComponentId
- )
+ if (activeScreen && $dndParent && $dndIndex != null) {
+ // Remove selected component from tree if we are moving an existing
+ // component
+ const { selectedComponentId, draggingNewComponent } = $builderStore
+ if (!draggingNewComponent) {
+ let selectedParent = findComponentParent(
+ activeScreen.props,
+ selectedComponentId
+ )
+ if (selectedParent) {
+ selectedParent._children = selectedParent._children?.filter(
+ x => x._id !== selectedComponentId
+ )
+ }
+ }
- // Insert placeholder
+ // Insert placeholder component
const placeholder = {
_component: DNDPlaceholderID,
_id: DNDPlaceholderType,
static: true,
}
- let parent = findComponentById(activeScreen.props, dndParent)
+ let parent = findComponentById(activeScreen.props, $dndParent)
if (!parent._children?.length) {
parent._children = [placeholder]
} else {
- parent._children = parent._children.filter(
- x => x._id !== selectedComponentId
- )
- parent._children.splice(dndIndex, 0, placeholder)
+ parent._children.splice($dndIndex, 0, placeholder)
}
}