Merge commit

This commit is contained in:
Dean 2024-10-31 16:38:26 +00:00
parent 4f93043b03
commit 9674b7aaa0
2 changed files with 266 additions and 80 deletions

View File

@ -9,9 +9,53 @@
} from "svelte"
import { Utils } from "@budibase/frontend-core"
import { selectedAutomation, automationStore } from "stores/builder"
import { memo } from "@budibase/frontend-core"
const getContentTransformOrigin = () => {
const midPoint = {
midX: viewPort.getBoundingClientRect().width / 2,
midY: viewPort.getBoundingClientRect().height / 2,
}
return [
Math.abs(midPoint.midX - $contentPos.x),
Math.abs(midPoint.midY - $contentPos.y),
]
}
// TODO - Remove Debugging
window.markCenterPoint = async () => {
zoomOrigin = [...getContentTransformOrigin()]
const scale = 1 //0.9
const newWidth = contentDims.w * scale
const deltaWidth = contentDims.w - newWidth
const newHeight = contentDims.h * scale
const deltaHeight = contentDims.h - newHeight
console.log({
w: {
widf: contentDims.w,
newWidth,
deltaWidth: parseInt(deltaWidth),
},
h: {
hif: contentDims.h,
newHeight,
deltaHeight: parseInt(deltaHeight),
},
})
}
export function toFocus() {
viewToFocusEle()
}
export function zoomIn() {
const scale = Number(Math.min($view.scale + 0.1, 1.5).toFixed(2))
const scale = parseFloat(Math.min($view.scale + 0.1, 1.5).toFixed(2))
// zoomOrigin = [...getContentTransformOrigin()]
view.update(state => ({
...state,
scale,
@ -19,7 +63,9 @@
}
export function zoomOut() {
const scale = Number(Math.max($view.scale - 0.1, 0).toFixed(2))
const scale = parseFloat(Math.max($view.scale - 0.1, 0.1).toFixed(2))
// zoomOrigin = [...getContentTransformOrigin()]
view.update(state => ({
...state,
scale,
@ -48,18 +94,23 @@
const { width: wViewPort, height: hViewPort } =
viewPort.getBoundingClientRect()
const scaleTarget = Math.min(
wViewPort / contentDims.original.w,
hViewPort / contentDims.original.h
const scaleTarget = parseFloat(
Math.min(
wViewPort / contentDims.original.w,
hViewPort / contentDims.original.h
).toFixed(2)
)
// Smallest ratio determines which dimension needs squeezed
view.update(state => ({
...state,
scale: scaleTarget,
}))
// Skip behaviour if the scale target scale is the current scale
if ($view.scale !== scaleTarget) {
// Smallest ratio determines which dimension needs squeezed
view.update(state => ({
...state,
scale: parseFloat(scaleTarget.toFixed(2)),
}))
await tick()
await tick()
}
const adjustedY = (hViewPort - contentDims.original.h) / 2
@ -67,6 +118,8 @@
...state,
x: 0,
y: parseInt(0 + adjustedY),
scrollX: 0,
scrollY: 0,
}))
}
@ -79,7 +132,6 @@
dragSpot: null,
scale: 1,
dropzones: {},
//focus - node to center on?
})
setContext("draggableView", view)
@ -89,9 +141,18 @@
setContext("viewPos", internalPos)
// Content pos tracking
const contentPos = writable({ x: 0, y: 0, scrollX: 0, scrollY: 0 })
const contentPos = writable({
x: 0,
y: 0,
scrollX: 0,
scrollY: 0,
})
setContext("contentPos", contentPos)
$: if ($view || contentDims) {
window.testValues = { ...$view, contentDims }
}
// Elements
let mainContent
let viewPort
@ -101,7 +162,11 @@
let down = false
// Monitor the size of the viewPort
let observer
let viewObserver
// Monitor the size of the content
// Always reconfigure when changes occur
let contentObserver
// Size of the core display content
let contentDims = {}
@ -110,16 +175,21 @@
let viewDims = {}
// When dragging the content, maintain the drag start offset
let dragOffset
// Used when focusing the UI on trigger
let loaded = false
let dragOffset = []
// Edge around the draggable content
let contentDragPadding = 200
// Auto scroll
// let scrollInterval
let scrollInterval
let zoomOrigin = []
// Focus element details. Used to move the viewport
let focusElement = memo()
// Memo Focus
$: focusElement.set($view.focusEle)
const onScale = async () => {
dispatch("zoom", $view.scale)
@ -127,8 +197,6 @@
}
const getDims = async () => {
if (!mainContent) return
if (!contentDims.original) {
contentDims.original = {
w: parseInt(mainContent.getBoundingClientRect().width),
@ -153,10 +221,17 @@
return { x: Math.max(x, 0), y: Math.max(y, 0) }
}
const buildWrapStyles = (pos, scale, dims) => {
const buildWrapStyles = (pos, scale, dims, contentCursor) => {
const { x, y } = pos
const { w, h } = dims
return `--posX: ${x}px; --posY: ${y}px; --scale: ${scale}; --wrapH: ${h}px; --wrapW: ${w}px`
const [cursorContentX, cursorContentY] = contentCursor
return `
--posX: ${x}px; --posY: ${y}px;
--scale: ${scale};
--wrapH: ${h}px; --wrapW: ${w}px;
--ccX: ${cursorContentX}px;
--ccY: ${cursorContentY}px;
`
}
const onViewScroll = e => {
@ -186,12 +261,12 @@
if (e.deltaY < 0) {
updatedScale = Math.min(1, currentScale + 0.05)
} else if (e.deltaY > 0) {
updatedScale = Math.max(0, currentScale - 0.05)
updatedScale = Math.max(0.1, currentScale - 0.05)
}
view.update(state => ({
...state,
scale: Number(updatedScale.toFixed(2)),
scale: parseFloat(updatedScale.toFixed(2)),
}))
} else {
yBump = scrollIncrement * (e.deltaY < 0 ? -1 : 1)
@ -226,23 +301,58 @@
}))
}
// 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) {
// scrollInterval = setInterval(() => {
// contentPos.update(state => ({
// ...state,
// x: contentPos.x,
// y: contentPos.y + 35,
// }))
// }, 100)
// }
// } else {
// if (scrollInterval) {
// clearInterval(scrollInterval)
// scrollInterval = undefined
// }
const clearScrollInterval = () => {
if (scrollInterval) {
clearInterval(scrollInterval)
scrollInterval = undefined
}
}
if ($view.dragging) {
// Need to know the internal offset as well.
const scrollZones = {
top: y < viewDims.height * 0.05,
bottom:
y > viewDims.height - ($view.moveStep.h - $view.moveStep.mouse.y),
left: x < viewDims.width * 0.05,
right: x > viewDims.width - $view.moveStep.w,
}
// Determine which zones are currently in play
const dragOutEntries = Object.entries(scrollZones).filter(e => e[1])
if (dragOutEntries.length) {
const dragOut = Object.fromEntries(dragOutEntries)
if (!scrollInterval) {
const autoScroll = () => {
// Some internal tracking for implying direction
// const lastY = $contentPos.y
// const lastX = $contentPos.x
const bump = 30
// Depending on the zone, you want to move the content
// in the opposite direction
const xInterval = dragOut.right ? -bump : dragOut.left ? bump : 0
const yInterval = dragOut.bottom ? -bump : dragOut.top ? bump : 0
return () => {
contentPos.update(state => ({
...state,
x: (state.x || 0) + xInterval,
y: (state.y || 0) + yInterval,
scrollX: state.scrollX + xInterval,
scrollY: state.scrollY + yInterval,
}))
}
}
scrollInterval = setInterval(autoScroll(), 30)
}
} else {
clearScrollInterval()
}
} else {
clearScrollInterval()
}
}
@ -265,6 +375,35 @@
)
}
// Reset state on mouse up
const globalMouseUp = e => {
down = false
if ($view.dragging) {
dragOffset = [0, 0]
view.update(state => ({
...state,
dragging: false,
moveStep: null,
dragSpot: null,
dropzones: {},
droptarget: null,
}))
if (scrollInterval) {
clearInterval(scrollInterval)
scrollInterval = undefined
}
// Clear the scroll offset for dragging
contentPos.update(state => ({
...state,
scrollY: 0,
scrollX: 0,
}))
}
}
const onMouseUp = () => {
if ($view.droptarget) {
handleDragDrop()
@ -353,9 +492,8 @@
dragOffset = [Math.abs(x - $contentPos.x), Math.abs(y - $contentPos.y)]
}
const focusOnLoad = () => {
if ($view.focusEle && !loaded) {
const focusEleDims = $view.focusEle
const viewToFocusEle = () => {
if ($focusElement) {
const viewWidth = viewDims.width
// The amount to shift the content in order to center the trigger on load.
@ -363,8 +501,8 @@
// The sidebar offset factors into the left positioning of the content here.
const targetX =
contentWrap.getBoundingClientRect().x -
focusEleDims.x +
(viewWidth / 2 - focusEleDims.width / 2)
$focusElement.x +
(viewWidth / 2 - $focusElement.width / 2)
// Update the content position state
// Shift the content up slightly to accommodate the padding
@ -373,34 +511,45 @@
x: targetX,
y: -(contentDragPadding / 2),
}))
loaded = true
}
}
// Update dims after scaling
$: {
$view.scale
$: viewScale = $view.scale
$: if (viewScale && mainContent) {
onScale()
}
// Focus on a registered element
$: {
$view.focusEle
focusOnLoad()
$focusElement
viewToFocusEle()
}
// Content mouse pos and scale to css variables.
// The wrap is set to match the content size
$: wrapStyles = buildWrapStyles($contentPos, $view.scale, contentDims)
$: wrapStyles = buildWrapStyles(
$contentPos,
$view.scale,
contentDims,
zoomOrigin
)
onMount(() => {
observer = new ResizeObserver(getDims)
observer.observe(viewPort)
viewObserver = new ResizeObserver(getDims)
viewObserver.observe(viewPort)
contentObserver = new ResizeObserver(getDims)
contentObserver.observe(mainContent)
// Global mouse observer
document.addEventListener("mouseup", globalMouseUp)
})
onDestroy(() => {
observer.disconnect()
viewObserver.disconnect()
contentObserver.disconnect()
document.removeEventListener("mouseup", globalMouseUp)
})
</script>
@ -414,15 +563,17 @@
on:mousemove={Utils.domDebounce(onMouseMove)}
style={`--dragPadding: ${contentDragPadding}px;`}
>
<!-- svelte-ignore a11y-mouse-events-have-key-events -->
<div
class="draggable-view"
bind:this={viewPort}
on:wheel={Utils.domDebounce(onViewScroll)}
on:mousemove={Utils.domDebounce(onViewMouseMove)}
on:mouseup={onViewDragEnd}
on:mouseleave={onViewDragEnd}
>
<!-- <div class="debug">
<!--delete me-->
<div class="reticle" />
<div class="debug">
<span>
View Pos [{$internalPos.x}, {$internalPos.y}]
</span>
@ -432,13 +583,16 @@
<span>Dragging [{$view?.moveStep?.id || "no"}]</span>
<span>Scale [{$view.scale}]</span>
<span>Content [{JSON.stringify($contentPos)}]</span>
</div> -->
<span>Content Cursor [{JSON.stringify(dragOffset)}]</span>
</div>
<div
class="content-wrap"
style={wrapStyles}
bind:this={contentWrap}
class:dragging={down}
>
<!--delete me-->
<div class="reticle-content" />
<div
class="content"
bind:this={mainContent}
@ -453,16 +607,6 @@
down = false
}
}}
on:mouseleave={() => {
down = false
view.update(state => ({
...state,
dragging: false,
moveStep: null,
dragSpot: null,
dropzones: {},
}))
}}
>
<slot name="content" />
</div>
@ -481,6 +625,38 @@
overflow: hidden;
position: relative;
}
.reticle {
display: none;
z-index: 10000;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 2px;
height: 2px;
background-color: red;
border-radius: 50%;
}
.reticle-content {
z-index: 10000;
position: absolute;
top: var(--ccY);
left: var(--ccX);
width: 2px;
height: 2px;
background-color: greenyellow;
border-radius: 50%;
}
.content {
transform-origin: var(--ccX) var(--ccY);
transform: scale(var(--scale));
user-select: none;
padding: var(--dragPadding);
}
.content-wrap {
min-width: 100%;
position: absolute;
@ -491,22 +667,18 @@
cursor: grab;
transform: translate(var(--posX), var(--posY));
}
.content {
transform: scale(var(--scale));
user-select: none;
padding: var(--dragPadding);
}
.content-wrap.dragging {
cursor: grabbing;
}
/* .debug {
.debug {
display: flex;
align-items: center;
gap: 8px;
position: fixed;
padding: 8px;
padding: var(--spacing-l);
z-index: 2;
} */
pointer-events: none;
}
</style>

View File

@ -51,8 +51,13 @@
if (!blockEle) {
return
}
const { width, height } = blockEle.getBoundingClientRect()
blockDims = { width: width / $view.scale, height: height / $view.scale }
const { width, height, top, left } = blockEle?.getBoundingClientRect()
blockDims = {
width: width / $view.scale,
height: height / $view.scale,
top,
left,
}
}
const loadSteps = blockRef => {
@ -174,12 +179,21 @@
e.stopPropagation()
updateBlockDims()
const { clientX, clientY } = e
view.update(state => ({
...state,
moveStep: {
id: block.id,
offsetX: $pos.x,
offsetY: $pos.y,
w: blockDims.width,
h: blockDims.height,
mouse: {
x: Math.max(Math.round(clientX - blockDims.left), 0),
y: Math.max(Math.round(clientY - blockDims.top), 0),
},
},
}))
}