tldraw/state/commands/move.ts

179 lines
5.1 KiB
TypeScript
Raw Normal View History

2021-05-29 13:59:11 +00:00
import Command from './command'
import history from '../history'
import { Data, MoveType, Shape } from 'types'
2021-06-29 12:00:59 +00:00
import { setToArray } from 'utils'
import tld from 'utils/tld'
2021-06-21 21:35:28 +00:00
import { getShapeUtils } from 'state/shape-utils'
2021-05-23 17:09:23 +00:00
2021-06-21 21:35:28 +00:00
export default function moveCommand(data: Data, type: MoveType): void {
2021-06-29 12:00:59 +00:00
const page = tld.getPage(data)
2021-05-23 17:09:23 +00:00
2021-06-29 12:00:59 +00:00
const selectedIds = setToArray(tld.getSelectedIds(data))
2021-05-23 17:09:23 +00:00
const initialIndices = Object.fromEntries(
selectedIds.map((id) => [id, page.shapes[id].childIndex])
)
history.execute(
data,
new Command({
2021-06-19 17:22:46 +00:00
name: 'change_child_index',
2021-05-29 13:59:11 +00:00
category: 'canvas',
2021-05-23 17:09:23 +00:00
manualSelection: true,
do(data) {
2021-06-29 12:00:59 +00:00
const page = tld.getPage(data)
2021-05-23 17:09:23 +00:00
const shapes = selectedIds.map((id) => page.shapes[id])
const shapesByParentId = shapes.reduce<Record<string, Shape[]>>(
(acc, shape) => {
if (acc[shape.parentId] === undefined) {
acc[shape.parentId] = []
}
acc[shape.parentId].push(shape)
return acc
},
{}
)
switch (type) {
case MoveType.ToFront: {
2021-06-21 21:35:28 +00:00
for (const id in shapesByParentId) {
2021-06-29 12:00:59 +00:00
moveToFront(shapesByParentId[id], tld.getChildren(data, id))
2021-05-23 17:09:23 +00:00
}
break
}
case MoveType.ToBack: {
2021-06-21 21:35:28 +00:00
for (const id in shapesByParentId) {
2021-06-29 12:00:59 +00:00
moveToBack(shapesByParentId[id], tld.getChildren(data, id))
2021-05-23 17:09:23 +00:00
}
break
}
case MoveType.Forward: {
2021-06-21 21:35:28 +00:00
for (const id in shapesByParentId) {
2021-05-23 17:09:23 +00:00
const visited = new Set<string>()
2021-06-29 12:00:59 +00:00
const siblings = tld.getChildren(data, id)
2021-05-23 17:09:23 +00:00
shapesByParentId[id]
.sort((a, b) => b.childIndex - a.childIndex)
.forEach((shape) => moveForward(shape, siblings, visited))
}
break
}
case MoveType.Backward: {
2021-06-21 21:35:28 +00:00
for (const id in shapesByParentId) {
2021-05-23 17:09:23 +00:00
const visited = new Set<string>()
2021-06-29 12:00:59 +00:00
const siblings = tld.getChildren(data, id)
2021-05-23 17:09:23 +00:00
shapesByParentId[id]
.sort((a, b) => a.childIndex - b.childIndex)
.forEach((shape) => moveBackward(shape, siblings, visited))
}
break
}
}
},
undo(data) {
2021-06-29 12:00:59 +00:00
const page = tld.getPage(data)
2021-05-23 17:09:23 +00:00
2021-06-21 21:35:28 +00:00
for (const id of selectedIds) {
const shape = page.shapes[id]
2021-05-29 13:59:11 +00:00
getShapeUtils(shape).setProperty(
shape,
'childIndex',
initialIndices[id]
)
2021-05-23 17:09:23 +00:00
}
},
})
)
}
function moveToFront(shapes: Shape[], siblings: Shape[]) {
shapes.sort((a, b) => a.childIndex - b.childIndex)
const diff = siblings
.filter((sib) => !shapes.includes(sib))
.sort((a, b) => b.childIndex - a.childIndex)
if (diff.length === 0) return
const startIndex = Math.ceil(diff[0].childIndex) + 1
shapes.forEach((shape, i) =>
2021-05-29 13:59:11 +00:00
getShapeUtils(shape).setProperty(shape, 'childIndex', startIndex + i)
)
2021-05-23 17:09:23 +00:00
}
function moveToBack(shapes: Shape[], siblings: Shape[]) {
shapes.sort((a, b) => b.childIndex - a.childIndex)
const diff = siblings
.filter((sib) => !shapes.includes(sib))
.sort((a, b) => a.childIndex - b.childIndex)
if (diff.length === 0) return
const startIndex = diff[0]?.childIndex
const step = startIndex / (shapes.length + 1)
shapes.forEach((shape, i) =>
2021-05-29 13:59:11 +00:00
getShapeUtils(shape).setProperty(
shape,
'childIndex',
startIndex - (i + 1) * step
)
)
2021-05-23 17:09:23 +00:00
}
function moveForward(shape: Shape, siblings: Shape[], visited: Set<string>) {
visited.add(shape.id)
const index = siblings.indexOf(shape)
const nextSibling = siblings[index + 1]
if (nextSibling && !visited.has(nextSibling.id)) {
const nextNextSibling = siblings[index + 2]
let nextIndex = nextNextSibling
? (nextSibling.childIndex + nextNextSibling.childIndex) / 2
: Math.ceil(nextSibling.childIndex + 1)
if (nextIndex === nextSibling.childIndex) {
2021-06-29 12:00:59 +00:00
tld.forceIntegerChildIndices(siblings)
2021-05-23 17:09:23 +00:00
nextIndex = nextNextSibling
? (nextSibling.childIndex + nextNextSibling.childIndex) / 2
: Math.ceil(nextSibling.childIndex + 1)
}
2021-05-29 13:59:11 +00:00
getShapeUtils(shape).setProperty(shape, 'childIndex', nextIndex)
2021-05-23 17:09:23 +00:00
siblings.sort((a, b) => a.childIndex - b.childIndex)
}
}
function moveBackward(shape: Shape, siblings: Shape[], visited: Set<string>) {
visited.add(shape.id)
const index = siblings.indexOf(shape)
const nextSibling = siblings[index - 1]
if (nextSibling && !visited.has(nextSibling.id)) {
const nextNextSibling = siblings[index - 2]
2021-06-21 21:35:28 +00:00
const nextIndex = nextNextSibling
2021-05-23 17:09:23 +00:00
? (nextSibling.childIndex + nextNextSibling.childIndex) / 2
: nextSibling.childIndex / 2
if (shape.childIndex === nextSibling.childIndex) {
2021-06-29 12:00:59 +00:00
tld.forceIntegerChildIndices(siblings)
2021-05-23 17:09:23 +00:00
nextNextSibling
? (nextSibling.childIndex + nextNextSibling.childIndex) / 2
: nextSibling.childIndex / 2
}
2021-05-29 13:59:11 +00:00
getShapeUtils(shape).setProperty(shape, 'childIndex', nextIndex)
2021-05-23 17:09:23 +00:00
siblings.sort((a, b) => a.childIndex - b.childIndex)
}
}