Enable left/right side targetting for DND
This commit is contained in:
parent
7663bdb534
commit
ee2e2799d9
|
@ -1,3 +1,12 @@
|
|||
<script context="module">
|
||||
export const Sides = {
|
||||
Top: "Top",
|
||||
Right: "Right",
|
||||
Bottom: "Bottom",
|
||||
Left: "Left",
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
import { onMount } from "svelte"
|
||||
import { get } from "svelte/store"
|
||||
|
@ -8,6 +17,22 @@
|
|||
let dragInfo
|
||||
let dropInfo
|
||||
|
||||
const getEdges = (bounds, mousePoint) => {
|
||||
const { width, height, top, left } = bounds
|
||||
return {
|
||||
[Sides.Top]: [mousePoint[0], top],
|
||||
[Sides.Right]: [left + width, mousePoint[1]],
|
||||
[Sides.Bottom]: [mousePoint[0], top + height],
|
||||
[Sides.Left]: [left, mousePoint[1]],
|
||||
}
|
||||
}
|
||||
|
||||
const calculatePointDelta = (point1, point2) => {
|
||||
const deltaX = Math.abs(point1[0] - point2[0])
|
||||
const deltaY = Math.abs(point1[1] - point2[1])
|
||||
return Math.sqrt(deltaX * deltaX + deltaY * deltaY)
|
||||
}
|
||||
|
||||
const getDOMNodeForComponent = component => {
|
||||
const parent = component.closest(".component")
|
||||
const children = Array.from(parent.childNodes)
|
||||
|
@ -61,23 +86,57 @@
|
|||
|
||||
e.preventDefault()
|
||||
const { droppableInside, bounds } = dropInfo
|
||||
const { top, height } = bounds
|
||||
const { top, left, height, width } = bounds
|
||||
const mouseY = e.clientY
|
||||
const mouseX = e.clientX
|
||||
const snapFactor = droppableInside ? 0.33 : 0.5
|
||||
const snapLimit = Math.min(40, height * snapFactor)
|
||||
const edgeLimits = [
|
||||
Math.round(top + snapLimit),
|
||||
Math.round(top + height - snapLimit),
|
||||
]
|
||||
const snapLimitV = Math.min(40, height * snapFactor)
|
||||
const snapLimitH = Math.min(40, width * snapFactor)
|
||||
|
||||
if (mouseY <= edgeLimits[0]) {
|
||||
// Determine all sies we are within snap range of
|
||||
let sides = []
|
||||
if (mouseY <= top + snapLimitV) {
|
||||
sides.push(Sides.Top)
|
||||
} else if (mouseY >= top + height - snapLimitV) {
|
||||
sides.push(Sides.Bottom)
|
||||
}
|
||||
if (mouseX < left + snapLimitH) {
|
||||
sides.push(Sides.Left)
|
||||
} else if (mouseX > left + width - snapLimitH) {
|
||||
sides.push(Sides.Right)
|
||||
}
|
||||
|
||||
// When no edges match, drop inside if possible
|
||||
if (!sides.length) {
|
||||
dropInfo.mode = droppableInside ? "inside" : null
|
||||
dropInfo.side = null
|
||||
return
|
||||
}
|
||||
|
||||
// When one edge matches, use that edge
|
||||
if (sides.length === 1) {
|
||||
dropInfo.side = sides[0]
|
||||
if ([Sides.Top, Sides.Left].includes(sides[0])) {
|
||||
dropInfo.mode = "above"
|
||||
} else if (mouseY >= edgeLimits[1]) {
|
||||
dropInfo.mode = "below"
|
||||
} else if (droppableInside) {
|
||||
dropInfo.mode = "inside"
|
||||
} else {
|
||||
dropInfo.mode = null
|
||||
dropInfo.mode = "below"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// When 2 edges match, work out which is closer
|
||||
const mousePoint = [mouseX, mouseY]
|
||||
const edges = getEdges(bounds, mousePoint)
|
||||
const edge1 = edges[sides[0]]
|
||||
const delta1 = calculatePointDelta(mousePoint, edge1)
|
||||
const edge2 = edges[sides[1]]
|
||||
const delta2 = calculatePointDelta(mousePoint, edge2)
|
||||
const edge = delta1 < delta2 ? sides[0] : sides[1]
|
||||
dropInfo.side = edge
|
||||
if ([Sides.Top, Sides.Left].includes(edge)) {
|
||||
dropInfo.mode = "above"
|
||||
} else {
|
||||
dropInfo.mode = "below"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,39 +1,53 @@
|
|||
<script>
|
||||
import Indicator from "./Indicator.svelte"
|
||||
import { Sides } from "./DNDHandler.svelte"
|
||||
|
||||
export let dropInfo
|
||||
export let zIndex
|
||||
export let color
|
||||
export let transition
|
||||
|
||||
$: dimensions = getDimensions(dropInfo?.bounds, dropInfo?.mode)
|
||||
$: dimensions = getDimensions(dropInfo)
|
||||
$: prefix = dropInfo?.mode === "above" ? "Before" : "After"
|
||||
$: text = `${prefix} ${dropInfo?.name}`
|
||||
$: renderKey = `${dropInfo?.target}-${dropInfo?.side}`
|
||||
|
||||
const getDimensions = (bounds, mode) => {
|
||||
if (!bounds || !mode) {
|
||||
const getDimensions = info => {
|
||||
const { bounds, side } = info ?? {}
|
||||
if (!bounds || !side) {
|
||||
return null
|
||||
}
|
||||
const { left, top, width, height } = bounds
|
||||
if (side === Sides.Top || side === Sides.Bottom) {
|
||||
return {
|
||||
top: mode === "above" ? top - 4 : top + height,
|
||||
top: side === Sides.Top ? top - 4 : top + height,
|
||||
left: left - 2,
|
||||
width: width + 4,
|
||||
height: 0,
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
top: top - 2,
|
||||
left: side === Sides.Left ? left - 4 : left + width,
|
||||
width: 0,
|
||||
height: height + 4,
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#key `${dropInfo?.target}-${dropInfo?.mode}`}
|
||||
{#key renderKey}
|
||||
{#if dimensions && dropInfo?.mode !== "inside"}
|
||||
<Indicator
|
||||
left={dimensions.left}
|
||||
top={dimensions.top}
|
||||
left={Math.round(dimensions.left)}
|
||||
top={Math.round(dimensions.top)}
|
||||
width={dimensions.width}
|
||||
height={0}
|
||||
height={dimensions.height}
|
||||
{text}
|
||||
{zIndex}
|
||||
{color}
|
||||
{transition}
|
||||
alignRight={dropInfo?.side === Sides.Right}
|
||||
line
|
||||
/>
|
||||
{/if}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
export let zIndex
|
||||
export let transition = false
|
||||
export let line = false
|
||||
export let alignRight = false
|
||||
|
||||
$: flipped = top < 20
|
||||
</script>
|
||||
|
@ -26,7 +27,7 @@
|
|||
style="top: {top}px; left: {left}px; width: {width}px; height: {height}px; --color: {color}; --zIndex: {zIndex};"
|
||||
>
|
||||
{#if text}
|
||||
<div class="text" class:flipped class:line>
|
||||
<div class="text" class:flipped class:line class:right={alignRight}>
|
||||
{text}
|
||||
</div>
|
||||
{/if}
|
||||
|
@ -34,6 +35,7 @@
|
|||
|
||||
<style>
|
||||
.indicator {
|
||||
right: 0;
|
||||
position: absolute;
|
||||
z-index: var(--zIndex);
|
||||
border: 2px solid var(--color);
|
||||
|
@ -78,4 +80,8 @@
|
|||
transform: translateY(-50%) !important;
|
||||
border-radius: 4px !important;
|
||||
}
|
||||
.text.right {
|
||||
right: -2px !important;
|
||||
left: auto !important;
|
||||
}
|
||||
</style>
|
||||
|
|
Loading…
Reference in New Issue