tldraw/state/sessions/translate-session.ts

240 lines
5.8 KiB
TypeScript
Raw Normal View History

2021-06-23 22:32:21 +00:00
import { Data, GroupShape, Shape, ShapeType } from 'types'
import vec from 'utils/vec'
2021-05-29 12:40:41 +00:00
import BaseSession from './base-session'
import commands from 'state/commands'
import { uniqueId } from 'utils/utils'
2021-06-21 21:35:28 +00:00
import { getShapeUtils } from 'state/shape-utils'
2021-06-29 12:00:59 +00:00
import tld from 'utils/tld'
2021-05-13 06:44:52 +00:00
export default class TranslateSession extends BaseSession {
delta = [0, 0]
2021-06-05 19:36:46 +00:00
prev = [0, 0]
2021-05-13 06:44:52 +00:00
origin: number[]
snapshot: TranslateSnapshot
2021-05-19 21:24:41 +00:00
isCloning = false
2021-05-13 06:44:52 +00:00
constructor(data: Data, point: number[]) {
2021-05-13 06:44:52 +00:00
super(data)
this.origin = point
this.snapshot = getTranslateSnapshot(data)
}
2021-06-21 21:35:28 +00:00
update(
data: Data,
point: number[],
isAligned: boolean,
isCloning: boolean
): void {
2021-06-24 12:34:43 +00:00
const { clones, initialShapes, initialParents } = this.snapshot
2021-06-29 12:00:59 +00:00
const { shapes } = tld.getPage(data)
2021-05-20 09:25:14 +00:00
2021-05-13 06:44:52 +00:00
const delta = vec.vec(this.origin, point)
2021-05-20 08:19:13 +00:00
if (isAligned) {
if (Math.abs(delta[0]) < Math.abs(delta[1])) {
delta[0] = 0
} else {
delta[1] = 0
}
}
2021-06-08 10:32:20 +00:00
const trueDelta = vec.sub(delta, this.prev)
this.delta = delta
this.prev = delta
2021-05-20 09:25:14 +00:00
if (isCloning) {
if (!this.isCloning) {
this.isCloning = true
2021-06-13 13:55:37 +00:00
// Move original shapes back to start
for (const { id, point } of initialShapes) {
const shape = shapes[id]
2021-06-13 13:55:37 +00:00
getShapeUtils(shape).translateTo(shape, point)
shapes[shape.id] = { ...shape }
2021-05-20 09:25:14 +00:00
}
for (const clone of clones) {
2021-06-13 13:55:37 +00:00
shapes[clone.id] = { ...clone, point: [...clone.point] }
const shape = shapes[clone.id]
getShapeUtils(shape).translateBy(shape, delta)
shapes[clone.id] = { ...shape }
2021-06-13 13:55:37 +00:00
const parent = shapes[shape.parentId]
2021-06-04 18:49:27 +00:00
if (!parent) continue
2021-06-13 13:55:37 +00:00
2021-06-04 18:49:27 +00:00
getShapeUtils(parent).setProperty(parent, 'children', [
...parent.children,
2021-06-13 13:55:37 +00:00
shape.id,
2021-06-04 18:49:27 +00:00
])
shapes[shape.parentId] = { ...parent }
2021-05-20 09:25:14 +00:00
}
2021-05-19 21:24:41 +00:00
}
2021-05-20 09:25:14 +00:00
2021-06-08 10:32:20 +00:00
for (const { id } of clones) {
const shape = shapes[id]
2021-06-08 10:32:20 +00:00
getShapeUtils(shape).translateBy(shape, trueDelta)
shapes[id] = { ...shape }
2021-05-19 21:24:41 +00:00
}
2021-06-04 16:08:43 +00:00
2021-06-29 12:00:59 +00:00
tld.setSelectedIds(
2021-06-13 13:55:37 +00:00
data,
clones.map((c) => c.id)
)
2021-06-29 12:00:59 +00:00
tld.updateParents(
2021-06-04 16:08:43 +00:00
data,
clones.map((c) => c.id)
)
2021-05-20 09:25:14 +00:00
} else {
if (this.isCloning) {
this.isCloning = false
2021-06-29 12:00:59 +00:00
tld.setSelectedIds(
data,
initialShapes.map((c) => c.id)
)
2021-05-19 21:24:41 +00:00
2021-05-20 09:25:14 +00:00
for (const clone of clones) {
delete shapes[clone.id]
}
2021-06-04 16:08:43 +00:00
2021-06-13 13:55:37 +00:00
for (const initialShape of initialShapes) {
2021-06-29 12:00:59 +00:00
tld.getDocumentBranch(data, initialShape.id).forEach((id) => {
2021-06-13 13:55:37 +00:00
const shape = shapes[id]
getShapeUtils(shape).translateBy(shape, delta)
shapes[id] = { ...shape }
2021-06-13 13:55:37 +00:00
})
}
initialParents.forEach((parent) => {
const shape = shapes[parent.id] as GroupShape
shapes[parent.id] = { ...shape, children: parent.children }
})
2021-05-20 09:25:14 +00:00
}
2021-06-04 16:08:43 +00:00
for (const initialShape of initialShapes) {
2021-06-29 12:00:59 +00:00
tld.getDocumentBranch(data, initialShape.id).forEach((id) => {
2021-06-05 19:36:46 +00:00
const shape = shapes[id]
getShapeUtils(shape).translateBy(shape, trueDelta)
shapes[id] = { ...shape }
2021-06-05 19:36:46 +00:00
})
2021-05-20 09:25:14 +00:00
}
2021-06-04 16:08:43 +00:00
2021-06-29 12:00:59 +00:00
tld.updateParents(
2021-06-04 16:08:43 +00:00
data,
initialShapes.map((s) => s.id)
)
2021-05-13 06:44:52 +00:00
}
}
2021-06-21 21:35:28 +00:00
cancel(data: Data): void {
2021-06-24 12:34:43 +00:00
const { initialShapes, initialParents, clones } = this.snapshot
2021-06-29 12:00:59 +00:00
const { shapes } = tld.getPage(data)
2021-05-19 21:24:41 +00:00
2021-06-05 19:36:46 +00:00
for (const { id } of initialShapes) {
2021-06-29 12:00:59 +00:00
tld.getDocumentBranch(data, id).forEach((id) => {
2021-06-05 19:36:46 +00:00
const shape = shapes[id]
getShapeUtils(shape).translateBy(shape, vec.neg(this.delta))
})
2021-05-20 09:25:14 +00:00
}
for (const { id } of clones) {
delete shapes[id]
2021-05-13 06:44:52 +00:00
}
2021-06-04 16:08:43 +00:00
initialParents.forEach(({ id, children }) => {
const shape = shapes[id]
getShapeUtils(shape).setProperty(shape, 'children', children)
})
2021-06-29 12:00:59 +00:00
tld.updateParents(
2021-06-04 16:08:43 +00:00
data,
initialShapes.map((s) => s.id)
)
2021-05-13 06:44:52 +00:00
}
2021-06-21 21:35:28 +00:00
complete(data: Data): void {
2021-05-29 12:54:33 +00:00
if (!this.snapshot.hasUnlockedShapes) return
2021-05-29 12:40:41 +00:00
2021-05-19 21:24:41 +00:00
commands.translate(
data,
this.snapshot,
getTranslateSnapshot(data),
this.isCloning
)
2021-05-13 06:44:52 +00:00
}
}
2021-06-21 21:35:28 +00:00
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
2021-05-13 06:44:52 +00:00
export function getTranslateSnapshot(data: Data) {
2021-06-29 14:54:46 +00:00
const page = tld.getPage(data)
2021-06-05 19:36:46 +00:00
2021-06-29 14:54:46 +00:00
const selectedShapes = tld.getSelectedShapeSnapshot(data)
2021-06-04 18:49:27 +00:00
2021-06-04 16:08:43 +00:00
const hasUnlockedShapes = selectedShapes.length > 0
2021-06-29 14:54:46 +00:00
const initialParents = Array.from(
2021-06-04 16:08:43 +00:00
new Set(selectedShapes.map((s) => s.parentId)).values()
)
.filter((id) => id !== data.currentPageId)
2021-06-29 14:54:46 +00:00
.map((id) => {
const shape = page.shapes[id]
return {
id: shape.id,
children: shape.children,
}
})
2021-05-19 21:24:41 +00:00
2021-05-13 06:44:52 +00:00
return {
2021-05-29 12:54:33 +00:00
hasUnlockedShapes,
currentPageId: data.currentPageId,
2021-06-29 14:54:46 +00:00
initialParents,
2021-06-04 16:08:43 +00:00
initialShapes: selectedShapes.map(({ id, point, parentId }) => ({
id,
point,
parentId,
})),
2021-06-04 18:49:27 +00:00
clones: selectedShapes
.filter((shape) => shape.type !== ShapeType.Group)
.flatMap((shape) => {
2021-06-23 22:32:21 +00:00
const clone: Shape = {
2021-06-04 18:49:27 +00:00
...shape,
id: uniqueId(),
2021-06-04 18:49:27 +00:00
parentId: shape.parentId,
2021-06-29 14:54:46 +00:00
childIndex: tld.getChildIndexAbove(data, shape.id),
2021-06-04 18:49:27 +00:00
}
return clone
}),
2021-05-13 06:44:52 +00:00
}
}
export type TranslateSnapshot = ReturnType<typeof getTranslateSnapshot>
2021-06-04 18:49:27 +00:00
2021-06-21 21:35:28 +00:00
// function cloneGroup(data: Data, clone: Shape): Shape[] {
// if (clone.type !== ShapeType.Group) {
// return [clone]
// }
2021-06-04 18:49:27 +00:00
2021-06-29 12:00:59 +00:00
// const page = tld.getPage(data)
2021-06-21 21:35:28 +00:00
// const childClones = clone.children.flatMap((id) => {
// const newId = uniqueId()
// const source = page.shapes[id]
// const next = { ...source, id: newId, parentId: clone.id }
2021-06-04 18:49:27 +00:00
2021-06-21 21:35:28 +00:00
// if (next.type === ShapeType.Group) {
// return [next, ...cloneGroup(data, next)]
// }
2021-06-04 18:49:27 +00:00
2021-06-21 21:35:28 +00:00
// return [next]
// })
2021-06-04 18:49:27 +00:00
2021-06-21 21:35:28 +00:00
// return [clone, ...childClones]
// }