Removed zoom output, added focus element registration to allow centering the automations to the trigger on load.Stopped dragging when editing steps or branches and other minor fixes

This commit is contained in:
Dean 2024-10-25 12:44:21 +01:00
parent 95069ef59f
commit 2b982ab2d0
6 changed files with 145 additions and 58 deletions

View File

@ -82,14 +82,14 @@
//focus - node to center on? //focus - node to center on?
}) })
setContext("view", view) setContext("draggableView", view)
// View internal pos tracking // View internal pos tracking
const internalPos = writable({ x: 0, y: 0 }) const internalPos = writable({ x: 0, y: 0 })
setContext("viewPos", internalPos) setContext("viewPos", internalPos)
// Content pos tracking // Content pos tracking
const contentPos = writable({ x: 0, y: 0 }) const contentPos = writable({ x: 0, y: 0, scrollX: 0, scrollY: 0 })
setContext("contentPos", contentPos) setContext("contentPos", contentPos)
// Elements // Elements
@ -112,8 +112,14 @@
// When dragging the content, maintain the drag start offset // When dragging the content, maintain the drag start offset
let dragOffset let dragOffset
// Used when focusing the UI on trigger
let loaded = false
// Edge around the draggable content
let contentDragPadding = 200
// Auto scroll // Auto scroll
let scrollInterval // let scrollInterval
const onScale = async () => { const onScale = async () => {
dispatch("zoom", $view.scale) dispatch("zoom", $view.scale)
@ -167,9 +173,15 @@
...state, ...state,
x: state.x - xBump, x: state.x - xBump,
y: state.y, y: state.y,
// If scrolling *and* dragging, maintain a record of the scroll offset
...($view.dragging
? {
scrollX: state.scrollX - xBump,
}
: {}),
})) }))
} else if (e.ctrlKey || e.metaKey) { } else if (e.ctrlKey || e.metaKey) {
// Scale // Scale the content on scrolling
let updatedScale let updatedScale
if (e.deltaY < 0) { if (e.deltaY < 0) {
updatedScale = Math.min(1, currentScale + 0.05) updatedScale = Math.min(1, currentScale + 0.05)
@ -187,6 +199,12 @@
...state, ...state,
x: state.x, x: state.x,
y: state.y - yBump, y: state.y - yBump,
// If scrolling *and* dragging, maintain a record of the scroll offset
...($view.dragging
? {
scrollY: state.scrollY - yBump,
}
: {}),
})) }))
} }
} }
@ -211,22 +229,26 @@
// Needs to handle when the mouse leaves the screen // Needs to handle when the mouse leaves the screen
// Needs to know the direction of movement and accelerate/decelerate // Needs to know the direction of movement and accelerate/decelerate
if (y < (viewDims.height / 100) * 20 && $view.dragging) { if (y < (viewDims.height / 100) * 20 && $view.dragging) {
if (!scrollInterval) { // if (!scrollInterval) {
// scrollInterval = setInterval(() => { // scrollInterval = setInterval(() => {
// contentPos = { x: contentPos.x, y: contentPos.y + 35 } // contentPos.update(state => ({
// }, 100) // ...state,
} // x: contentPos.x,
} else { // y: contentPos.y + 35,
if (scrollInterval) { // }))
clearInterval(scrollInterval) // }, 100)
scrollInterval = undefined // }
} // } else {
// if (scrollInterval) {
// clearInterval(scrollInterval)
// scrollInterval = undefined
// }
} }
} }
const onViewDragEnd = () => { const onViewDragEnd = () => {
down = false down = false
dragOffset = [] dragOffset = [0, 0]
} }
const handleDragDrop = () => { const handleDragDrop = () => {
@ -256,6 +278,13 @@
dropzones: {}, dropzones: {},
droptarget: null, droptarget: null,
})) }))
// Clear the scroll offset for dragging
contentPos.update(state => ({
...state,
scrollY: 0,
scrollX: 0,
}))
} }
const onMouseMove = async e => { const onMouseMove = async e => {
@ -279,7 +308,6 @@
} }
if ($view.dragging) { if ($view.dragging) {
// TODO - Need to adjust content pos
const adjustedX = const adjustedX =
(e.clientX - viewDims.left - $view.moveStep.offsetX) / $view.scale (e.clientX - viewDims.left - $view.moveStep.offsetX) / $view.scale
const adjustedY = const adjustedY =
@ -325,23 +353,49 @@
dragOffset = [Math.abs(x - $contentPos.x), Math.abs(y - $contentPos.y)] dragOffset = [Math.abs(x - $contentPos.x), Math.abs(y - $contentPos.y)]
} }
const focusOnLoad = () => {
if ($view.focusEle && !loaded) {
const focusEleDims = $view.focusEle
const viewWidth = viewDims.width
// The amount to shift the content in order to center the trigger on load.
// The content is also padded with `contentDragPadding`
// The sidebar offset factors into the left positioning of the content here.
const targetX =
contentWrap.getBoundingClientRect().x -
focusEleDims.x +
(viewWidth / 2 - focusEleDims.width / 2)
// Update the content position state
// Shift the content up slightly to accommodate the padding
contentPos.update(state => ({
...state,
x: targetX,
y: -(contentDragPadding / 2),
}))
loaded = true
}
}
// Update dims after scaling // Update dims after scaling
$: { $: {
$view.scale $view.scale
onScale() onScale()
} }
// Focus on a registered element
$: {
$view.focusEle
focusOnLoad()
}
// Content mouse pos and scale to css variables. // Content mouse pos and scale to css variables.
// The wrap is set to match the content size // The wrap is set to match the content size
$: wrapStyles = buildWrapStyles($contentPos, $view.scale, contentDims) $: wrapStyles = buildWrapStyles($contentPos, $view.scale, contentDims)
onMount(() => { onMount(() => {
observer = new ResizeObserver(entries => { observer = new ResizeObserver(getDims)
if (!entries?.[0]) {
return
}
getDims()
})
observer.observe(viewPort) observer.observe(viewPort)
}) })
@ -358,6 +412,7 @@
aria-label="Viewport for building automations" aria-label="Viewport for building automations"
on:mouseup={onMouseUp} on:mouseup={onMouseUp}
on:mousemove={Utils.domDebounce(onMouseMove)} on:mousemove={Utils.domDebounce(onMouseMove)}
style={`--dragPadding: ${contentDragPadding}px;`}
> >
<div <div
class="draggable-view" class="draggable-view"
@ -367,6 +422,17 @@
on:mouseup={onViewDragEnd} on:mouseup={onViewDragEnd}
on:mouseleave={onViewDragEnd} on:mouseleave={onViewDragEnd}
> >
<!-- <div class="debug">
<span>
View Pos [{$internalPos.x}, {$internalPos.y}]
</span>
<span>View Dims [{viewDims.width}, {viewDims.height}]</span>
<span>Mouse Down [{down}]</span>
<span>Drag [{$view.dragging}]</span>
<span>Dragging [{$view?.moveStep?.id || "no"}]</span>
<span>Scale [{$view.scale}]</span>
<span>Content [{JSON.stringify($contentPos)}]</span>
</div> -->
<div <div
class="content-wrap" class="content-wrap"
style={wrapStyles} style={wrapStyles}
@ -423,16 +489,24 @@
width: var(--wrapW); width: var(--wrapW);
height: var(--wrapH); height: var(--wrapH);
cursor: grab; cursor: grab;
transform: translate(var(--posX), var(--posY));
} }
.content { .content {
/* transition: all 0.1s ease-out; */ transform: scale(var(--scale));
transform: translate(var(--posX), var(--posY)) scale(var(--scale));
user-select: none; user-select: none;
padding: 200px; padding: var(--dragPadding);
padding-top: 200px;
} }
.content-wrap.dragging { .content-wrap.dragging {
cursor: grabbing; cursor: grabbing;
} }
.debug {
display: flex;
align-items: center;
gap: 8px;
position: fixed;
padding: 8px;
z-index: 2;
}
</style> </style>

View File

@ -31,7 +31,7 @@
export let bindings export let bindings
export let automation export let automation
const view = getContext("view") const view = getContext("draggableView")
let drawer let drawer
let condition let condition
@ -87,7 +87,14 @@
</Drawer> </Drawer>
<div class="flow-item"> <div class="flow-item">
<div class={`block branch-node hoverable`} class:selected={false}> <!-- svelte-ignore a11y-no-static-element-interactions -->
<div
class={`block branch-node hoverable`}
class:selected={false}
on:mousedown={e => {
e.stopPropagation()
}}
>
<FlowItemHeader <FlowItemHeader
{automation} {automation}
{open} {open}

View File

@ -7,7 +7,7 @@
let dropEle let dropEle
let dzid = generate() let dzid = generate()
const view = getContext("view") const view = getContext("draggableView")
onMount(() => { onMount(() => {
// Always return up-to-date values // Always return up-to-date values

View File

@ -31,7 +31,6 @@
let blockRefs = {} let blockRefs = {}
let treeEle let treeEle
let draggable let draggable
let zoom = 100
// Memo auto - selectedAutomation // Memo auto - selectedAutomation
$: memoAutomation.set(automation) $: memoAutomation.set(automation)
@ -89,7 +88,6 @@
<ActionButton icon="Add" quiet on:click={draggable.zoomIn} /> <ActionButton icon="Add" quiet on:click={draggable.zoomIn} />
<ActionButton icon="Remove" quiet on:click={draggable.zoomOut} /> <ActionButton icon="Remove" quiet on:click={draggable.zoomOut} />
</div> </div>
<div class="zoom-display">{`${zoom}%`}</div>
</div> </div>
<Button <Button
@ -140,17 +138,7 @@
</div> </div>
<div class="root" bind:this={treeEle}> <div class="root" bind:this={treeEle}>
<DraggableCanvas <DraggableCanvas bind:this={draggable}>
bind:this={draggable}
on:zoom={e => {
const baseZoom = Math.round(e.detail * 100)
// Fit to zoom shifts the zoom to explicit values
// The interval is in 5 or 10 so rounding to the closest 5 should flatten this out
const roundedZoom = 5 * Math.floor((baseZoom + 2) / 5)
zoom = roundedZoom
}}
>
<span class="main-content" slot="content"> <span class="main-content" slot="content">
{#if Object.keys(blockRefs).length} {#if Object.keys(blockRefs).length}
{#each blocks as block, idx (block.id)} {#each blocks as block, idx (block.id)}
@ -183,12 +171,6 @@
</Modal> </Modal>
<style> <style>
.zoom {
display: flex;
align-items: center;
gap: var(--spacing-s);
}
.toggle-active :global(.spectrum-Switch) { .toggle-active :global(.spectrum-Switch) {
margin: 0px; margin: 0px;
} }

View File

@ -35,22 +35,23 @@
export let bindings export let bindings
export let draggable = true export let draggable = true
const view = getContext("view") const view = getContext("draggableView")
const pos = getContext("viewPos") const pos = getContext("viewPos")
const contentPos = getContext("contentPos")
let webhookModal let webhookModal
let open = true let open = true
let showLooping = false let showLooping = false
let role let role
let blockEle let blockEle
let positionStyles
$: pathSteps = loadSteps(blockRef)
const loadSteps = blockRef => { const loadSteps = blockRef => {
return blockRef return blockRef
? automationStore.actions.getPathSteps(blockRef.pathTo, automation) ? automationStore.actions.getPathSteps(blockRef.pathTo, automation)
: [] : []
} }
$: pathSteps = loadSteps(blockRef)
$: collectBlockExists = pathSteps.some( $: collectBlockExists = pathSteps.some(
step => step.stepId === ActionStepID.COLLECT step => step.stepId === ActionStepID.COLLECT
@ -76,18 +77,23 @@
$: blockDims = blockEle?.getBoundingClientRect() $: blockDims = blockEle?.getBoundingClientRect()
$: placeholderDims = buildPlaceholderStyles(blockDims) $: placeholderDims = buildPlaceholderStyles(blockDims)
let positionStyles
// Move the selected item // Move the selected item
$: move(blockEle, $view?.dragSpot, selected) // Listen for scrolling in the content. As its scrolled this will be updated
$: move(
blockEle,
$view?.dragSpot,
selected,
$contentPos?.scrollX,
$contentPos?.scrollY
)
const move = (block, dragPos, selected) => { const move = (block, dragPos, selected, scrollX, scrollY) => {
if ((!block && !selected) || !dragPos) { if ((!block && !selected) || !dragPos) {
return return
} }
positionStyles = ` positionStyles = `
--blockPosX: ${Math.round(dragPos.x)}px; --blockPosX: ${Math.round(dragPos.x - scrollX / $view.scale)}px;
--blockPosY: ${Math.round(dragPos.y)}px; --blockPosY: ${Math.round(dragPos.y - scrollY / $view.scale)}px;
` `
} }
@ -184,6 +190,9 @@
class="block-content" class="block-content"
class:dragging={$view.dragging && selected} class:dragging={$view.dragging && selected}
style={positionStyles} style={positionStyles}
on:mousedown={e => {
e.stopPropagation()
}}
> >
{#if draggable} {#if draggable}
<div <div

View File

@ -7,6 +7,7 @@
import { environment } from "stores/portal" import { environment } from "stores/portal"
import { cloneDeep } from "lodash" import { cloneDeep } from "lodash"
import { memo } from "@budibase/frontend-core" import { memo } from "@budibase/frontend-core"
import { getContext, onMount } from "svelte"
export let step = {} export let step = {}
export let stepIdx export let stepIdx
@ -15,6 +16,9 @@
export let isLast = false export let isLast = false
const memoEnvVariables = memo($environment.variables) const memoEnvVariables = memo($environment.variables)
const view = getContext("draggableView")
let stepEle
$: memoEnvVariables.set($environment.variables) $: memoEnvVariables.set($environment.variables)
$: blockRef = blocks?.[step.id] $: blockRef = blocks?.[step.id]
@ -34,6 +38,17 @@
// Combine all bindings for the step // Combine all bindings for the step
$: bindings = [...availableBindings, ...environmentBindings] $: bindings = [...availableBindings, ...environmentBindings]
onMount(() => {
// Register the trigger as the focus element for the automation
// Onload, the canvas will use the dimensions to center the step
if (stepEle && step.type === "TRIGGER" && !$view.focusEle) {
view.update(state => ({
...state,
focusEle: stepEle.getBoundingClientRect(),
}))
}
})
</script> </script>
{#if isBranch} {#if isBranch}
@ -105,7 +120,7 @@
{/each} {/each}
</div> </div>
{:else} {:else}
<div class="block"> <div class="block" bind:this={stepEle}>
<FlowItem <FlowItem
block={step} block={step}
idx={stepIdx} idx={stepIdx}