Adds handle toggling

This commit is contained in:
Steve Ruiz 2021-08-11 14:34:17 +01:00
parent 283e678a4d
commit 1a31dd6697
10 changed files with 105 additions and 20 deletions

View file

@ -23,7 +23,7 @@ export function useHandleEvents(id: string) {
if (e.button !== 0) return if (e.button !== 0) return
e.stopPropagation() e.stopPropagation()
const isDoubleClick = inputs.isDoubleClick() const isDoubleClick = inputs.isDoubleClick()
const info = inputs.pointerUp(e, 'bounds') const info = inputs.pointerUp(e, id)
if (e.currentTarget.hasPointerCapture(e.pointerId)) { if (e.currentTarget.hasPointerCapture(e.pointerId)) {
e.currentTarget?.releasePointerCapture(e.pointerId) e.currentTarget?.releasePointerCapture(e.pointerId)

View file

@ -6,9 +6,7 @@ export function create(data: Data, shapes: TLDrawShape[]): Command {
id: 'toggle_shapes', id: 'toggle_shapes',
before: { before: {
page: { page: {
shapes: Object.fromEntries( shapes: Object.fromEntries(shapes.map((shape) => [shape.id, undefined])),
shapes.map((shape) => [shape.id, undefined])
),
}, },
pageState: { pageState: {
selectedIds: [...data.pageState.selectedIds], selectedIds: [...data.pageState.selectedIds],

View file

@ -51,7 +51,6 @@ export function deleteShapes(data: Data, ids: string[]): Command {
after: { after: {
page: { page: {
shapes: { shapes: {
...Object.fromEntries(ids.map((id) => [id, undefined])),
...Object.fromEntries( ...Object.fromEntries(
shapesWithBindingsToUpdate.map((shape) => { shapesWithBindingsToUpdate.map((shape) => {
for (const id in shape.handles) { for (const id in shape.handles) {
@ -64,6 +63,7 @@ export function deleteShapes(data: Data, ids: string[]): Command {
return [shape.id, shape] return [shape.id, shape]
}) })
), ),
...Object.fromEntries(ids.map((id) => [id, undefined])),
}, },
bindings: Object.fromEntries(bindingIdsToDelete.map((id) => [id, undefined])), bindings: Object.fromEntries(bindingIdsToDelete.map((id) => [id, undefined])),
}, },

View file

@ -10,3 +10,4 @@ export * from './style'
export * from './toggle' export * from './toggle'
export * from './translate' export * from './translate'
export * from './flip' export * from './flip'
export * from './toggle-decoration'

View file

@ -0,0 +1 @@
export * from './toggle-decoration.command'

View file

@ -0,0 +1,34 @@
import { TLDR } from '../../tldr'
import { TLDrawState } from '../../tlstate'
import { mockDocument } from '../../test-helpers'
import { ArrowShape, Decoration, TLDrawShape } from '../../../shape'
describe('Handle command', () => {
const tlstate = new TLDrawState()
it('does, undoes and redoes command', () => {
tlstate
.loadDocument(mockDocument)
.create(
TLDR.getShapeUtils({ type: 'arrow' } as TLDrawShape).create({
id: 'arrow1',
parentId: 'page1',
})
)
.select('arrow1')
expect(tlstate.getShape<ArrowShape>('arrow1').decorations?.end).toBe(Decoration.Arrow)
tlstate.toggleDecoration('end')
expect(tlstate.getShape<ArrowShape>('arrow1').decorations?.end).toBe(undefined)
tlstate.undo()
expect(tlstate.getShape<ArrowShape>('arrow1').decorations?.end).toBe(Decoration.Arrow)
tlstate.redo()
expect(tlstate.getShape<ArrowShape>('arrow1').decorations?.end).toBe(undefined)
})
})

View file

@ -0,0 +1,39 @@
import { Decoration } from '../../../shape'
import type { ArrowShape } from '../../../shape'
import type { Command, Data } from '../../state-types'
import { TLDR } from '../../tldr'
export function toggleDecoration(data: Data, ids: string[], handleId: 'start' | 'end'): Command {
const { before, after } = TLDR.mutateShapes<ArrowShape>(data, ids, (shape) => {
const decorations = shape.decorations
? {
...shape.decorations,
[handleId]: shape.decorations[handleId] ? undefined : Decoration.Arrow,
}
: {
[handleId]: Decoration.Arrow,
}
return {
decorations,
}
})
return {
id: 'toggle_decorations',
before: {
page: {
shapes: {
...before,
},
},
},
after: {
page: {
shapes: {
...after,
},
},
},
}
}

View file

@ -85,9 +85,14 @@ export class ArrowSession implements Session {
const rayPoint = Vec.add(nextPoint, shape.point) const rayPoint = Vec.add(nextPoint, shape.point)
const rayDirection = Vec.uni(Vec.sub(rayPoint, rayOrigin)) const rayDirection = Vec.uni(Vec.sub(rayPoint, rayOrigin))
const oppositeBinding = oppositeHandle.bindingId
? data.page.bindings[oppositeHandle.bindingId]
: undefined
// From all bindable shapes on the page... // From all bindable shapes on the page...
for (const id of this.bindableShapeIds) { for (const id of this.bindableShapeIds) {
if (id === initialShape.id) continue if (id === initialShape.id) continue
if (id === oppositeBinding?.toId) continue
const target = TLDR.getShape(data, id) const target = TLDR.getShape(data, id)

View file

@ -345,29 +345,26 @@ export class TLDR {
this.setSelectedIds(data, []) this.setSelectedIds(data, [])
} }
static mutateShapes( static mutateShapes<T extends TLDrawShape>(
data: Data, data: Data,
ids: string[], ids: string[],
fn: (shape: TLDrawShape, i: number) => Partial<TLDrawShape> fn: (shape: T, i: number) => Partial<T>
): { ): {
before: Record<string, Partial<TLDrawShape>> before: Record<string, Partial<T>>
after: Record<string, Partial<TLDrawShape>> after: Record<string, Partial<T>>
data: Data data: Data
} { } {
const beforeShapes: Record<string, Partial<TLDrawShape>> = {} const beforeShapes: Record<string, Partial<T>> = {}
const afterShapes: Record<string, Partial<TLDrawShape>> = {} const afterShapes: Record<string, Partial<T>> = {}
ids.forEach((id, i) => { ids.forEach((id, i) => {
const shape = data.page.shapes[id] const shape = this.getShape<T>(data, id)
const change = fn(shape, i) const change = fn(shape, i)
beforeShapes[id] = Object.fromEntries( beforeShapes[id] = Object.fromEntries(
Object.keys(change).map((key) => [key, shape[key as keyof TLDrawShape]]) Object.keys(change).map((key) => [key, shape[key as keyof T]])
) as Partial<TLDrawShape> ) as Partial<T>
afterShapes[id] = change afterShapes[id] = change
data.page.shapes[id] = this.getShapeUtils(shape).mutate( data.page.shapes[id] = this.getShapeUtils(shape).mutate(shape as T, change as Partial<T>)
shape as TLDrawShape,
change as Partial<TLDrawShape>
)
}) })
const dataWithChildrenChanges = ids.reduce<Data>((cData, id) => { const dataWithChildrenChanges = ids.reduce<Data>((cData, id) => {

View file

@ -796,6 +796,16 @@ export class TLDrawState implements TLCallbacks {
return this return this
} }
toggleDecoration = (handleId: string, ids?: string[]) => {
if (handleId === 'start' || handleId === 'end') {
const data = this.store.getState()
const idsToMutate = ids ? ids : data.pageState.selectedIds
this.do(commands.toggleDecoration(data, idsToMutate, handleId))
}
return this
}
rotate = (delta = Math.PI * -0.5, ids?: string[]) => { rotate = (delta = Math.PI * -0.5, ids?: string[]) => {
const data = this.store.getState() const data = this.store.getState()
const idsToMutate = ids ? ids : data.pageState.selectedIds const idsToMutate = ids ? ids : data.pageState.selectedIds
@ -1612,8 +1622,8 @@ export class TLDrawState implements TLCallbacks {
this.setStatus('pointingHandle') this.setStatus('pointingHandle')
} }
onDoubleClickHandle: TLPointerEventHandler = () => { onDoubleClickHandle: TLPointerEventHandler = (info) => {
// TODO this.toggleDecoration(info.target)
} }
onRightPointHandle: TLPointerEventHandler = () => { onRightPointHandle: TLPointerEventHandler = () => {