Allow grid DND without selecting components

This commit is contained in:
Andrew Kingston 2022-10-18 18:29:21 +01:00
parent 0e63a403bc
commit 465d10a8be
11 changed files with 88 additions and 55 deletions

View File

@ -542,12 +542,11 @@ export const getFrontendStore = () => {
},
patch: async (patchFn, componentId, screenId) => {
// Use selected component by default
if (!componentId && !screenId) {
if (!componentId || !screenId) {
const state = get(store)
componentId = state.selectedComponentId
screenId = state.selectedScreenId
componentId = componentId || state.selectedComponentId
screenId = screenId || state.selectedScreenId
}
// Invalid if only a screen or component ID provided
if (!componentId || !screenId || !patchFn) {
return
}
@ -902,13 +901,14 @@ export const getFrontendStore = () => {
}
})
},
updateStyles: async styles => {
await store.actions.components.patch(component => {
updateStyles: async (styles, id) => {
const patchFn = component => {
component._styles.normal = {
...component._styles.normal,
...styles,
}
})
}
await store.actions.components.patch(patchFn, id)
},
updateCustomStyle: async style => {
await store.actions.components.patch(component => {

View File

@ -156,7 +156,7 @@
} else if (type === "update-prop") {
await store.actions.components.updateSetting(data.prop, data.value)
} else if (type === "update-styles") {
await store.actions.components.updateStyles(data.styles)
await store.actions.components.updateStyles(data.styles, data.id)
} else if (type === "delete-component" && data.id) {
// Legacy type, can be deleted in future
confirmDeleteComponent(data.id)

View File

@ -22,6 +22,7 @@
componentStore,
appStore,
dndComponentPath,
dndIsDragging,
} from "stores"
import { Helpers } from "@budibase/bbui"
import { getActiveConditions, reduceConditionActions } from "utils/conditions"
@ -162,7 +163,7 @@
// nested layers. Only reset this when dragging stops.
let pad = false
$: pad = pad || (interactive && hasChildren && inDndPath)
$: $builderStore.dragging, (pad = false)
$: $dndIsDragging, (pad = false)
// Update component context
$: store.set({

View File

@ -2,7 +2,13 @@
import { onMount, onDestroy } from "svelte"
import { get } from "svelte/store"
import IndicatorSet from "./IndicatorSet.svelte"
import { builderStore, screenStore, dndStore, dndParent } from "stores"
import {
builderStore,
screenStore,
dndStore,
dndParent,
dndIsDragging,
} from "stores"
import DNDPlaceholderOverlay from "./DNDPlaceholderOverlay.svelte"
import { Utils } from "@budibase/frontend-core"
import { findComponentById } from "utils/components.js"
@ -49,7 +55,6 @@
// Reset state
dndStore.actions.reset()
builderStore.actions.setDragging(false)
}
// Callback when initially starting a drag on a draggable component
@ -85,7 +90,6 @@
index,
})
builderStore.actions.selectComponent(id)
builderStore.actions.setDragging(true)
// Set initial drop info to show placeholder exactly where the dragged
// component is.
@ -329,6 +333,6 @@
prefix="Inside"
/>
{#if $builderStore.dragging}
{#if $dndIsDragging}
<DNDPlaceholderOverlay />
{/if}

View File

@ -1,7 +1,8 @@
<script>
import { onMount, onDestroy } from "svelte"
import { builderStore, componentStore } from "stores"
import { builderStore, screenStore } from "stores"
import { Utils } from "@budibase/frontend-core"
import { findComponentById } from "utils/components.js"
let dragInfo
let gridStyles
@ -32,7 +33,7 @@
const stopDragging = () => {
// Save changes
if (gridStyles) {
builderStore.actions.updateStyles(gridStyles)
builderStore.actions.updateStyles(gridStyles, dragInfo.id)
}
// Reset listener
@ -43,7 +44,6 @@
// Reset state
dragInfo = null
gridStyles = null
builderStore.actions.setDragging(false)
}
// Callback when initially starting a drag on a draggable component
@ -84,8 +84,6 @@
mode,
side,
}
// builderStore.actions.selectComponent(dragInfo.id)
// builderStore.actions.setDragging(true)
// Add event handler to clear all drag state when dragging ends
dragInfo.domTarget.addEventListener("dragend", stopDragging)
@ -98,7 +96,13 @@
return
}
const compDef = $componentStore.selectedComponent
const compDef = findComponentById(
$screenStore.activeScreen.props,
dragInfo.id
)
if (!compDef) {
return
}
const domGrid = getDOMNode(dragInfo.gridId)
if (domGrid) {
const getStyle = x => parseInt(compDef._styles.normal?.[x] || "0")

View File

@ -1,14 +1,31 @@
<script>
import { onMount, onDestroy } from "svelte"
import IndicatorSet from "./IndicatorSet.svelte"
import { builderStore } from "stores"
import { builderStore, dndIsDragging } from "stores"
let componentId
$: zIndex = componentId === $builderStore.selectedComponentId ? 900 : 920
const onMouseOver = e => {
const element = e.target.closest(".interactive.component")
const newId = element?.dataset?.id
// Ignore if dragging
if (e.buttons > 0) {
console.log("ignore")
return
}
let newId
if (e.target.classList.contains("anchor")) {
// Handle resize anchors
newId = e.target.dataset.id
console.log("anchor", newId)
} else {
// Handle normal components
const element = e.target.closest(".interactive.component")
newId = element?.dataset?.id
console.log("normal", newId)
}
if (newId !== componentId) {
componentId = newId
}
@ -30,8 +47,9 @@
</script>
<IndicatorSet
componentId={$builderStore.dragging ? null : componentId}
componentId={$dndIsDragging ? null : componentId}
color="var(--spectrum-global-color-static-blue-200)"
transition
{zIndex}
allowResizeAnchors
/>

View File

@ -60,7 +60,9 @@
class="anchor {side}"
data-side={side}
data-id={componentId}
/>
>
<div class="anchor-inner" />
</div>
{/each}
{/if}
</div>
@ -131,52 +133,61 @@
/* Anchor */
.anchor {
--size: 24px;
position: absolute;
width: 10px;
height: 10px;
border-radius: 50%;
background-color: var(--color);
width: var(--size);
height: var(--size);
pointer-events: all;
display: grid;
place-items: center;
border-radius: 50%;
}
.anchor-inner {
width: 12px;
height: 12px;
background: white;
border: 2px solid var(--color);
pointer-events: none;
}
.anchor.right {
right: -6px;
top: calc(50% - 5px);
right: calc(var(--size) / -2 - 1px);
top: calc(50% - var(--size) / 2);
cursor: e-resize;
}
.anchor.left {
left: -6px;
top: calc(50% - 5px);
left: calc(var(--size) / -2 - 1px);
top: calc(50% - var(--size) / 2);
cursor: w-resize;
}
.anchor.bottom {
left: calc(50% - 5px);
bottom: -6px;
left: calc(50% - var(--size) / 2 + 1px);
bottom: calc(var(--size) / -2 - 1px);
cursor: s-resize;
}
.anchor.top {
left: calc(50% - 5px);
top: -6px;
left: calc(50% - var(--size) / 2 + 1px);
top: calc(var(--size) / -2 - 1px);
cursor: n-resize;
}
.anchor.bottom-right {
right: -6px;
bottom: -6px;
right: calc(var(--size) / -2 - 1px);
bottom: calc(var(--size) / -2 - 1px);
cursor: se-resize;
}
.anchor.bottom-left {
left: -6px;
bottom: -6px;
left: calc(var(--size) / -2 - 1px);
bottom: calc(var(--size) / -2 - 1px);
cursor: sw-resize;
}
.anchor.top-right {
right: -6px;
top: -6px;
right: calc(var(--size) / -2 - 1px);
top: calc(var(--size) / -2 - 1px);
cursor: ne-resize;
}
.anchor.top-left {
left: -6px;
top: -6px;
left: calc(var(--size) / -2 - 1px);
top: calc(var(--size) / -2 - 1px);
cursor: nw-resize;
}
</style>

View File

@ -3,7 +3,7 @@
import SettingsButton from "./SettingsButton.svelte"
import SettingsColorPicker from "./SettingsColorPicker.svelte"
import SettingsPicker from "./SettingsPicker.svelte"
import { builderStore, componentStore } from "stores"
import { builderStore, componentStore, dndIsDragging } from "stores"
import { domDebounce } from "utils/domDebounce"
const verticalOffset = 36
@ -16,7 +16,7 @@
let measured = false
$: definition = $componentStore.selectedComponentDefinition
$: showBar = definition?.showSettingsBar && !$builderStore.dragging
$: showBar = definition?.showSettingsBar && !$dndIsDragging
$: settings = getBarSettings(definition)
const getBarSettings = definition => {

View File

@ -19,7 +19,6 @@ const createBuilderStore = () => {
navigation: null,
hiddenComponentIds: [],
usedPlugins: null,
dragging: false,
// Legacy - allow the builder to specify a layout
layout: null,
@ -41,8 +40,8 @@ const createBuilderStore = () => {
updateProp: (prop, value) => {
dispatchEvent("update-prop", { prop, value })
},
updateStyles: styles => {
dispatchEvent("update-styles", { styles })
updateStyles: (styles, id) => {
dispatchEvent("update-styles", { styles, id })
},
keyDown: (key, ctrlKey) => {
dispatchEvent("key-down", { key, ctrlKey })
@ -112,12 +111,6 @@ const createBuilderStore = () => {
// Notify the builder so we can reload component definitions
dispatchEvent("reload-plugin")
},
setDragging: dragging => {
store.update(state => {
state.dragging = dragging
return state
})
},
}
return {
...store,

View File

@ -87,3 +87,4 @@ export const dndIsNewComponent = derived(
dndStore,
$dndStore => $dndStore.source?.newComponentType != null
)
export const dndIsDragging = derived(dndStore, $dndStore => !!$dndStore.source)

View File

@ -21,6 +21,7 @@ export {
dndParent,
dndBounds,
dndIsNewComponent,
dndIsDragging,
} from "./dnd"
// Context stores are layered and duplicated, so it is not a singleton