tldraw/state/commands/distribute.ts

149 lines
4.6 KiB
TypeScript
Raw Normal View History

2021-06-02 15:05:44 +00:00
import Command from './command'
import history from '../history'
import { Data, DistributeType } from 'types'
2021-05-26 21:47:46 +00:00
import {
getBoundsCenter,
getCommonBounds,
getPage,
getSelectedShapes,
2021-06-02 15:05:44 +00:00
} from 'utils/utils'
import { getShapeUtils } from 'lib/shape-utils'
2021-05-26 19:20:52 +00:00
export default function distributeCommand(data: Data, type: DistributeType) {
const { currentPageId } = data
2021-05-26 21:47:46 +00:00
const selectedShapes = getSelectedShapes(data)
const entries = selectedShapes.map(
(shape) => [shape.id, getShapeUtils(shape).getBounds(shape)] as const
)
2021-05-27 06:32:55 +00:00
2021-05-26 21:47:46 +00:00
const boundsForShapes = Object.fromEntries(entries)
const commonBounds = getCommonBounds(...entries.map((entry) => entry[1]))
const centers = Object.fromEntries(
selectedShapes.map((shape) => [
shape.id,
getBoundsCenter(boundsForShapes[shape.id]),
2021-05-26 19:20:52 +00:00
])
)
history.execute(
data,
new Command({
2021-06-02 15:05:44 +00:00
name: 'distributed',
category: 'canvas',
2021-05-26 19:20:52 +00:00
do(data) {
const { shapes } = getPage(data, currentPageId)
2021-05-26 21:47:46 +00:00
const len = entries.length
2021-05-26 19:20:52 +00:00
switch (type) {
case DistributeType.Horizontal: {
2021-05-26 21:53:31 +00:00
const span = entries.reduce((a, c) => a + c[1].width, 0)
2021-05-26 21:47:46 +00:00
if (span > commonBounds.width) {
2021-05-26 21:53:31 +00:00
const left = entries.sort((a, b) => a[1].minX - b[1].minX)[0]
2021-05-26 21:47:46 +00:00
2021-05-26 21:53:31 +00:00
const right = entries.sort((a, b) => b[1].maxX - a[1].maxX)[0]
2021-05-26 21:47:46 +00:00
2021-05-26 21:53:31 +00:00
const entriesToMove = entries
2021-05-26 21:47:46 +00:00
.filter((a) => a !== left && a !== right)
.sort((a, b) => centers[a[0]][0] - centers[b[0]][0])
const step =
(centers[right[0]][0] - centers[left[0]][0]) / (len - 1)
const x = centers[left[0]][0] + step
for (let i = 0; i < entriesToMove.length; i++) {
const [id, bounds] = entriesToMove[i]
const shape = shapes[id]
2021-06-02 15:05:44 +00:00
getShapeUtils(shape).setProperty(shape, 'point', [
2021-05-26 21:47:46 +00:00
x + step * i - bounds.width / 2,
bounds.minY,
])
}
} else {
2021-05-27 06:32:55 +00:00
const entriesToMove = entries.sort(
2021-05-26 21:53:31 +00:00
(a, b) => centers[a[0]][0] - centers[b[0]][0]
)
2021-05-26 21:47:46 +00:00
let x = commonBounds.minX
2021-05-26 21:53:31 +00:00
const step = (commonBounds.width - span) / (len - 1)
2021-05-26 21:47:46 +00:00
2021-05-27 06:32:55 +00:00
for (let i = 0; i < entriesToMove.length - 1; i++) {
const [id, bounds] = entriesToMove[i]
2021-05-26 21:47:46 +00:00
const shape = shapes[id]
2021-06-02 15:05:44 +00:00
getShapeUtils(shape).setProperty(shape, 'point', [
x,
bounds.minY,
])
2021-05-26 21:47:46 +00:00
x += bounds.width + step
}
}
break
2021-05-26 19:20:52 +00:00
}
case DistributeType.Vertical: {
2021-05-26 21:53:31 +00:00
const span = entries.reduce((a, c) => a + c[1].height, 0)
2021-05-26 21:47:46 +00:00
if (span > commonBounds.height) {
2021-05-26 21:53:31 +00:00
const top = entries.sort((a, b) => a[1].minY - b[1].minY)[0]
2021-05-26 21:47:46 +00:00
2021-05-26 21:53:31 +00:00
const bottom = entries.sort((a, b) => b[1].maxY - a[1].maxY)[0]
2021-05-26 21:47:46 +00:00
2021-05-26 21:53:31 +00:00
const entriesToMove = entries
2021-05-26 21:47:46 +00:00
.filter((a) => a !== top && a !== bottom)
.sort((a, b) => centers[a[0]][1] - centers[b[0]][1])
const step =
(centers[bottom[0]][1] - centers[top[0]][1]) / (len - 1)
const y = centers[top[0]][1] + step
for (let i = 0; i < entriesToMove.length; i++) {
const [id, bounds] = entriesToMove[i]
const shape = shapes[id]
2021-06-02 15:05:44 +00:00
getShapeUtils(shape).setProperty(shape, 'point', [
2021-05-26 21:47:46 +00:00
bounds.minX,
y + step * i - bounds.height / 2,
])
}
} else {
2021-05-27 06:32:55 +00:00
const entriesToMove = entries.sort(
2021-05-26 21:53:31 +00:00
(a, b) => centers[a[0]][1] - centers[b[0]][1]
)
2021-05-26 21:47:46 +00:00
let y = commonBounds.minY
2021-05-26 21:53:31 +00:00
const step = (commonBounds.height - span) / (len - 1)
2021-05-26 21:47:46 +00:00
2021-05-27 06:32:55 +00:00
for (let i = 0; i < entriesToMove.length - 1; i++) {
const [id, bounds] = entriesToMove[i]
2021-05-26 21:47:46 +00:00
const shape = shapes[id]
2021-06-02 15:05:44 +00:00
getShapeUtils(shape).setProperty(shape, 'point', [
bounds.minX,
y,
])
2021-05-26 21:47:46 +00:00
y += bounds.height + step
}
}
break
2021-05-26 19:20:52 +00:00
}
}
},
undo(data) {
const { shapes } = getPage(data, currentPageId)
2021-05-26 21:47:46 +00:00
for (let id in boundsForShapes) {
2021-05-26 19:20:52 +00:00
const shape = shapes[id]
2021-05-26 21:47:46 +00:00
const initialBounds = boundsForShapes[id]
2021-06-02 15:05:44 +00:00
getShapeUtils(shape).setProperty(shape, 'point', [
2021-05-26 21:47:46 +00:00
initialBounds.minX,
initialBounds.minY,
])
2021-05-26 19:20:52 +00:00
}
},
})
)
}