kopia lustrzana https://github.com/Tldraw/Tldraw
389 wiersze
10 KiB
TypeScript
389 wiersze
10 KiB
TypeScript
import { Box, PI, TLShapeId } from '@tldraw/editor'
|
|
import { TestEditor } from '../TestEditor'
|
|
import { TL } from '../test-jsx'
|
|
|
|
let editor: TestEditor
|
|
let ids: Record<string, TLShapeId>
|
|
|
|
jest.useFakeTimers()
|
|
|
|
beforeEach(() => {
|
|
editor = new TestEditor()
|
|
ids = editor.createShapesFromJsx([
|
|
<TL.geo ref="boxA" x={0} y={0} w={100} h={100} />,
|
|
<TL.geo ref="boxB" x={100} y={100} w={50} h={50} />,
|
|
<TL.geo ref="boxC" x={400} y={400} w={100} h={100} />,
|
|
])
|
|
|
|
editor.selectAll()
|
|
})
|
|
|
|
describe('when less than two shapes are selected', () => {
|
|
it('does nothing', () => {
|
|
editor.setSelectedShapes([ids.boxB])
|
|
|
|
const fn = jest.fn()
|
|
editor.store.listen(fn)
|
|
editor.alignShapes(editor.getSelectedShapeIds(), 'top')
|
|
jest.advanceTimersByTime(1000)
|
|
expect(fn).not.toHaveBeenCalled()
|
|
})
|
|
})
|
|
|
|
describe('when multiple shapes are selected', () => {
|
|
it('does, undoes and redoes command', () => {
|
|
editor.mark('align')
|
|
editor.alignShapes(editor.getSelectedShapeIds(), 'top')
|
|
jest.advanceTimersByTime(1000)
|
|
|
|
editor.expectShapeToMatch({ id: ids.boxB, y: 0 })
|
|
editor.undo()
|
|
editor.expectShapeToMatch({ id: ids.boxB, y: 100 })
|
|
editor.redo()
|
|
editor.expectShapeToMatch({ id: ids.boxB, y: 0 })
|
|
})
|
|
|
|
it('aligns top', () => {
|
|
editor.alignShapes(editor.getSelectedShapeIds(), 'top')
|
|
jest.advanceTimersByTime(1000)
|
|
|
|
editor.expectShapeToMatch(
|
|
{ id: ids.boxA, y: 0 },
|
|
{ id: ids.boxB, y: 0 },
|
|
{ id: ids.boxC, y: 0 }
|
|
)
|
|
})
|
|
|
|
it('aligns right', () => {
|
|
editor.alignShapes(editor.getSelectedShapeIds(), 'right')
|
|
jest.advanceTimersByTime(1000)
|
|
|
|
editor.expectShapeToMatch(
|
|
{ id: ids.boxA, x: 400 },
|
|
{ id: ids.boxB, x: 450 },
|
|
{ id: ids.boxC, x: 400 }
|
|
)
|
|
})
|
|
|
|
it('aligns bottom', () => {
|
|
editor.alignShapes(editor.getSelectedShapeIds(), 'bottom')
|
|
jest.advanceTimersByTime(1000)
|
|
|
|
editor.expectShapeToMatch(
|
|
{ id: ids.boxA, y: 400 },
|
|
{ id: ids.boxB, y: 450 },
|
|
{ id: ids.boxC, y: 400 }
|
|
)
|
|
})
|
|
|
|
it('aligns left', () => {
|
|
editor.alignShapes(editor.getSelectedShapeIds(), 'left')
|
|
jest.advanceTimersByTime(1000)
|
|
|
|
editor.expectShapeToMatch(
|
|
{ id: ids.boxA, x: 0 },
|
|
{ id: ids.boxB, x: 0 },
|
|
{ id: ids.boxC, x: 0 }
|
|
)
|
|
})
|
|
|
|
it('aligns center horizontal', () => {
|
|
editor.alignShapes(editor.getSelectedShapeIds(), 'center-horizontal')
|
|
jest.advanceTimersByTime(1000)
|
|
|
|
editor.expectShapeToMatch(
|
|
{ id: ids.boxA, x: 200 },
|
|
{ id: ids.boxB, x: 225 },
|
|
{ id: ids.boxC, x: 200 }
|
|
)
|
|
})
|
|
|
|
it('aligns center vertical', () => {
|
|
editor.alignShapes(editor.getSelectedShapeIds(), 'center-vertical')
|
|
jest.advanceTimersByTime(1000)
|
|
|
|
editor.expectShapeToMatch(
|
|
{ id: ids.boxA, y: 200 },
|
|
{ id: ids.boxB, y: 225 },
|
|
{ id: ids.boxC, y: 200 }
|
|
)
|
|
})
|
|
|
|
it('aligns center, when shapes are rotated', () => {
|
|
editor.updateShapes([
|
|
{
|
|
id: ids.boxA,
|
|
type: 'geo',
|
|
rotation: PI,
|
|
},
|
|
{
|
|
id: ids.boxB,
|
|
type: 'geo',
|
|
rotation: PI,
|
|
},
|
|
{
|
|
id: ids.boxC,
|
|
type: 'geo',
|
|
rotation: PI,
|
|
},
|
|
])
|
|
|
|
editor.alignShapes(editor.getSelectedShapeIds(), 'center-vertical')
|
|
jest.advanceTimersByTime(1000)
|
|
editor.alignShapes(editor.getSelectedShapeIds(), 'center-horizontal')
|
|
jest.advanceTimersByTime(1000)
|
|
|
|
const commonBounds = Box.Common([
|
|
editor.getShapePageBounds(ids.boxA)!,
|
|
editor.getShapePageBounds(ids.boxB)!,
|
|
editor.getShapePageBounds(ids.boxC)!,
|
|
])
|
|
|
|
expect(commonBounds.midX).toBeCloseTo(editor.getShapePageBounds(ids.boxA)!.midX, 5)
|
|
expect(commonBounds.midX).toBeCloseTo(editor.getShapePageBounds(ids.boxB)!.midX, 5)
|
|
expect(commonBounds.midX).toBeCloseTo(editor.getShapePageBounds(ids.boxC)!.midX, 5)
|
|
|
|
expect(commonBounds.midY).toBeCloseTo(editor.getShapePageBounds(ids.boxA)!.midY, 5)
|
|
expect(commonBounds.midY).toBeCloseTo(editor.getShapePageBounds(ids.boxB)!.midY, 5)
|
|
expect(commonBounds.midY).toBeCloseTo(editor.getShapePageBounds(ids.boxC)!.midY, 5)
|
|
})
|
|
|
|
it('aligns top-left, when shapes are rotated', () => {
|
|
editor.updateShapes([
|
|
{
|
|
id: ids.boxA,
|
|
type: 'geo',
|
|
rotation: 0.2,
|
|
},
|
|
{
|
|
id: ids.boxB,
|
|
type: 'geo',
|
|
rotation: 0.4,
|
|
},
|
|
{
|
|
id: ids.boxC,
|
|
type: 'geo',
|
|
rotation: 0.6,
|
|
},
|
|
])
|
|
|
|
editor.alignShapes(editor.getSelectedShapeIds(), 'top')
|
|
jest.advanceTimersByTime(1000)
|
|
editor.alignShapes(editor.getSelectedShapeIds(), 'left')
|
|
jest.advanceTimersByTime(1000)
|
|
|
|
const commonBounds = Box.Common([
|
|
editor.getShapePageBounds(ids.boxA)!,
|
|
editor.getShapePageBounds(ids.boxB)!,
|
|
editor.getShapePageBounds(ids.boxC)!,
|
|
])
|
|
|
|
expect(commonBounds.minX).toBeCloseTo(editor.getShapePageBounds(ids.boxA)!.minX, 5)
|
|
expect(commonBounds.minX).toBeCloseTo(editor.getShapePageBounds(ids.boxB)!.minX, 5)
|
|
expect(commonBounds.minX).toBeCloseTo(editor.getShapePageBounds(ids.boxC)!.minX, 5)
|
|
|
|
expect(commonBounds.minY).toBeCloseTo(editor.getShapePageBounds(ids.boxA)!.minY, 5)
|
|
expect(commonBounds.minY).toBeCloseTo(editor.getShapePageBounds(ids.boxB)!.minY, 5)
|
|
expect(commonBounds.minY).toBeCloseTo(editor.getShapePageBounds(ids.boxC)!.minY, 5)
|
|
})
|
|
|
|
it('aligns bottom-right, when shapes are rotated', () => {
|
|
editor.updateShapes([
|
|
{
|
|
id: ids.boxA,
|
|
type: 'geo',
|
|
rotation: 0.2,
|
|
},
|
|
{
|
|
id: ids.boxB,
|
|
type: 'geo',
|
|
rotation: 0.4,
|
|
},
|
|
{
|
|
id: ids.boxC,
|
|
type: 'geo',
|
|
rotation: 0.6,
|
|
},
|
|
])
|
|
|
|
editor.setSelectedShapes([ids.boxA, ids.boxB, ids.boxC])
|
|
editor.alignShapes(editor.getSelectedShapeIds(), 'bottom')
|
|
jest.advanceTimersByTime(1000)
|
|
editor.alignShapes(editor.getSelectedShapeIds(), 'right')
|
|
jest.advanceTimersByTime(1000)
|
|
|
|
const commonBounds = Box.Common([
|
|
editor.getShapePageBounds(ids.boxA)!,
|
|
editor.getShapePageBounds(ids.boxC)!,
|
|
])
|
|
|
|
expect(commonBounds.maxX).toBeCloseTo(editor.getShapePageBounds(ids.boxA)!.maxX, 5)
|
|
expect(commonBounds.maxX).toBeCloseTo(editor.getShapePageBounds(ids.boxB)!.maxX, 5)
|
|
expect(commonBounds.maxX).toBeCloseTo(editor.getShapePageBounds(ids.boxC)!.maxX, 5)
|
|
|
|
expect(commonBounds.maxX).toBeCloseTo(editor.getShapePageBounds(ids.boxA)!.maxX, 5)
|
|
expect(commonBounds.maxY).toBeCloseTo(editor.getShapePageBounds(ids.boxB)!.maxY, 5)
|
|
expect(commonBounds.maxY).toBeCloseTo(editor.getShapePageBounds(ids.boxC)!.maxY, 5)
|
|
})
|
|
})
|
|
|
|
describe('When shapes are parented to other shapes...', () => {
|
|
beforeEach(() => {
|
|
editor = new TestEditor()
|
|
editor.selectAll()
|
|
editor.deleteShapes(editor.getSelectedShapeIds())
|
|
ids = editor.createShapesFromJsx([
|
|
<TL.geo ref="boxA" x={0} y={0} w={100} h={100}>
|
|
<TL.geo ref="boxB" x={100} y={100} w={50} h={50} />
|
|
</TL.geo>,
|
|
<TL.geo ref="boxC" x={400} y={400} w={100} h={100} />,
|
|
])
|
|
|
|
editor.selectAll()
|
|
})
|
|
|
|
it('Aligns to the top left.', () => {
|
|
editor.setSelectedShapes([ids.boxC, ids.boxB])
|
|
|
|
const commonBoundsBefore = Box.Common([
|
|
editor.getShapePageBounds(ids.boxC)!,
|
|
editor.getShapePageBounds(ids.boxB)!,
|
|
])
|
|
|
|
editor.alignShapes(editor.getSelectedShapeIds(), 'top')
|
|
jest.advanceTimersByTime(1000)
|
|
editor.alignShapes(editor.getSelectedShapeIds(), 'left')
|
|
jest.advanceTimersByTime(1000)
|
|
|
|
const commonBoundsAfter = Box.Common([
|
|
editor.getShapePageBounds(ids.boxC)!,
|
|
editor.getShapePageBounds(ids.boxB)!,
|
|
])
|
|
|
|
expect(commonBoundsBefore.minX).toBeCloseTo(commonBoundsAfter.minX)
|
|
expect(commonBoundsBefore.minY).toBeCloseTo(commonBoundsAfter.minY)
|
|
})
|
|
|
|
it('Aligns to the bottom right.', () => {
|
|
editor.setSelectedShapes([ids.boxC, ids.boxB])
|
|
|
|
const commonBoundsBefore = Box.Common([
|
|
editor.getShapePageBounds(ids.boxC)!,
|
|
editor.getShapePageBounds(ids.boxB)!,
|
|
])
|
|
|
|
editor.alignShapes(editor.getSelectedShapeIds(), 'bottom')
|
|
jest.advanceTimersByTime(1000)
|
|
editor.alignShapes(editor.getSelectedShapeIds(), 'right')
|
|
jest.advanceTimersByTime(1000)
|
|
|
|
const commonBoundsAfter = Box.Common([
|
|
editor.getShapePageBounds(ids.boxC)!,
|
|
editor.getShapePageBounds(ids.boxB)!,
|
|
])
|
|
|
|
expect(commonBoundsBefore.maxX).toBeCloseTo(commonBoundsAfter.maxX)
|
|
expect(commonBoundsBefore.maxY).toBeCloseTo(commonBoundsAfter.maxY)
|
|
})
|
|
})
|
|
|
|
describe('When shapes are parented to a rotated shape...', () => {
|
|
beforeEach(() => {
|
|
editor = new TestEditor()
|
|
editor.selectAll()
|
|
editor.deleteShapes(editor.getSelectedShapeIds())
|
|
editor.createShapes([
|
|
{
|
|
id: ids.boxA,
|
|
type: 'geo',
|
|
x: 100,
|
|
y: 100,
|
|
props: {
|
|
w: 100,
|
|
h: 100,
|
|
},
|
|
rotation: PI / 2,
|
|
},
|
|
{
|
|
id: ids.boxB,
|
|
type: 'geo',
|
|
parentId: ids.boxA,
|
|
x: 100,
|
|
y: 100,
|
|
props: {
|
|
geo: 'ellipse',
|
|
w: 50,
|
|
h: 50,
|
|
},
|
|
},
|
|
{
|
|
id: ids.boxC,
|
|
type: 'geo',
|
|
x: 400,
|
|
y: 400,
|
|
props: {
|
|
w: 100,
|
|
h: 100,
|
|
},
|
|
},
|
|
])
|
|
editor.selectAll()
|
|
})
|
|
|
|
it('Aligns to the top left.', () => {
|
|
editor.setSelectedShapes([ids.boxC, ids.boxB])
|
|
|
|
const commonBoundsBefore = Box.Common([
|
|
editor.getShapePageBounds(ids.boxC)!,
|
|
editor.getShapePageBounds(ids.boxB)!,
|
|
])
|
|
|
|
editor.alignShapes(editor.getSelectedShapeIds(), 'top')
|
|
jest.advanceTimersByTime(1000)
|
|
editor.alignShapes(editor.getSelectedShapeIds(), 'left')
|
|
jest.advanceTimersByTime(1000)
|
|
|
|
const commonBoundsAfter = Box.Common([
|
|
editor.getShapePageBounds(ids.boxC)!,
|
|
editor.getShapePageBounds(ids.boxB)!,
|
|
])
|
|
|
|
expect(commonBoundsBefore.minX).toBeCloseTo(commonBoundsAfter.minX)
|
|
expect(commonBoundsBefore.minY).toBeCloseTo(commonBoundsAfter.minY)
|
|
|
|
expect(commonBoundsAfter.minX).toBeCloseTo(editor.getShapePageBounds(ids.boxB)!.minX, 5)
|
|
expect(commonBoundsAfter.minX).toBeCloseTo(editor.getShapePageBounds(ids.boxC)!.minX, 5)
|
|
|
|
expect(commonBoundsAfter.minY).toBeCloseTo(editor.getShapePageBounds(ids.boxB)!.minY, 5)
|
|
expect(commonBoundsAfter.minY).toBeCloseTo(editor.getShapePageBounds(ids.boxC)!.minY, 5)
|
|
})
|
|
|
|
it('Aligns to the bottom right.', () => {
|
|
editor.setSelectedShapes([ids.boxC, ids.boxB])
|
|
|
|
const commonBoundsBefore = Box.Common([
|
|
editor.getShapePageBounds(ids.boxC)!,
|
|
editor.getShapePageBounds(ids.boxB)!,
|
|
])
|
|
|
|
editor.alignShapes(editor.getSelectedShapeIds(), 'bottom')
|
|
jest.advanceTimersByTime(1000)
|
|
editor.alignShapes(editor.getSelectedShapeIds(), 'right')
|
|
jest.advanceTimersByTime(1000)
|
|
|
|
const commonBoundsAfter = Box.Common([
|
|
editor.getShapePageBounds(ids.boxC)!,
|
|
editor.getShapePageBounds(ids.boxB)!,
|
|
])
|
|
|
|
expect(commonBoundsBefore.maxX).toBeCloseTo(commonBoundsAfter.maxX)
|
|
expect(commonBoundsBefore.maxY).toBeCloseTo(commonBoundsAfter.maxY)
|
|
|
|
expect(commonBoundsAfter.maxX).toBeCloseTo(editor.getShapePageBounds(ids.boxB)!.maxX, 5)
|
|
expect(commonBoundsAfter.maxX).toBeCloseTo(editor.getShapePageBounds(ids.boxC)!.maxX, 5)
|
|
|
|
expect(commonBoundsAfter.maxY).toBeCloseTo(editor.getShapePageBounds(ids.boxB)!.maxY, 5)
|
|
expect(commonBoundsAfter.maxY).toBeCloseTo(editor.getShapePageBounds(ids.boxC)!.maxY, 5)
|
|
})
|
|
})
|