Adds manual create and update, more jsdoc
This commit is contained in:
parent
92b8df4b51
commit
16fda2fddf
13 changed files with 304 additions and 61 deletions
|
@ -1,6 +1,11 @@
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import { TLDraw } from '@tldraw/tldraw'
|
import { TLDraw, TLDrawState } from '@tldraw/tldraw'
|
||||||
|
|
||||||
export default function Editor(): JSX.Element {
|
export default function Editor(): JSX.Element {
|
||||||
return <TLDraw id="tldraw" />
|
const handleMount = React.useCallback((tlstate: TLDrawState) => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
|
window.tlstate = tlstate
|
||||||
|
}, [])
|
||||||
|
return <TLDraw id="tldraw" onMount={handleMount} />
|
||||||
}
|
}
|
||||||
|
|
|
@ -312,7 +312,7 @@ export class Rectangle extends TLDrawShapeUtil<RectangleShape> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
transformSingle(shape: RectangleShape, bounds: TLBounds) {
|
transformSingle(_shape: RectangleShape, bounds: TLBounds) {
|
||||||
return {
|
return {
|
||||||
size: Vec.round([bounds.width, bounds.height]),
|
size: Vec.round([bounds.width, bounds.height]),
|
||||||
point: Vec.round([bounds.minX, bounds.minY]),
|
point: Vec.round([bounds.minX, bounds.minY]),
|
||||||
|
|
|
@ -56,7 +56,7 @@ if (typeof window !== 'undefined') {
|
||||||
export class Text extends TLDrawShapeUtil<TextShape> {
|
export class Text extends TLDrawShapeUtil<TextShape> {
|
||||||
type = TLDrawShapeType.Text as const
|
type = TLDrawShapeType.Text as const
|
||||||
toolType = TLDrawToolType.Text
|
toolType = TLDrawToolType.Text
|
||||||
canChangeAspectRatio = false
|
isAspectRatioLocked = true
|
||||||
isEditableText = true
|
isEditableText = true
|
||||||
canBind = true
|
canBind = true
|
||||||
|
|
||||||
|
@ -325,7 +325,7 @@ export class Text extends TLDrawShapeUtil<TextShape> {
|
||||||
transformSingle(
|
transformSingle(
|
||||||
_shape: TextShape,
|
_shape: TextShape,
|
||||||
bounds: TLBounds,
|
bounds: TLBounds,
|
||||||
{ initialShape, scaleX }: TLTransformInfo<TextShape>
|
{ initialShape, scaleX, scaleY }: TLTransformInfo<TextShape>
|
||||||
): Partial<TextShape> {
|
): Partial<TextShape> {
|
||||||
const {
|
const {
|
||||||
style: { scale = 1 },
|
style: { scale = 1 },
|
||||||
|
@ -335,7 +335,7 @@ export class Text extends TLDrawShapeUtil<TextShape> {
|
||||||
point: Vec.round([bounds.minX, bounds.minY]),
|
point: Vec.round([bounds.minX, bounds.minY]),
|
||||||
style: {
|
style: {
|
||||||
...initialShape.style,
|
...initialShape.style,
|
||||||
scale: scale * Math.abs(scaleX),
|
scale: scale * Math.max(Math.abs(scaleY), Math.abs(scaleX)),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -369,7 +369,7 @@ export class Text extends TLDrawShapeUtil<TextShape> {
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldDelete(shape: TextShape): boolean {
|
shouldDelete(shape: TextShape): boolean {
|
||||||
return shape.text.length === 0
|
return shape.text.trim().length === 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// getBindingPoint(shape, point, origin, direction, expandDistance) {
|
// getBindingPoint(shape, point, origin, direction, expandDistance) {
|
||||||
|
|
|
@ -16,3 +16,4 @@ export * from './delete-page'
|
||||||
export * from './rename-page'
|
export * from './rename-page'
|
||||||
export * from './duplicate-page'
|
export * from './duplicate-page'
|
||||||
export * from './change-page'
|
export * from './change-page'
|
||||||
|
export * from './update'
|
||||||
|
|
|
@ -44,7 +44,7 @@ describe('Move command', () => {
|
||||||
|
|
||||||
it('does, undoes and redoes command', () => {
|
it('does, undoes and redoes command', () => {
|
||||||
tlstate.loadDocument(doc)
|
tlstate.loadDocument(doc)
|
||||||
tlstate.setSelectedIds(['b'])
|
tlstate.select('b')
|
||||||
tlstate.moveToBack()
|
tlstate.moveToBack()
|
||||||
expect(getSortedShapeIds(tlstate.state)).toBe('bacd')
|
expect(getSortedShapeIds(tlstate.state)).toBe('bacd')
|
||||||
tlstate.undo()
|
tlstate.undo()
|
||||||
|
@ -56,21 +56,21 @@ describe('Move command', () => {
|
||||||
describe('to back', () => {
|
describe('to back', () => {
|
||||||
it('moves a shape to back', () => {
|
it('moves a shape to back', () => {
|
||||||
tlstate.loadDocument(doc)
|
tlstate.loadDocument(doc)
|
||||||
tlstate.setSelectedIds(['b'])
|
tlstate.select('b')
|
||||||
tlstate.moveToBack()
|
tlstate.moveToBack()
|
||||||
expect(getSortedShapeIds(tlstate.state)).toBe('bacd')
|
expect(getSortedShapeIds(tlstate.state)).toBe('bacd')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('moves two adjacent siblings to back', () => {
|
it('moves two adjacent siblings to back', () => {
|
||||||
tlstate.loadDocument(doc)
|
tlstate.loadDocument(doc)
|
||||||
tlstate.setSelectedIds(['b', 'c'])
|
tlstate.select('b', 'c')
|
||||||
tlstate.moveToBack()
|
tlstate.moveToBack()
|
||||||
expect(getSortedShapeIds(tlstate.state)).toBe('bcad')
|
expect(getSortedShapeIds(tlstate.state)).toBe('bcad')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('moves two non-adjacent siblings to back', () => {
|
it('moves two non-adjacent siblings to back', () => {
|
||||||
tlstate.loadDocument(doc)
|
tlstate.loadDocument(doc)
|
||||||
tlstate.setSelectedIds(['b', 'd'])
|
tlstate.select('b', 'd')
|
||||||
tlstate.moveToBack()
|
tlstate.moveToBack()
|
||||||
expect(getSortedShapeIds(tlstate.state)).toBe('bdac')
|
expect(getSortedShapeIds(tlstate.state)).toBe('bdac')
|
||||||
})
|
})
|
||||||
|
@ -79,35 +79,35 @@ describe('Move command', () => {
|
||||||
describe('backward', () => {
|
describe('backward', () => {
|
||||||
it('moves a shape backward', () => {
|
it('moves a shape backward', () => {
|
||||||
tlstate.loadDocument(doc)
|
tlstate.loadDocument(doc)
|
||||||
tlstate.setSelectedIds(['c'])
|
tlstate.select('c')
|
||||||
tlstate.moveBackward()
|
tlstate.moveBackward()
|
||||||
expect(getSortedShapeIds(tlstate.state)).toBe('acbd')
|
expect(getSortedShapeIds(tlstate.state)).toBe('acbd')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('moves a shape at first index backward', () => {
|
it('moves a shape at first index backward', () => {
|
||||||
tlstate.loadDocument(doc)
|
tlstate.loadDocument(doc)
|
||||||
tlstate.setSelectedIds(['a'])
|
tlstate.select('a')
|
||||||
tlstate.moveBackward()
|
tlstate.moveBackward()
|
||||||
expect(getSortedShapeIds(tlstate.state)).toBe('abcd')
|
expect(getSortedShapeIds(tlstate.state)).toBe('abcd')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('moves two adjacent siblings backward', () => {
|
it('moves two adjacent siblings backward', () => {
|
||||||
tlstate.loadDocument(doc)
|
tlstate.loadDocument(doc)
|
||||||
tlstate.setSelectedIds(['c', 'd'])
|
tlstate.select('c', 'd')
|
||||||
tlstate.moveBackward()
|
tlstate.moveBackward()
|
||||||
expect(getSortedShapeIds(tlstate.state)).toBe('acdb')
|
expect(getSortedShapeIds(tlstate.state)).toBe('acdb')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('moves two non-adjacent siblings backward', () => {
|
it('moves two non-adjacent siblings backward', () => {
|
||||||
tlstate.loadDocument(doc)
|
tlstate.loadDocument(doc)
|
||||||
tlstate.setSelectedIds(['b', 'd'])
|
tlstate.select('b', 'd')
|
||||||
tlstate.moveBackward()
|
tlstate.moveBackward()
|
||||||
expect(getSortedShapeIds(tlstate.state)).toBe('badc')
|
expect(getSortedShapeIds(tlstate.state)).toBe('badc')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('moves two adjacent siblings backward at zero index', () => {
|
it('moves two adjacent siblings backward at zero index', () => {
|
||||||
tlstate.loadDocument(doc)
|
tlstate.loadDocument(doc)
|
||||||
tlstate.setSelectedIds(['a', 'b'])
|
tlstate.select('a', 'b')
|
||||||
tlstate.moveBackward()
|
tlstate.moveBackward()
|
||||||
expect(getSortedShapeIds(tlstate.state)).toBe('abcd')
|
expect(getSortedShapeIds(tlstate.state)).toBe('abcd')
|
||||||
})
|
})
|
||||||
|
@ -116,14 +116,14 @@ describe('Move command', () => {
|
||||||
describe('forward', () => {
|
describe('forward', () => {
|
||||||
it('moves a shape forward', () => {
|
it('moves a shape forward', () => {
|
||||||
tlstate.loadDocument(doc)
|
tlstate.loadDocument(doc)
|
||||||
tlstate.setSelectedIds(['c'])
|
tlstate.select('c')
|
||||||
tlstate.moveForward()
|
tlstate.moveForward()
|
||||||
expect(getSortedShapeIds(tlstate.state)).toBe('abdc')
|
expect(getSortedShapeIds(tlstate.state)).toBe('abdc')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('moves a shape forward at the top index', () => {
|
it('moves a shape forward at the top index', () => {
|
||||||
tlstate.loadDocument(doc)
|
tlstate.loadDocument(doc)
|
||||||
tlstate.setSelectedIds(['b'])
|
tlstate.select('b')
|
||||||
tlstate.moveForward()
|
tlstate.moveForward()
|
||||||
tlstate.moveForward()
|
tlstate.moveForward()
|
||||||
tlstate.moveForward()
|
tlstate.moveForward()
|
||||||
|
@ -132,21 +132,21 @@ describe('Move command', () => {
|
||||||
|
|
||||||
it('moves two adjacent siblings forward', () => {
|
it('moves two adjacent siblings forward', () => {
|
||||||
tlstate.loadDocument(doc)
|
tlstate.loadDocument(doc)
|
||||||
tlstate.setSelectedIds(['a', 'b'])
|
tlstate.select('a', 'b')
|
||||||
tlstate.moveForward()
|
tlstate.moveForward()
|
||||||
expect(getSortedShapeIds(tlstate.state)).toBe('cabd')
|
expect(getSortedShapeIds(tlstate.state)).toBe('cabd')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('moves two non-adjacent siblings forward', () => {
|
it('moves two non-adjacent siblings forward', () => {
|
||||||
tlstate.loadDocument(doc)
|
tlstate.loadDocument(doc)
|
||||||
tlstate.setSelectedIds(['a', 'c'])
|
tlstate.select('a', 'c')
|
||||||
tlstate.moveForward()
|
tlstate.moveForward()
|
||||||
expect(getSortedShapeIds(tlstate.state)).toBe('badc')
|
expect(getSortedShapeIds(tlstate.state)).toBe('badc')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('moves two adjacent siblings forward at top index', () => {
|
it('moves two adjacent siblings forward at top index', () => {
|
||||||
tlstate.loadDocument(doc)
|
tlstate.loadDocument(doc)
|
||||||
tlstate.setSelectedIds(['c', 'd'])
|
tlstate.select('c', 'd')
|
||||||
tlstate.moveForward()
|
tlstate.moveForward()
|
||||||
expect(getSortedShapeIds(tlstate.state)).toBe('abcd')
|
expect(getSortedShapeIds(tlstate.state)).toBe('abcd')
|
||||||
})
|
})
|
||||||
|
@ -155,28 +155,28 @@ describe('Move command', () => {
|
||||||
describe('to front', () => {
|
describe('to front', () => {
|
||||||
it('moves a shape to front', () => {
|
it('moves a shape to front', () => {
|
||||||
tlstate.loadDocument(doc)
|
tlstate.loadDocument(doc)
|
||||||
tlstate.setSelectedIds(['b'])
|
tlstate.select('b')
|
||||||
tlstate.moveToFront()
|
tlstate.moveToFront()
|
||||||
expect(getSortedShapeIds(tlstate.state)).toBe('acdb')
|
expect(getSortedShapeIds(tlstate.state)).toBe('acdb')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('moves two adjacent siblings to front', () => {
|
it('moves two adjacent siblings to front', () => {
|
||||||
tlstate.loadDocument(doc)
|
tlstate.loadDocument(doc)
|
||||||
tlstate.setSelectedIds(['a', 'b'])
|
tlstate.select('a', 'b')
|
||||||
tlstate.moveToFront()
|
tlstate.moveToFront()
|
||||||
expect(getSortedShapeIds(tlstate.state)).toBe('cdab')
|
expect(getSortedShapeIds(tlstate.state)).toBe('cdab')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('moves two non-adjacent siblings to front', () => {
|
it('moves two non-adjacent siblings to front', () => {
|
||||||
tlstate.loadDocument(doc)
|
tlstate.loadDocument(doc)
|
||||||
tlstate.setSelectedIds(['a', 'c'])
|
tlstate.select('a', 'c')
|
||||||
tlstate.moveToFront()
|
tlstate.moveToFront()
|
||||||
expect(getSortedShapeIds(tlstate.state)).toBe('bdac')
|
expect(getSortedShapeIds(tlstate.state)).toBe('bdac')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('moves siblings already at front to front', () => {
|
it('moves siblings already at front to front', () => {
|
||||||
tlstate.loadDocument(doc)
|
tlstate.loadDocument(doc)
|
||||||
tlstate.setSelectedIds(['c', 'd'])
|
tlstate.select('c', 'd')
|
||||||
tlstate.moveToFront()
|
tlstate.moveToFront()
|
||||||
expect(getSortedShapeIds(tlstate.state)).toBe('abcd')
|
expect(getSortedShapeIds(tlstate.state)).toBe('abcd')
|
||||||
})
|
})
|
||||||
|
|
|
@ -26,7 +26,7 @@ describe('Toggle command', () => {
|
||||||
|
|
||||||
it('toggles on before off when mixed values', () => {
|
it('toggles on before off when mixed values', () => {
|
||||||
tlstate.loadDocument(mockDocument)
|
tlstate.loadDocument(mockDocument)
|
||||||
tlstate.setSelectedIds(['rect2'])
|
tlstate.select('rect2')
|
||||||
expect(tlstate.getShape<RectangleShape>('rect1').isAspectRatioLocked).toBe(undefined)
|
expect(tlstate.getShape<RectangleShape>('rect1').isAspectRatioLocked).toBe(undefined)
|
||||||
expect(tlstate.getShape<RectangleShape>('rect2').isAspectRatioLocked).toBe(undefined)
|
expect(tlstate.getShape<RectangleShape>('rect2').isAspectRatioLocked).toBe(undefined)
|
||||||
tlstate.toggleAspectRatioLocked()
|
tlstate.toggleAspectRatioLocked()
|
||||||
|
|
1
packages/tldraw/src/state/command/update/index.ts
Normal file
1
packages/tldraw/src/state/command/update/index.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export * from './update.command'
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { TLDrawState } from '~state'
|
||||||
|
import { mockDocument } from '~test'
|
||||||
|
import { Utils } from '@tldraw/core'
|
||||||
|
|
||||||
|
const doc = Utils.deepClone(mockDocument)
|
||||||
|
|
||||||
|
describe('Move command', () => {
|
||||||
|
const tlstate = new TLDrawState()
|
||||||
|
|
||||||
|
it('does, undoes and redoes command', () => {
|
||||||
|
tlstate.loadDocument(doc)
|
||||||
|
tlstate.updateShapes({ id: 'rect1', point: [100, 100] })
|
||||||
|
expect(tlstate.getShape('rect1').point).toStrictEqual([100, 100])
|
||||||
|
tlstate.undo()
|
||||||
|
expect(tlstate.getShape('rect1').point).toStrictEqual([0, 0])
|
||||||
|
tlstate.redo()
|
||||||
|
expect(tlstate.getShape('rect1').point).toStrictEqual([100, 100])
|
||||||
|
})
|
||||||
|
})
|
47
packages/tldraw/src/state/command/update/update.command.ts
Normal file
47
packages/tldraw/src/state/command/update/update.command.ts
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
import type { Data, TLDrawCommand, PagePartial, TLDrawShape } from '~types'
|
||||||
|
import { TLDR } from '~state/tldr'
|
||||||
|
|
||||||
|
export function update(
|
||||||
|
data: Data,
|
||||||
|
updates: ({ id: string } & Partial<TLDrawShape>)[]
|
||||||
|
): TLDrawCommand {
|
||||||
|
const ids = updates.map((update) => update.id)
|
||||||
|
|
||||||
|
const before: PagePartial = {
|
||||||
|
shapes: {},
|
||||||
|
bindings: {},
|
||||||
|
}
|
||||||
|
|
||||||
|
const after: PagePartial = {
|
||||||
|
shapes: {},
|
||||||
|
bindings: {},
|
||||||
|
}
|
||||||
|
|
||||||
|
const change = TLDR.mutateShapes(
|
||||||
|
data,
|
||||||
|
ids,
|
||||||
|
(_shape, i) => updates[i],
|
||||||
|
data.appState.currentPageId
|
||||||
|
)
|
||||||
|
|
||||||
|
before.shapes = change.before
|
||||||
|
after.shapes = change.after
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: 'translate_shapes',
|
||||||
|
before: {
|
||||||
|
document: {
|
||||||
|
pages: {
|
||||||
|
[data.appState.currentPageId]: before,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
after: {
|
||||||
|
document: {
|
||||||
|
pages: {
|
||||||
|
[data.appState.currentPageId]: after,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
import { TextShape, TLDrawShape, TLDrawStatus } from '~types'
|
import { TextShape, TLDrawStatus } from '~types'
|
||||||
import type { Session } from '~types'
|
import type { Session } from '~types'
|
||||||
import type { Data } from '~types'
|
import type { Data } from '~types'
|
||||||
import { TLDR } from '~state/tldr'
|
import { TLDR } from '~state/tldr'
|
||||||
|
|
|
@ -24,6 +24,7 @@ export class TransformSession implements Session {
|
||||||
|
|
||||||
start = () => void null
|
start = () => void null
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
update = (data: Data, point: number[], isAspectRatioLocked = false, _altKey = false) => {
|
update = (data: Data, point: number[], isAspectRatioLocked = false, _altKey = false) => {
|
||||||
const {
|
const {
|
||||||
transformType,
|
transformType,
|
||||||
|
@ -37,7 +38,7 @@ export class TransformSession implements Session {
|
||||||
const newBoundingBox = Utils.getTransformedBoundingBox(
|
const newBoundingBox = Utils.getTransformedBoundingBox(
|
||||||
initialBounds,
|
initialBounds,
|
||||||
transformType,
|
transformType,
|
||||||
Vec.vec(this.origin, point),
|
Vec.sub(point, this.origin),
|
||||||
pageState.boundsRotation,
|
pageState.boundsRotation,
|
||||||
isAspectRatioLocked || isAllAspectRatioLocked
|
isAspectRatioLocked || isAllAspectRatioLocked
|
||||||
)
|
)
|
||||||
|
|
|
@ -668,8 +668,6 @@ export class TLDR {
|
||||||
next = this.onChildrenChange(data, next, pageId) || next
|
next = this.onChildrenChange(data, next, pageId) || next
|
||||||
}
|
}
|
||||||
|
|
||||||
// data.page.shapes[next.id] = next
|
|
||||||
|
|
||||||
return next
|
return next
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -135,14 +135,14 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
}
|
}
|
||||||
/* -------------------- Internal -------------------- */
|
/* -------------------- Internal -------------------- */
|
||||||
|
|
||||||
protected onStateWillChange = (_state: Data, id: string): void => {
|
// protected onStateWillChange = (_state: Data, id: string): void => {
|
||||||
if (!id.startsWith('patch')) {
|
// }
|
||||||
this.selectHistory.stack = [[]]
|
|
||||||
this.selectHistory.pointer = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected onStateDidChange = (state: Data, id: string): void => {
|
protected onStateDidChange = (state: Data, id: string): void => {
|
||||||
|
if (!id.startsWith('patch')) {
|
||||||
|
this.clearSelectHistory()
|
||||||
|
}
|
||||||
|
|
||||||
this._onChange?.(this, state, id)
|
this._onChange?.(this, state, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -531,7 +531,9 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
|
|
||||||
loadDocument = (document: TLDrawDocument, onChange?: TLDrawState['_onChange']): this => {
|
loadDocument = (document: TLDrawDocument, onChange?: TLDrawState['_onChange']): this => {
|
||||||
this._onChange = onChange
|
this._onChange = onChange
|
||||||
|
this.deselectAll()
|
||||||
this.resetHistory()
|
this.resetHistory()
|
||||||
|
this.clearSelectHistory()
|
||||||
|
|
||||||
// this.selectHistory.pointer = 0
|
// this.selectHistory.pointer = 0
|
||||||
// this.selectHistory.stack = [[]]
|
// this.selectHistory.stack = [[]]
|
||||||
|
@ -942,7 +944,13 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
this.selectHistory.stack.push(ids)
|
this.selectHistory.stack.push(ids)
|
||||||
}
|
}
|
||||||
|
|
||||||
setSelectedIds(ids: string[], push = false): this {
|
/**
|
||||||
|
* Set the current selection.
|
||||||
|
* @param ids The ids to select
|
||||||
|
* @param push Whether to add the ids to the current selection instead.
|
||||||
|
* @returns this
|
||||||
|
*/
|
||||||
|
private setSelectedIds(ids: string[], push = false): this {
|
||||||
return this.patchState(
|
return this.patchState(
|
||||||
{
|
{
|
||||||
appState: {
|
appState: {
|
||||||
|
@ -961,6 +969,10 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undo the most recent selection.
|
||||||
|
* @returns this
|
||||||
|
*/
|
||||||
undoSelect(): this {
|
undoSelect(): this {
|
||||||
if (this.selectHistory.pointer > 0) {
|
if (this.selectHistory.pointer > 0) {
|
||||||
this.selectHistory.pointer--
|
this.selectHistory.pointer--
|
||||||
|
@ -969,6 +981,10 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redo the previous selection.
|
||||||
|
* @returns this
|
||||||
|
*/
|
||||||
redoSelect(): this {
|
redoSelect(): this {
|
||||||
if (this.selectHistory.pointer < this.selectHistory.stack.length - 1) {
|
if (this.selectHistory.pointer < this.selectHistory.stack.length - 1) {
|
||||||
this.selectHistory.pointer++
|
this.selectHistory.pointer++
|
||||||
|
@ -977,12 +993,21 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select one or more shapes.
|
||||||
|
* @param ids The shape ids to select.
|
||||||
|
* @returns this
|
||||||
|
*/
|
||||||
select = (...ids: string[]): this => {
|
select = (...ids: string[]): this => {
|
||||||
this.setSelectedIds(ids)
|
this.setSelectedIds(ids)
|
||||||
this.addToSelectHistory(ids)
|
this.addToSelectHistory(ids)
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select all shapes on the page.
|
||||||
|
* @returns this
|
||||||
|
*/
|
||||||
selectAll = (): this => {
|
selectAll = (): this => {
|
||||||
if (this.session) return this
|
if (this.session) return this
|
||||||
this.setSelectedIds(Object.keys(this.page.shapes))
|
this.setSelectedIds(Object.keys(this.page.shapes))
|
||||||
|
@ -993,6 +1018,10 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deselect any selected shapes.
|
||||||
|
* @returns this
|
||||||
|
*/
|
||||||
deselectAll = (): this => {
|
deselectAll = (): this => {
|
||||||
this.setSelectedIds([])
|
this.setSelectedIds([])
|
||||||
this.addToSelectHistory(this.selectedIds)
|
this.addToSelectHistory(this.selectedIds)
|
||||||
|
@ -1003,66 +1032,213 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
/* Shape Functions */
|
/* Shape Functions */
|
||||||
/* -------------------------------------------------- */
|
/* -------------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manually create shapes on the page.
|
||||||
|
* @param shapes An array of shape partials, containing the initial props for the shapes.
|
||||||
|
* @command
|
||||||
|
* @returns this
|
||||||
|
*/
|
||||||
|
createShapes = (
|
||||||
|
...shapes: ({ id: string; type: TLDrawShapeType } & Partial<TLDrawShape>)[]
|
||||||
|
): this => {
|
||||||
|
if (shapes.length === 0) return this
|
||||||
|
return this.create(
|
||||||
|
...shapes.map((shape) => {
|
||||||
|
return TLDR.getShapeUtils(shape as TLDrawShape).create(shape)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manually update a set of shapes.
|
||||||
|
* @param shapes An array of shape partials, containing the changes to be made to each shape.
|
||||||
|
* @command
|
||||||
|
* @returns this
|
||||||
|
*/
|
||||||
|
updateShapes = (...shapes: ({ id: string } & Partial<TLDrawShape>)[]): this => {
|
||||||
|
if (shapes.length === 0) return this
|
||||||
|
return this.setState(Commands.update(this.state, shapes), 'updated_shape')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create one or more shapes.
|
||||||
|
* @param shapes An array of shapes.
|
||||||
|
* @command
|
||||||
|
* @returns this
|
||||||
|
*/
|
||||||
|
create = (...shapes: TLDrawShape[]): this => {
|
||||||
|
if (shapes.length === 0) return this
|
||||||
|
return this.setState(Commands.create(this.state, shapes))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete one or more shapes.
|
||||||
|
* @param ids The ids of the shapes to delete.
|
||||||
|
* @command
|
||||||
|
* @returns this
|
||||||
|
*/
|
||||||
|
delete = (ids = this.selectedIds): this => {
|
||||||
|
if (ids.length === 0) return this
|
||||||
|
return this.setState(Commands.deleteShapes(this.state, ids))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete all shapes on the page.
|
||||||
|
* @returns this
|
||||||
|
*/
|
||||||
|
clear = (): this => {
|
||||||
|
this.selectAll()
|
||||||
|
this.delete()
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the style for one or more shapes.
|
||||||
|
* @param style A style partial to apply to the shapes.
|
||||||
|
* @param ids The ids of the shapes to change (defaults to selection).
|
||||||
|
* @returns this
|
||||||
|
*/
|
||||||
style = (style: Partial<ShapeStyles>, ids = this.selectedIds) => {
|
style = (style: Partial<ShapeStyles>, ids = this.selectedIds) => {
|
||||||
return this.setState(Commands.style(this.state, ids, style))
|
return this.setState(Commands.style(this.state, ids, style))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Align one or more shapes.
|
||||||
|
* @param direction Whether to align horizontally or vertically.
|
||||||
|
* @param ids The ids of the shapes to change (defaults to selection).
|
||||||
|
* @returns this
|
||||||
|
*/
|
||||||
align = (type: AlignType, ids = this.selectedIds) => {
|
align = (type: AlignType, ids = this.selectedIds) => {
|
||||||
return this.setState(Commands.align(this.state, ids, type))
|
return this.setState(Commands.align(this.state, ids, type))
|
||||||
}
|
}
|
||||||
|
|
||||||
distribute = (type: DistributeType, ids = this.selectedIds) => {
|
/**
|
||||||
return this.setState(Commands.distribute(this.state, ids, type))
|
* Distribute one or more shapes.
|
||||||
|
* @param direction Whether to distribute horizontally or vertically..
|
||||||
|
* @param ids The ids of the shapes to change (defaults to selection).
|
||||||
|
* @returns this
|
||||||
|
*/
|
||||||
|
distribute = (direction: DistributeType, ids = this.selectedIds) => {
|
||||||
|
return this.setState(Commands.distribute(this.state, ids, direction))
|
||||||
}
|
}
|
||||||
|
|
||||||
stretch = (type: StretchType, ids = this.selectedIds) => {
|
/**
|
||||||
return this.setState(Commands.stretch(this.state, ids, type))
|
* Stretch one or more shapes to their common bounds.
|
||||||
|
* @param direction Whether to stretch horizontally or vertically.
|
||||||
|
* @param ids The ids of the shapes to change (defaults to selection).
|
||||||
|
* @returns this
|
||||||
|
*/
|
||||||
|
stretch = (direction: StretchType, ids = this.selectedIds) => {
|
||||||
|
return this.setState(Commands.stretch(this.state, ids, direction))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flip one or more shapes horizontally.
|
||||||
|
* @param ids The ids of the shapes to change (defaults to selection).
|
||||||
|
* @returns this
|
||||||
|
*/
|
||||||
flipHorizontal = (ids = this.selectedIds) => {
|
flipHorizontal = (ids = this.selectedIds) => {
|
||||||
return this.setState(Commands.flip(this.state, ids, FlipType.Horizontal))
|
return this.setState(Commands.flip(this.state, ids, FlipType.Horizontal))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flip one or more shapes vertically.
|
||||||
|
* @param ids The ids of the shapes to change (defaults to selection).
|
||||||
|
* @returns this
|
||||||
|
*/
|
||||||
flipVertical = (ids = this.selectedIds) => {
|
flipVertical = (ids = this.selectedIds) => {
|
||||||
return this.setState(Commands.flip(this.state, ids, FlipType.Vertical))
|
return this.setState(Commands.flip(this.state, ids, FlipType.Vertical))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move one or more shapes to the back of the page.
|
||||||
|
* @param ids The ids of the shapes to change (defaults to selection).
|
||||||
|
* @returns this
|
||||||
|
*/
|
||||||
moveToBack = (ids = this.selectedIds) => {
|
moveToBack = (ids = this.selectedIds) => {
|
||||||
return this.setState(Commands.move(this.state, ids, MoveType.ToBack))
|
return this.setState(Commands.move(this.state, ids, MoveType.ToBack))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move one or more shapes backward on of the page.
|
||||||
|
* @param ids The ids of the shapes to change (defaults to selection).
|
||||||
|
* @returns this
|
||||||
|
*/
|
||||||
moveBackward = (ids = this.selectedIds) => {
|
moveBackward = (ids = this.selectedIds) => {
|
||||||
return this.setState(Commands.move(this.state, ids, MoveType.Backward))
|
return this.setState(Commands.move(this.state, ids, MoveType.Backward))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move one or more shapes forward on the page.
|
||||||
|
* @param ids The ids of the shapes to change (defaults to selection).
|
||||||
|
* @returns this
|
||||||
|
*/
|
||||||
moveForward = (ids = this.selectedIds) => {
|
moveForward = (ids = this.selectedIds) => {
|
||||||
return this.setState(Commands.move(this.state, ids, MoveType.Forward))
|
return this.setState(Commands.move(this.state, ids, MoveType.Forward))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move one or more shapes to the front of the page.
|
||||||
|
* @param ids The ids of the shapes to change (defaults to selection).
|
||||||
|
* @returns this
|
||||||
|
*/
|
||||||
moveToFront = (ids = this.selectedIds) => {
|
moveToFront = (ids = this.selectedIds) => {
|
||||||
return this.setState(Commands.move(this.state, ids, MoveType.ToFront))
|
return this.setState(Commands.move(this.state, ids, MoveType.ToFront))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nudge one or more shapes in a direction.
|
||||||
|
* @param delta The direction to nudge the shapes.
|
||||||
|
* @param isMajor Whether this is a major (i.e. shift) nudge.
|
||||||
|
* @param ids The ids to change (defaults to selection).
|
||||||
|
* @returns this
|
||||||
|
*/
|
||||||
nudge = (delta: number[], isMajor = false, ids = this.selectedIds): this => {
|
nudge = (delta: number[], isMajor = false, ids = this.selectedIds): this => {
|
||||||
return this.setState(Commands.translate(this.state, ids, Vec.mul(delta, isMajor ? 10 : 1)))
|
return this.setState(Commands.translate(this.state, ids, Vec.mul(delta, isMajor ? 10 : 1)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Duplicate one or more shapes.
|
||||||
|
* @param ids The ids to duplicate (defaults to selection).
|
||||||
|
* @returns this
|
||||||
|
*/
|
||||||
duplicate = (ids = this.selectedIds): this => {
|
duplicate = (ids = this.selectedIds): this => {
|
||||||
return this.setState(Commands.duplicate(this.state, ids))
|
return this.setState(Commands.duplicate(this.state, ids))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle the hidden property of one or more shapes.
|
||||||
|
* @param ids The ids to change (defaults to selection).
|
||||||
|
* @returns this
|
||||||
|
*/
|
||||||
toggleHidden = (ids = this.selectedIds): this => {
|
toggleHidden = (ids = this.selectedIds): this => {
|
||||||
return this.setState(Commands.toggle(this.state, ids, 'isHidden'))
|
return this.setState(Commands.toggle(this.state, ids, 'isHidden'))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle the locked property of one or more shapes.
|
||||||
|
* @param ids The ids to change (defaults to selection).
|
||||||
|
* @returns this
|
||||||
|
*/
|
||||||
toggleLocked = (ids = this.selectedIds): this => {
|
toggleLocked = (ids = this.selectedIds): this => {
|
||||||
return this.setState(Commands.toggle(this.state, ids, 'isLocked'))
|
return this.setState(Commands.toggle(this.state, ids, 'isLocked'))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle the fixed-aspect-ratio property of one or more shapes.
|
||||||
|
* @param ids The ids to change (defaults to selection).
|
||||||
|
* @returns this
|
||||||
|
*/
|
||||||
toggleAspectRatioLocked = (ids = this.selectedIds): this => {
|
toggleAspectRatioLocked = (ids = this.selectedIds): this => {
|
||||||
return this.setState(Commands.toggle(this.state, ids, 'isAspectRatioLocked'))
|
return this.setState(Commands.toggle(this.state, ids, 'isAspectRatioLocked'))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle the decoration at a handle of one or more shapes.
|
||||||
|
* @param handleId The handle to toggle.
|
||||||
|
* @param ids The ids of the shapes to toggle the decoration on.
|
||||||
|
* @returns this
|
||||||
|
*/
|
||||||
toggleDecoration = (handleId: string, ids = this.selectedIds): this => {
|
toggleDecoration = (handleId: string, ids = this.selectedIds): this => {
|
||||||
if (handleId === 'start' || handleId === 'end') {
|
if (handleId === 'start' || handleId === 'end') {
|
||||||
return this.setState(Commands.toggleDecoration(this.state, ids, handleId))
|
return this.setState(Commands.toggleDecoration(this.state, ids, handleId))
|
||||||
|
@ -1071,34 +1247,29 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rotate one or more shapes by a delta.
|
||||||
|
* @param delta The delta in radians.
|
||||||
|
* @param ids The ids to rotate (defaults to selection).
|
||||||
|
* @returns this
|
||||||
|
*/
|
||||||
rotate = (delta = Math.PI * -0.5, ids = this.selectedIds): this => {
|
rotate = (delta = Math.PI * -0.5, ids = this.selectedIds): this => {
|
||||||
return this.setState(Commands.rotate(this.state, ids, delta))
|
return this.setState(Commands.rotate(this.state, ids, delta))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Group one or more shapes.
|
||||||
|
* @returns this
|
||||||
|
* @todo
|
||||||
|
*/
|
||||||
group = (): this => {
|
group = (): this => {
|
||||||
// TODO
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// this.setState(Commands.toggle(this.state, ids, 'isAspectRatioLocked'))
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
create = (...shapes: TLDrawShape[]): this => {
|
|
||||||
return this.setState(Commands.create(this.state, shapes))
|
|
||||||
}
|
|
||||||
|
|
||||||
delete = (ids = this.selectedIds): this => {
|
|
||||||
if (ids.length === 0) return this
|
|
||||||
|
|
||||||
return this.setState(Commands.deleteShapes(this.state, ids))
|
|
||||||
}
|
|
||||||
|
|
||||||
clear = (): this => {
|
|
||||||
this.selectAll()
|
|
||||||
this.delete()
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel the current session.
|
||||||
|
* @returns this
|
||||||
|
*/
|
||||||
cancel = (): this => {
|
cancel = (): this => {
|
||||||
switch (this.state.appState.status.current) {
|
switch (this.state.appState.status.current) {
|
||||||
case TLDrawStatus.Idle: {
|
case TLDrawStatus.Idle: {
|
||||||
|
|
Loading…
Reference in a new issue