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?
})
setContext("view", view)
setContext("draggableView", view)
// View internal pos tracking
const internalPos = writable({ x: 0, y: 0 })
setContext("viewPos", internalPos)
// Content pos tracking
const contentPos = writable({ x: 0, y: 0 })
const contentPos = writable({ x: 0, y: 0, scrollX: 0, scrollY: 0 })
setContext("contentPos", contentPos)
// Elements
@ -112,8 +112,14 @@
// When dragging the content, maintain the drag start offset
let dragOffset
// Used when focusing the UI on trigger
let loaded = false
// Edge around the draggable content
let contentDragPadding = 200
// Auto scroll
let scrollInterval
// let scrollInterval
const onScale = async () => {
dispatch("zoom", $view.scale)
@ -167,9 +173,15 @@
...state,
x: state.x - xBump,
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) {
// Scale
// Scale the content on scrolling
let updatedScale
if (e.deltaY < 0) {
updatedScale = Math.min(1, currentScale + 0.05)
@ -187,6 +199,12 @@
...state,
x: state.x,
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 know the direction of movement and accelerate/decelerate
if (y < (viewDims.height / 100) * 20 && $view.dragging) {
if (!scrollInterval) {
// if (!scrollInterval) {
// scrollInterval = setInterval(() => {
// contentPos = { x: contentPos.x, y: contentPos.y + 35 }
// contentPos.update(state => ({
// ...state,
// x: contentPos.x,
// y: contentPos.y + 35,
// }))
// }, 100)
}
} else {
if (scrollInterval) {
clearInterval(scrollInterval)
scrollInterval = undefined
}
// }
// } else {
// if (scrollInterval) {
// clearInterval(scrollInterval)
// scrollInterval = undefined
// }
}
}
const onViewDragEnd = () => {
down = false
dragOffset = []
dragOffset = [0, 0]
}
const handleDragDrop = () => {
@ -256,6 +278,13 @@
dropzones: {},
droptarget: null,
}))
// Clear the scroll offset for dragging
contentPos.update(state => ({
...state,
scrollY: 0,
scrollX: 0,
}))
}
const onMouseMove = async e => {
@ -279,7 +308,6 @@
}
if ($view.dragging) {
// TODO - Need to adjust content pos
const adjustedX =
(e.clientX - viewDims.left - $view.moveStep.offsetX) / $view.scale
const adjustedY =
@ -325,23 +353,49 @@
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
$: {
$view.scale
onScale()
}
// Focus on a registered element
$: {
$view.focusEle
focusOnLoad()
}
// Content mouse pos and scale to css variables.
// The wrap is set to match the content size
$: wrapStyles = buildWrapStyles($contentPos, $view.scale, contentDims)
onMount(() => {
observer = new ResizeObserver(entries => {
if (!entries?.[0]) {
return
}
getDims()
})
observer = new ResizeObserver(getDims)
observer.observe(viewPort)
})
@ -358,6 +412,7 @@
aria-label="Viewport for building automations"
on:mouseup={onMouseUp}
on:mousemove={Utils.domDebounce(onMouseMove)}
style={`--dragPadding: ${contentDragPadding}px;`}
>
<div
class="draggable-view"
@ -367,6 +422,17 @@
on:mouseup={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
class="content-wrap"
style={wrapStyles}
@ -423,16 +489,24 @@
width: var(--wrapW);
height: var(--wrapH);
cursor: grab;
transform: translate(var(--posX), var(--posY));
}
.content {
/* transition: all 0.1s ease-out; */
transform: translate(var(--posX), var(--posY)) scale(var(--scale));
transform: scale(var(--scale));
user-select: none;
padding: 200px;
padding-top: 200px;
padding: var(--dragPadding);
}
.content-wrap.dragging {
cursor: grabbing;
}
.debug {
display: flex;
align-items: center;
gap: 8px;
position: fixed;
padding: 8px;
z-index: 2;
}
</style>

View File

@ -31,7 +31,7 @@
export let bindings
export let automation
const view = getContext("view")
const view = getContext("draggableView")
let drawer
let condition
@ -87,7 +87,14 @@
</Drawer>
<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
{automation}
{open}

View File

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

View File

@ -31,7 +31,6 @@
let blockRefs = {}
let treeEle
let draggable
let zoom = 100
// Memo auto - selectedAutomation
$: memoAutomation.set(automation)
@ -89,7 +88,6 @@
<ActionButton icon="Add" quiet on:click={draggable.zoomIn} />
<ActionButton icon="Remove" quiet on:click={draggable.zoomOut} />
</div>
<div class="zoom-display">{`${zoom}%`}</div>
</div>
<Button
@ -140,17 +138,7 @@
</div>
<div class="root" bind:this={treeEle}>
<DraggableCanvas
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
}}
>
<DraggableCanvas bind:this={draggable}>
<span class="main-content" slot="content">
{#if Object.keys(blockRefs).length}
{#each blocks as block, idx (block.id)}
@ -183,12 +171,6 @@
</Modal>
<style>
.zoom {
display: flex;
align-items: center;
gap: var(--spacing-s);
}
.toggle-active :global(.spectrum-Switch) {
margin: 0px;
}

View File

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

View File

@ -7,6 +7,7 @@
import { environment } from "stores/portal"
import { cloneDeep } from "lodash"
import { memo } from "@budibase/frontend-core"
import { getContext, onMount } from "svelte"
export let step = {}
export let stepIdx
@ -15,6 +16,9 @@
export let isLast = false
const memoEnvVariables = memo($environment.variables)
const view = getContext("draggableView")
let stepEle
$: memoEnvVariables.set($environment.variables)
$: blockRef = blocks?.[step.id]
@ -34,6 +38,17 @@
// Combine all bindings for the step
$: 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>
{#if isBranch}
@ -105,7 +120,7 @@
{/each}
</div>
{:else}
<div class="block">
<div class="block" bind:this={stepEle}>
<FlowItem
block={step}
idx={stepIdx}