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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,14 +1,31 @@
<script> <script>
import { onMount, onDestroy } from "svelte" import { onMount, onDestroy } from "svelte"
import IndicatorSet from "./IndicatorSet.svelte" import IndicatorSet from "./IndicatorSet.svelte"
import { builderStore } from "stores" import { builderStore, dndIsDragging } from "stores"
let componentId let componentId
$: zIndex = componentId === $builderStore.selectedComponentId ? 900 : 920 $: zIndex = componentId === $builderStore.selectedComponentId ? 900 : 920
const onMouseOver = e => { const onMouseOver = e => {
// 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") const element = e.target.closest(".interactive.component")
const newId = element?.dataset?.id newId = element?.dataset?.id
console.log("normal", newId)
}
if (newId !== componentId) { if (newId !== componentId) {
componentId = newId componentId = newId
} }
@ -30,8 +47,9 @@
</script> </script>
<IndicatorSet <IndicatorSet
componentId={$builderStore.dragging ? null : componentId} componentId={$dndIsDragging ? null : componentId}
color="var(--spectrum-global-color-static-blue-200)" color="var(--spectrum-global-color-static-blue-200)"
transition transition
{zIndex} {zIndex}
allowResizeAnchors
/> />

View File

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

View File

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

View File

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

View File

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

View File

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