Adds more tests, simplifies draw tool
This commit is contained in:
parent
75e60d5eb2
commit
ff58073d12
17 changed files with 521 additions and 20437 deletions
File diff suppressed because it is too large
Load diff
118
__tests__/__snapshots__/delete.test.ts.snap
Normal file
118
__tests__/__snapshots__/delete.test.ts.snap
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`deletes and restores grouped shapes creates a group: data after mount from file 1`] = `
|
||||||
|
Object {
|
||||||
|
"code": Object {
|
||||||
|
"file0": Object {
|
||||||
|
"code": "",
|
||||||
|
"id": "file0",
|
||||||
|
"name": "index.ts",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"id": "0001",
|
||||||
|
"name": "My Document",
|
||||||
|
"pages": Object {
|
||||||
|
"page1": Object {
|
||||||
|
"childIndex": 0,
|
||||||
|
"id": "page1",
|
||||||
|
"name": "Page 1",
|
||||||
|
"shapes": Object {
|
||||||
|
"1f6c251c-e12e-40b4-8dd2-c1847d80b72f": Object {
|
||||||
|
"childIndex": 24,
|
||||||
|
"id": "1f6c251c-e12e-40b4-8dd2-c1847d80b72f",
|
||||||
|
"isAspectRatioLocked": false,
|
||||||
|
"isGenerated": false,
|
||||||
|
"isHidden": false,
|
||||||
|
"isLocked": false,
|
||||||
|
"name": "Rectangle",
|
||||||
|
"parentId": "page1",
|
||||||
|
"point": Array [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
"radius": 2,
|
||||||
|
"rotation": 0,
|
||||||
|
"seed": 0.6440313303074272,
|
||||||
|
"size": Array [
|
||||||
|
67.22075383450237,
|
||||||
|
72.92795609221832,
|
||||||
|
],
|
||||||
|
"style": Object {
|
||||||
|
"color": "Black",
|
||||||
|
"dash": "Solid",
|
||||||
|
"isFilled": false,
|
||||||
|
"size": "Small",
|
||||||
|
},
|
||||||
|
"type": "rectangle",
|
||||||
|
},
|
||||||
|
"5ca167d7-54de-47c9-aa8f-86affa25e44d": Object {
|
||||||
|
"bend": 0,
|
||||||
|
"childIndex": 16,
|
||||||
|
"decorations": Object {
|
||||||
|
"end": null,
|
||||||
|
"middle": null,
|
||||||
|
"start": null,
|
||||||
|
},
|
||||||
|
"handles": Object {
|
||||||
|
"bend": Object {
|
||||||
|
"id": "bend",
|
||||||
|
"index": 2,
|
||||||
|
"point": Array [
|
||||||
|
3.2518097616315345,
|
||||||
|
140.54510317291172,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"end": Object {
|
||||||
|
"id": "end",
|
||||||
|
"index": 1,
|
||||||
|
"point": Array [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"start": Object {
|
||||||
|
"id": "start",
|
||||||
|
"index": 0,
|
||||||
|
"point": Array [
|
||||||
|
6.503619523263069,
|
||||||
|
281.09020634582345,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"id": "5ca167d7-54de-47c9-aa8f-86affa25e44d",
|
||||||
|
"isAspectRatioLocked": false,
|
||||||
|
"isGenerated": false,
|
||||||
|
"isHidden": false,
|
||||||
|
"isLocked": false,
|
||||||
|
"name": "Arrow",
|
||||||
|
"parentId": "page1",
|
||||||
|
"point": Array [
|
||||||
|
100,
|
||||||
|
100,
|
||||||
|
],
|
||||||
|
"points": Array [
|
||||||
|
Array [
|
||||||
|
6.503619523263069,
|
||||||
|
281.09020634582345,
|
||||||
|
],
|
||||||
|
Array [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
"rotation": 0,
|
||||||
|
"seed": 0.08116783083496548,
|
||||||
|
"style": Object {
|
||||||
|
"color": "Black",
|
||||||
|
"dash": "Solid",
|
||||||
|
"isFilled": false,
|
||||||
|
"size": "Small",
|
||||||
|
},
|
||||||
|
"type": "arrow",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "page",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
File diff suppressed because it is too large
Load diff
|
@ -1,22 +1,33 @@
|
||||||
import state from 'state'
|
import state from 'state'
|
||||||
import inputs from 'state/inputs'
|
import inputs from 'state/inputs'
|
||||||
|
import { ShapeType } from 'types'
|
||||||
import { getShape } from 'utils'
|
import { getShape } from 'utils'
|
||||||
import { idsAreSelected, point, rectangleId } from './test-utils'
|
import {
|
||||||
|
idsAreSelected,
|
||||||
|
point,
|
||||||
|
rectangleId,
|
||||||
|
arrowId,
|
||||||
|
getOnlySelectedShape,
|
||||||
|
assertShapeProps,
|
||||||
|
} from './test-utils'
|
||||||
import * as json from './__mocks__/document.json'
|
import * as json from './__mocks__/document.json'
|
||||||
|
|
||||||
state.reset()
|
describe('deleting single shapes', () => {
|
||||||
state.send('MOUNTED').send('LOADED_FROM_FILE', { json: JSON.stringify(json) })
|
state.reset()
|
||||||
|
state.send('MOUNTED').send('LOADED_FROM_FILE', { json: JSON.stringify(json) })
|
||||||
|
|
||||||
describe('selection', () => {
|
|
||||||
it('deletes a shape and undoes the delete', () => {
|
it('deletes a shape and undoes the delete', () => {
|
||||||
state
|
state
|
||||||
.send('CANCELED')
|
.send('CANCELED')
|
||||||
.send('POINTED_SHAPE', inputs.pointerDown(point(), rectangleId))
|
.send('POINTED_SHAPE', inputs.pointerDown(point(), rectangleId))
|
||||||
.send('STOPPED_POINTING', inputs.pointerUp(point(), rectangleId))
|
.send('STOPPED_POINTING', inputs.pointerUp(point(), rectangleId))
|
||||||
.send('DELETED')
|
|
||||||
|
|
||||||
expect(getShape(state.data, rectangleId)).toBe(undefined)
|
expect(idsAreSelected(state.data, [rectangleId])).toBe(true)
|
||||||
|
|
||||||
|
state.send('DELETED')
|
||||||
|
|
||||||
expect(idsAreSelected(state.data, [])).toBe(true)
|
expect(idsAreSelected(state.data, [])).toBe(true)
|
||||||
|
expect(getShape(state.data, rectangleId)).toBe(undefined)
|
||||||
|
|
||||||
state.send('UNDO')
|
state.send('UNDO')
|
||||||
|
|
||||||
|
@ -26,6 +37,123 @@ describe('selection', () => {
|
||||||
state.send('REDO')
|
state.send('REDO')
|
||||||
|
|
||||||
expect(getShape(state.data, rectangleId)).toBe(undefined)
|
expect(getShape(state.data, rectangleId)).toBe(undefined)
|
||||||
expect(idsAreSelected(state.data, [])).toBe(true)
|
|
||||||
|
state.send('UNDO')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('deletes and restores grouped shapes', () => {
|
||||||
|
state.reset()
|
||||||
|
state.send('MOUNTED').send('LOADED_FROM_FILE', { json: JSON.stringify(json) })
|
||||||
|
|
||||||
|
it('creates a group', () => {
|
||||||
|
expect(state.data.document).toMatchSnapshot('data after mount from file')
|
||||||
|
|
||||||
|
state
|
||||||
|
.send('CANCELED')
|
||||||
|
.send('POINTED_SHAPE', inputs.pointerDown(point(), rectangleId))
|
||||||
|
.send('STOPPED_POINTING', inputs.pointerUp(point(), rectangleId))
|
||||||
|
.send(
|
||||||
|
'POINTED_SHAPE',
|
||||||
|
inputs.pointerDown(point({ shiftKey: true }), arrowId)
|
||||||
|
)
|
||||||
|
.send(
|
||||||
|
'STOPPED_POINTING',
|
||||||
|
inputs.pointerUp(point({ shiftKey: true }), arrowId)
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(idsAreSelected(state.data, [rectangleId, arrowId])).toBe(true)
|
||||||
|
|
||||||
|
state.send('GROUPED')
|
||||||
|
|
||||||
|
const group = getOnlySelectedShape(state.data)
|
||||||
|
|
||||||
|
// Should select the group
|
||||||
|
expect(assertShapeProps(group, { type: ShapeType.Group }))
|
||||||
|
|
||||||
|
const arrow = getShape(state.data, arrowId)
|
||||||
|
|
||||||
|
// The arrow should be have the group as its parent
|
||||||
|
expect(assertShapeProps(arrow, { parentId: group.id }))
|
||||||
|
})
|
||||||
|
|
||||||
|
// it('selects the new group', () => {
|
||||||
|
// expect(idsAreSelected(state.data, [groupId])).toBe(true)
|
||||||
|
// })
|
||||||
|
|
||||||
|
// it('assigns a new parent', () => {
|
||||||
|
// expect(groupId === state.data.currentPageId).toBe(false)
|
||||||
|
// })
|
||||||
|
|
||||||
|
// // Rectangle has the same new parent?
|
||||||
|
// it('assigns new parent to all selected shapes', () => {
|
||||||
|
// expect(hasParent(state.data, arrowId, groupId)).toBe(true)
|
||||||
|
// })
|
||||||
|
|
||||||
|
// // New parent is selected?
|
||||||
|
// it('selects the new parent', () => {
|
||||||
|
// expect(idsAreSelected(state.data, [groupId])).toBe(true)
|
||||||
|
// })
|
||||||
|
})
|
||||||
|
|
||||||
|
// // it('selects the group when pointing a shape', () => {
|
||||||
|
// // state
|
||||||
|
// // .send('CANCELED')
|
||||||
|
// // .send('POINTED_SHAPE', inputs.pointerDown(point(), rectangleId))
|
||||||
|
// // .send('STOPPED_POINTING', inputs.pointerUp(point(), rectangleId))
|
||||||
|
|
||||||
|
// // expect(idsAreSelected(state.data, [groupId])).toBe(true)
|
||||||
|
// // })
|
||||||
|
|
||||||
|
// // it('keeps selection when pointing bounds', () => {
|
||||||
|
// // state
|
||||||
|
// // .send('CANCELED')
|
||||||
|
// // .send('POINTED_BOUNDS', inputs.pointerDown(point(), 'bounds'))
|
||||||
|
// // .send('STOPPED_POINTING', inputs.pointerUp(point(), 'bounds'))
|
||||||
|
|
||||||
|
// // expect(idsAreSelected(state.data, [groupId])).toBe(true)
|
||||||
|
// // })
|
||||||
|
|
||||||
|
// // it('selects a grouped shape by double-pointing', () => {
|
||||||
|
// // state
|
||||||
|
// // .send('CANCELED')
|
||||||
|
// // .send('DOUBLE_POINTED_SHAPE', inputs.pointerDown(point(), rectangleId))
|
||||||
|
// // .send('STOPPED_POINTING', inputs.pointerUp(point(), rectangleId))
|
||||||
|
|
||||||
|
// // expect(idsAreSelected(state.data, [rectangleId])).toBe(true)
|
||||||
|
// // })
|
||||||
|
|
||||||
|
// // it('selects a sibling on point when selecting a grouped shape', () => {
|
||||||
|
// // state
|
||||||
|
// // .send('POINTED_SHAPE', inputs.pointerDown(point(), arrowId))
|
||||||
|
// // .send('STOPPED_POINTING', inputs.pointerUp(point(), arrowId))
|
||||||
|
|
||||||
|
// // expect(idsAreSelected(state.data, [arrowId])).toBe(true)
|
||||||
|
// // })
|
||||||
|
|
||||||
|
// // it('rises up a selection level when escape is pressed', () => {
|
||||||
|
// // state
|
||||||
|
// // .send('CANCELED')
|
||||||
|
// // .send('POINTED_SHAPE', inputs.pointerDown(point(), rectangleId))
|
||||||
|
// // .send('STOPPED_POINTING', inputs.pointerUp(point(), rectangleId))
|
||||||
|
|
||||||
|
// // expect(idsAreSelected(state.data, [groupId])).toBe(true)
|
||||||
|
// // })
|
||||||
|
|
||||||
|
// // it('deletes and restores one shape', () => {
|
||||||
|
// // // Delete the rectangle first
|
||||||
|
// // state.send('UNDO')
|
||||||
|
|
||||||
|
// // expect(getShape(state.data, rectangleId)).toBeTruthy()
|
||||||
|
// // expect(idsAreSelected(state.data, [rectangleId])).toBe(true)
|
||||||
|
|
||||||
|
// // state.send('REDO')
|
||||||
|
|
||||||
|
// // expect(getShape(state.data, rectangleId)).toBe(undefined)
|
||||||
|
|
||||||
|
// // state.send('UNDO')
|
||||||
|
|
||||||
|
// // expect(getShape(state.data, rectangleId)).toBeTruthy()
|
||||||
|
// // expect(idsAreSelected(state.data, [rectangleId])).toBe(true)
|
||||||
|
// // })
|
||||||
|
// })
|
||||||
|
|
|
@ -7,7 +7,6 @@ describe('project', () => {
|
||||||
|
|
||||||
it('mounts the state', () => {
|
it('mounts the state', () => {
|
||||||
state.send('MOUNTED')
|
state.send('MOUNTED')
|
||||||
expect(state.data.document).toMatchSnapshot('data after initial mount')
|
|
||||||
expect(state.isIn('ready')).toBe(true)
|
expect(state.isIn('ready')).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Data } from 'types'
|
import { Data, Shape, ShapeType } from 'types'
|
||||||
import { getSelectedIds } from 'utils'
|
import { getSelectedIds, getSelectedShapes, getShape } from 'utils'
|
||||||
|
|
||||||
export const rectangleId = '1f6c251c-e12e-40b4-8dd2-c1847d80b72f'
|
export const rectangleId = '1f6c251c-e12e-40b4-8dd2-c1847d80b72f'
|
||||||
export const arrowId = '5ca167d7-54de-47c9-aa8f-86affa25e44d'
|
export const arrowId = '5ca167d7-54de-47c9-aa8f-86affa25e44d'
|
||||||
|
@ -47,10 +47,43 @@ export function idsAreSelected(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function asyncDelay<T>(fn: () => T): Promise<T> {
|
export function hasParent(
|
||||||
return new Promise((resolve) => {
|
data: Data,
|
||||||
setTimeout(() => {
|
childId: string,
|
||||||
resolve(fn())
|
parentId: string
|
||||||
}, 100)
|
): boolean {
|
||||||
})
|
return getShape(data, childId).parentId === parentId
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getOnlySelectedShape(data: Data): Shape {
|
||||||
|
const selectedShapes = getSelectedShapes(data)
|
||||||
|
return selectedShapes.length === 1 ? selectedShapes[0] : undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
export function assertShapeType(
|
||||||
|
data: Data,
|
||||||
|
shapeId: string,
|
||||||
|
type: ShapeType
|
||||||
|
): boolean {
|
||||||
|
const shape = getShape(data, shapeId)
|
||||||
|
if (shape.type !== type) {
|
||||||
|
throw new TypeError(
|
||||||
|
`expected shape ${shapeId} to be of type ${type}, found ${shape?.type} instead`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
export function assertShapeProps<T extends Shape>(
|
||||||
|
shape: T,
|
||||||
|
props: { [K in keyof Partial<T>]: T[K] }
|
||||||
|
): boolean {
|
||||||
|
for (const key in props) {
|
||||||
|
if (shape[key] !== props[key]) {
|
||||||
|
throw new TypeError(
|
||||||
|
`expected shape ${shape.id} to have property ${key}: ${props[key]}, found ${key}: ${shape[key]} instead`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import React, { memo } from 'react'
|
||||||
import { useSelector } from 'state'
|
import { useSelector } from 'state'
|
||||||
import { deepCompareArrays, getCurrentCamera, getPage } from 'utils'
|
import { deepCompareArrays, getCurrentCamera, getPage } from 'utils'
|
||||||
import { DotCircle, Handle } from './misc'
|
import { DotCircle, Handle } from './misc'
|
||||||
|
import useShapeDef from 'hooks/useShape'
|
||||||
|
|
||||||
export default function Defs(): JSX.Element {
|
export default function Defs(): JSX.Element {
|
||||||
const zoom = useSelector((s) => getCurrentCamera(s.data).zoom)
|
const zoom = useSelector((s) => getCurrentCamera(s.data).zoom)
|
||||||
|
@ -31,7 +32,7 @@ export default function Defs(): JSX.Element {
|
||||||
}
|
}
|
||||||
|
|
||||||
const Def = memo(function Def({ id }: { id: string }) {
|
const Def = memo(function Def({ id }: { id: string }) {
|
||||||
const shape = useSelector((s) => getPage(s.data).shapes[id])
|
const shape = useShapeDef(id)
|
||||||
|
|
||||||
if (!shape) return null
|
if (!shape) return null
|
||||||
|
|
||||||
|
|
|
@ -24,21 +24,23 @@ export default function useKeyboardEvents() {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const info = getKeyboardEventInfo(e)
|
||||||
|
|
||||||
switch (e.key) {
|
switch (e.key) {
|
||||||
case 'ArrowUp': {
|
case 'ArrowUp': {
|
||||||
state.send('NUDGED', { delta: [0, -1], ...getKeyboardEventInfo(e) })
|
state.send('NUDGED', { delta: [0, -1], ...info })
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'ArrowRight': {
|
case 'ArrowRight': {
|
||||||
state.send('NUDGED', { delta: [1, 0], ...getKeyboardEventInfo(e) })
|
state.send('NUDGED', { delta: [1, 0], ...info })
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'ArrowDown': {
|
case 'ArrowDown': {
|
||||||
state.send('NUDGED', { delta: [0, 1], ...getKeyboardEventInfo(e) })
|
state.send('NUDGED', { delta: [0, 1], ...info })
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'ArrowLeft': {
|
case 'ArrowLeft': {
|
||||||
state.send('NUDGED', { delta: [-1, 0], ...getKeyboardEventInfo(e) })
|
state.send('NUDGED', { delta: [-1, 0], ...info })
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case '=': {
|
case '=': {
|
||||||
|
@ -81,9 +83,9 @@ export default function useKeyboardEvents() {
|
||||||
case 'z': {
|
case 'z': {
|
||||||
if (metaKey(e)) {
|
if (metaKey(e)) {
|
||||||
if (e.shiftKey) {
|
if (e.shiftKey) {
|
||||||
state.send('REDO', getKeyboardEventInfo(e))
|
state.send('REDO', info)
|
||||||
} else {
|
} else {
|
||||||
state.send('UNDO', getKeyboardEventInfo(e))
|
state.send('UNDO', info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
@ -91,7 +93,7 @@ export default function useKeyboardEvents() {
|
||||||
case '‘': {
|
case '‘': {
|
||||||
if (metaKey(e)) {
|
if (metaKey(e)) {
|
||||||
state.send('MOVED', {
|
state.send('MOVED', {
|
||||||
...getKeyboardEventInfo(e),
|
...info,
|
||||||
type: MoveType.ToFront,
|
type: MoveType.ToFront,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -100,7 +102,7 @@ export default function useKeyboardEvents() {
|
||||||
case '“': {
|
case '“': {
|
||||||
if (metaKey(e)) {
|
if (metaKey(e)) {
|
||||||
state.send('MOVED', {
|
state.send('MOVED', {
|
||||||
...getKeyboardEventInfo(e),
|
...info,
|
||||||
type: MoveType.ToBack,
|
type: MoveType.ToBack,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -109,7 +111,7 @@ export default function useKeyboardEvents() {
|
||||||
case ']': {
|
case ']': {
|
||||||
if (metaKey(e)) {
|
if (metaKey(e)) {
|
||||||
state.send('MOVED', {
|
state.send('MOVED', {
|
||||||
...getKeyboardEventInfo(e),
|
...info,
|
||||||
type: MoveType.Forward,
|
type: MoveType.Forward,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -118,30 +120,34 @@ export default function useKeyboardEvents() {
|
||||||
case '[': {
|
case '[': {
|
||||||
if (metaKey(e)) {
|
if (metaKey(e)) {
|
||||||
state.send('MOVED', {
|
state.send('MOVED', {
|
||||||
...getKeyboardEventInfo(e),
|
...info,
|
||||||
type: MoveType.Backward,
|
type: MoveType.Backward,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'Shift': {
|
case 'Shift': {
|
||||||
state.send('PRESSED_SHIFT_KEY', getKeyboardEventInfo(e))
|
state.send('PRESSED_SHIFT_KEY', info)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'Alt': {
|
case 'Alt': {
|
||||||
state.send('PRESSED_ALT_KEY', getKeyboardEventInfo(e))
|
state.send('PRESSED_ALT_KEY', info)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'Backspace': {
|
case 'Backspace': {
|
||||||
state.send('DELETED', getKeyboardEventInfo(e))
|
if (metaKey(e)) {
|
||||||
|
state.send('RESET_PAGE', info)
|
||||||
|
} else {
|
||||||
|
state.send('DELETED', info)
|
||||||
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'g': {
|
case 'g': {
|
||||||
if (metaKey(e)) {
|
if (metaKey(e)) {
|
||||||
if (e.shiftKey) {
|
if (e.shiftKey) {
|
||||||
state.send('UNGROUPED', getKeyboardEventInfo(e))
|
state.send('UNGROUPED', info)
|
||||||
} else {
|
} else {
|
||||||
state.send('GROUPED', getKeyboardEventInfo(e))
|
state.send('GROUPED', info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
@ -149,9 +155,9 @@ export default function useKeyboardEvents() {
|
||||||
case 's': {
|
case 's': {
|
||||||
if (metaKey(e)) {
|
if (metaKey(e)) {
|
||||||
if (e.shiftKey) {
|
if (e.shiftKey) {
|
||||||
state.send('SAVED_AS_TO_FILESYSTEM', getKeyboardEventInfo(e))
|
state.send('SAVED_AS_TO_FILESYSTEM', info)
|
||||||
} else {
|
} else {
|
||||||
state.send('SAVED', getKeyboardEventInfo(e))
|
state.send('SAVED', info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
@ -160,47 +166,47 @@ export default function useKeyboardEvents() {
|
||||||
if (metaKey(e)) {
|
if (metaKey(e)) {
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
state.send('SELECTED_DOT_TOOL', getKeyboardEventInfo(e))
|
state.send('SELECTED_DOT_TOOL', info)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'v': {
|
case 'v': {
|
||||||
if (metaKey(e)) {
|
if (metaKey(e)) {
|
||||||
state.send('PASTED', getKeyboardEventInfo(e))
|
state.send('PASTED', info)
|
||||||
} else {
|
} else {
|
||||||
state.send('SELECTED_SELECT_TOOL', getKeyboardEventInfo(e))
|
state.send('SELECTED_SELECT_TOOL', info)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'a': {
|
case 'a': {
|
||||||
if (metaKey(e)) {
|
if (metaKey(e)) {
|
||||||
state.send('SELECTED_ALL', getKeyboardEventInfo(e))
|
state.send('SELECTED_ALL', info)
|
||||||
} else {
|
} else {
|
||||||
state.send('SELECTED_ARROW_TOOL', getKeyboardEventInfo(e))
|
state.send('SELECTED_ARROW_TOOL', info)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'd': {
|
case 'd': {
|
||||||
if (metaKey(e)) {
|
if (metaKey(e)) {
|
||||||
state.send('DUPLICATED', getKeyboardEventInfo(e))
|
state.send('DUPLICATED', info)
|
||||||
} else {
|
} else {
|
||||||
state.send('SELECTED_DRAW_TOOL', getKeyboardEventInfo(e))
|
state.send('SELECTED_DRAW_TOOL', info)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 't': {
|
case 't': {
|
||||||
state.send('SELECTED_TEXT_TOOL', getKeyboardEventInfo(e))
|
state.send('SELECTED_TEXT_TOOL', info)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'c': {
|
case 'c': {
|
||||||
if (metaKey(e)) {
|
if (metaKey(e)) {
|
||||||
if (e.shiftKey) {
|
if (e.shiftKey) {
|
||||||
state.send('COPIED_TO_SVG', getKeyboardEventInfo(e))
|
state.send('COPIED_TO_SVG', info)
|
||||||
} else {
|
} else {
|
||||||
state.send('COPIED', getKeyboardEventInfo(e))
|
state.send('COPIED', info)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
state.send('SELECTED_ELLIPSE_TOOL', getKeyboardEventInfo(e))
|
state.send('SELECTED_ELLIPSE_TOOL', info)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -208,17 +214,17 @@ export default function useKeyboardEvents() {
|
||||||
if (metaKey(e)) {
|
if (metaKey(e)) {
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
state.send('SELECTED_CIRCLE_TOOL', getKeyboardEventInfo(e))
|
state.send('SELECTED_CIRCLE_TOOL', info)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'l': {
|
case 'l': {
|
||||||
if (metaKey(e)) {
|
if (metaKey(e)) {
|
||||||
if (e.shiftKey) {
|
if (e.shiftKey) {
|
||||||
state.send('LOADED_FROM_FILE_STSTEM', getKeyboardEventInfo(e))
|
state.send('LOADED_FROM_FILE_STSTEM', info)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
state.send('SELECTED_LINE_TOOL', getKeyboardEventInfo(e))
|
state.send('SELECTED_LINE_TOOL', info)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -226,7 +232,7 @@ export default function useKeyboardEvents() {
|
||||||
if (metaKey(e)) {
|
if (metaKey(e)) {
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
state.send('SELECTED_RAY_TOOL', getKeyboardEventInfo(e))
|
state.send('SELECTED_RAY_TOOL', info)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -234,7 +240,7 @@ export default function useKeyboardEvents() {
|
||||||
if (metaKey(e)) {
|
if (metaKey(e)) {
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
state.send('SELECTED_POLYLINE_TOOL', getKeyboardEventInfo(e))
|
state.send('SELECTED_POLYLINE_TOOL', info)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -242,7 +248,7 @@ export default function useKeyboardEvents() {
|
||||||
if (metaKey(e)) {
|
if (metaKey(e)) {
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
state.send('SELECTED_RECTANGLE_TOOL', getKeyboardEventInfo(e))
|
state.send('SELECTED_RECTANGLE_TOOL', info)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -251,21 +257,23 @@ export default function useKeyboardEvents() {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
state.send('PRESSED_KEY', getKeyboardEventInfo(e))
|
state.send('PRESSED_KEY', info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleKeyUp(e: KeyboardEvent) {
|
function handleKeyUp(e: KeyboardEvent) {
|
||||||
|
const info = getKeyboardEventInfo(e)
|
||||||
|
|
||||||
if (e.key === 'Shift') {
|
if (e.key === 'Shift') {
|
||||||
state.send('RELEASED_SHIFT_KEY', getKeyboardEventInfo(e))
|
state.send('RELEASED_SHIFT_KEY', info)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.key === 'Alt') {
|
if (e.key === 'Alt') {
|
||||||
state.send('RELEASED_ALT_KEY', getKeyboardEventInfo(e))
|
state.send('RELEASED_ALT_KEY', info)
|
||||||
}
|
}
|
||||||
|
|
||||||
state.send('RELEASED_KEY', getKeyboardEventInfo(e))
|
state.send('RELEASED_KEY', info)
|
||||||
}
|
}
|
||||||
|
|
||||||
document.body.addEventListener('keydown', handleKeyDown)
|
document.body.addEventListener('keydown', handleKeyDown)
|
||||||
|
|
20
hooks/useShape.ts
Normal file
20
hooks/useShape.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||||
|
import { useSelector } from 'state'
|
||||||
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
|
import { getShape } from 'utils'
|
||||||
|
|
||||||
|
export default function useShapeDef(id: string) {
|
||||||
|
return useSelector(
|
||||||
|
(s) => getShape(s.data, id),
|
||||||
|
(prev, next) => {
|
||||||
|
const shouldSkip = !(
|
||||||
|
prev &&
|
||||||
|
next &&
|
||||||
|
next !== prev &&
|
||||||
|
getShapeUtils(next).shouldRender(next, prev)
|
||||||
|
)
|
||||||
|
|
||||||
|
return shouldSkip
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
|
@ -12,7 +12,7 @@
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"test-all": "yarn lint && yarn type-check && yarn test",
|
"test-all": "yarn lint && yarn type-check && yarn test",
|
||||||
"test:update": "jest --updateSnapshot",
|
"test:update": "jest --updateSnapshot",
|
||||||
"test:watch": "jest --watchAll",
|
"test:watch": "jest --watchAll --verbose=false --silent=false",
|
||||||
"test": "jest --watchAll=false",
|
"test": "jest --watchAll=false",
|
||||||
"type-check": "tsc --pretty --noEmit"
|
"type-check": "tsc --pretty --noEmit"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import Command from './command'
|
import Command from './command'
|
||||||
import history from '../history'
|
import history from '../history'
|
||||||
import { Data } from 'types'
|
import { Data, Shape } from 'types'
|
||||||
import {
|
import {
|
||||||
deepClone,
|
deepClone,
|
||||||
getDocumentBranch,
|
getDocumentBranch,
|
||||||
|
@ -17,16 +17,16 @@ export default function deleteSelected(data: Data): void {
|
||||||
.filter((shape) => !shape.isLocked)
|
.filter((shape) => !shape.isLocked)
|
||||||
.map((shape) => shape.id)
|
.map((shape) => shape.id)
|
||||||
|
|
||||||
const page = getPage(data)
|
const shapeIdsToDelete = selectedIdsArr.flatMap((id) =>
|
||||||
|
getDocumentBranch(data, id)
|
||||||
const childrenToDelete = selectedIdsArr
|
)
|
||||||
.flatMap((id) => getDocumentBranch(data, id))
|
|
||||||
.map((id) => deepClone(page.shapes[id]))
|
|
||||||
|
|
||||||
const remainingIds = selectedShapes
|
const remainingIds = selectedShapes
|
||||||
.filter((shape) => shape.isLocked)
|
.filter((shape) => shape.isLocked)
|
||||||
.map((shape) => shape.id)
|
.map((shape) => shape.id)
|
||||||
|
|
||||||
|
let deletedShapes: Shape[] = []
|
||||||
|
|
||||||
history.execute(
|
history.execute(
|
||||||
data,
|
data,
|
||||||
new Command({
|
new Command({
|
||||||
|
@ -34,57 +34,83 @@ export default function deleteSelected(data: Data): void {
|
||||||
category: 'canvas',
|
category: 'canvas',
|
||||||
manualSelection: true,
|
manualSelection: true,
|
||||||
do(data) {
|
do(data) {
|
||||||
const page = getPage(data)
|
// Update selected ids
|
||||||
|
|
||||||
for (const id of selectedIdsArr) {
|
|
||||||
const shape = page.shapes[id]
|
|
||||||
if (!shape) {
|
|
||||||
console.error('no shape ' + id)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shape.parentId !== data.currentPageId) {
|
|
||||||
const parent = page.shapes[shape.parentId]
|
|
||||||
getShapeUtils(parent)
|
|
||||||
.setProperty(
|
|
||||||
parent,
|
|
||||||
'children',
|
|
||||||
parent.children.filter((childId) => childId !== shape.id)
|
|
||||||
)
|
|
||||||
.onChildrenChange(
|
|
||||||
parent,
|
|
||||||
parent.children.map((id) => page.shapes[id])
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const shape of childrenToDelete) {
|
|
||||||
delete page.shapes[shape.id]
|
|
||||||
}
|
|
||||||
|
|
||||||
setSelectedIds(data, remainingIds)
|
setSelectedIds(data, remainingIds)
|
||||||
|
|
||||||
|
// Recursively delete shapes (and maybe their parents too)
|
||||||
|
deletedShapes = deleteShapes(data, shapeIdsToDelete)
|
||||||
},
|
},
|
||||||
undo(data) {
|
undo(data) {
|
||||||
const page = getPage(data)
|
const page = getPage(data)
|
||||||
|
|
||||||
for (const shape of childrenToDelete) {
|
// Update selected ids
|
||||||
page.shapes[shape.id] = shape
|
setSelectedIds(data, selectedIdsArr)
|
||||||
}
|
|
||||||
|
// Restore deleted shapes
|
||||||
|
deletedShapes.forEach((shape) => (page.shapes[shape.id] = shape))
|
||||||
|
|
||||||
|
// Update parents
|
||||||
|
deletedShapes.forEach((shape) => {
|
||||||
|
if (shape.parentId === data.currentPageId) return
|
||||||
|
|
||||||
for (const shape of childrenToDelete) {
|
|
||||||
if (shape.parentId !== data.currentPageId) {
|
|
||||||
const parent = page.shapes[shape.parentId]
|
const parent = page.shapes[shape.parentId]
|
||||||
|
|
||||||
getShapeUtils(parent)
|
getShapeUtils(parent)
|
||||||
.setProperty(parent, 'children', [...parent.children, shape.id])
|
.setProperty(parent, 'children', [...parent.children, shape.id])
|
||||||
.onChildrenChange(
|
.onChildrenChange(
|
||||||
parent,
|
parent,
|
||||||
parent.children.map((id) => page.shapes[id])
|
parent.children.map((id) => page.shapes[id])
|
||||||
)
|
)
|
||||||
}
|
})
|
||||||
}
|
|
||||||
|
|
||||||
setSelectedIds(data, selectedIdsArr)
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Recursively delete shapes and their parents */
|
||||||
|
|
||||||
|
function deleteShapes(
|
||||||
|
data: Data,
|
||||||
|
shapeIds: string[],
|
||||||
|
shapesDeleted: Shape[] = []
|
||||||
|
): Shape[] {
|
||||||
|
const parentsToDelete: string[] = []
|
||||||
|
|
||||||
|
const page = getPage(data)
|
||||||
|
|
||||||
|
const parentIds = new Set(shapeIds.map((id) => page.shapes[id].parentId))
|
||||||
|
|
||||||
|
// Delete shapes
|
||||||
|
shapeIds.forEach((id) => {
|
||||||
|
shapesDeleted.push(deepClone(page.shapes[id]))
|
||||||
|
delete page.shapes[id]
|
||||||
|
})
|
||||||
|
|
||||||
|
// Update parents
|
||||||
|
parentIds.forEach((id) => {
|
||||||
|
const parent = page.shapes[id]
|
||||||
|
|
||||||
|
if (!parent || id === page.id) return
|
||||||
|
|
||||||
|
getShapeUtils(parent)
|
||||||
|
.setProperty(
|
||||||
|
parent,
|
||||||
|
'children',
|
||||||
|
parent.children.filter((childId) => !shapeIds.includes(childId))
|
||||||
|
)
|
||||||
|
.onChildrenChange(
|
||||||
|
parent,
|
||||||
|
parent.children.map((id) => page.shapes[id])
|
||||||
|
)
|
||||||
|
|
||||||
|
if (getShapeUtils(parent).shouldDelete(parent)) {
|
||||||
|
parentsToDelete.push(parent.id)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (parentsToDelete.length > 0) {
|
||||||
|
return deleteShapes(data, parentsToDelete, shapesDeleted)
|
||||||
|
}
|
||||||
|
|
||||||
|
return shapesDeleted
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { current } from 'immer'
|
||||||
import { Data, DrawShape } from 'types'
|
import { Data, DrawShape } from 'types'
|
||||||
import BaseSession from './base-session'
|
import BaseSession from './base-session'
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
import { getPage, getShape, updateParents } from 'utils'
|
import { getBoundsFromPoints, getPage, getShape, updateParents } from 'utils'
|
||||||
import vec from 'utils/vec'
|
import vec from 'utils/vec'
|
||||||
import commands from 'state/commands'
|
import commands from 'state/commands'
|
||||||
export default class BrushSession extends BaseSession {
|
export default class BrushSession extends BaseSession {
|
||||||
|
@ -14,13 +14,12 @@ export default class BrushSession extends BaseSession {
|
||||||
isLocked: boolean
|
isLocked: boolean
|
||||||
lockedDirection: 'horizontal' | 'vertical'
|
lockedDirection: 'horizontal' | 'vertical'
|
||||||
|
|
||||||
constructor(data: Data, id: string, point: number[], isLocked = false) {
|
constructor(data: Data, id: string, point: number[]) {
|
||||||
super(data)
|
super(data)
|
||||||
this.origin = point
|
this.origin = point
|
||||||
this.previous = point
|
this.previous = point
|
||||||
this.last = point
|
this.last = point
|
||||||
this.snapshot = getDrawSnapshot(data, id)
|
this.snapshot = getDrawSnapshot(data, id)
|
||||||
isLocked
|
|
||||||
|
|
||||||
// Add a first point but don't update the shape yet. We'll update
|
// Add a first point but don't update the shape yet. We'll update
|
||||||
// when the draw session ends; if the user hasn't added additional
|
// when the draw session ends; if the user hasn't added additional
|
||||||
|
@ -42,18 +41,18 @@ export default class BrushSession extends BaseSession {
|
||||||
): void => {
|
): void => {
|
||||||
const { snapshot } = this
|
const { snapshot } = this
|
||||||
|
|
||||||
const delta = vec.vec(this.origin, point)
|
|
||||||
|
|
||||||
// Drawing while holding shift will "lock" the pen to either the
|
// Drawing while holding shift will "lock" the pen to either the
|
||||||
// x or y axis, depending on which direction has the greater
|
// x or y axis, depending on which direction has the greater
|
||||||
// delta. Pressing shift will also add more points to "return"
|
// delta. Pressing shift will also add more points to "return"
|
||||||
// the pen to the axis.
|
// the pen to the axis.
|
||||||
if (isLocked) {
|
if (isLocked) {
|
||||||
if (!this.isLocked && this.points.length > 1) {
|
if (!this.isLocked && this.points.length > 1) {
|
||||||
|
const bounds = getBoundsFromPoints(this.points)
|
||||||
|
if (bounds.width > 8 || bounds.height > 8) {
|
||||||
this.isLocked = true
|
this.isLocked = true
|
||||||
const returning = [...this.previous]
|
const returning = [...this.previous]
|
||||||
|
|
||||||
const isVertical = Math.abs(delta[0]) < Math.abs(delta[1])
|
const isVertical = bounds.height > 8
|
||||||
|
|
||||||
if (isVertical) {
|
if (isVertical) {
|
||||||
this.lockedDirection = 'vertical'
|
this.lockedDirection = 'vertical'
|
||||||
|
@ -66,6 +65,7 @@ export default class BrushSession extends BaseSession {
|
||||||
this.previous = returning
|
this.previous = returning
|
||||||
this.points.push(vec.sub(returning, this.origin))
|
this.points.push(vec.sub(returning, this.origin))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else if (this.isLocked) {
|
} else if (this.isLocked) {
|
||||||
this.isLocked = false
|
this.isLocked = false
|
||||||
}
|
}
|
||||||
|
@ -104,19 +104,7 @@ export default class BrushSession extends BaseSession {
|
||||||
// Update the points and update the shape's parents.
|
// Update the points and update the shape's parents.
|
||||||
const shape = getShape(data, snapshot.id) as DrawShape
|
const shape = getShape(data, snapshot.id) as DrawShape
|
||||||
|
|
||||||
// Offset the points and shapes to avoid negative numbers
|
|
||||||
const ox = Math.min(newPoint[0], 0)
|
|
||||||
const oy = Math.min(newPoint[1], 0)
|
|
||||||
|
|
||||||
if (ox < 0 || oy < 0) {
|
|
||||||
const offset = [ox, oy]
|
|
||||||
this.points = this.points.map((pt) => [...vec.sub(pt, offset), pt[2]])
|
|
||||||
this.origin = vec.add(this.origin, offset)
|
|
||||||
getShapeUtils(shape).translateBy(shape, offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
getShapeUtils(shape).setProperty(shape, 'points', [...this.points])
|
getShapeUtils(shape).setProperty(shape, 'points', [...this.points])
|
||||||
|
|
||||||
updateParents(data, [shape.id])
|
updateParents(data, [shape.id])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,8 @@ const polygonCache = new WeakMap<DrawShape['points'], string>([])
|
||||||
const draw = registerShapeUtils<DrawShape>({
|
const draw = registerShapeUtils<DrawShape>({
|
||||||
boundsCache: new WeakMap([]),
|
boundsCache: new WeakMap([]),
|
||||||
|
|
||||||
|
canStyleFill: true,
|
||||||
|
|
||||||
create(props) {
|
create(props) {
|
||||||
return {
|
return {
|
||||||
id: uniqueId(),
|
id: uniqueId(),
|
||||||
|
@ -43,6 +45,11 @@ const draw = registerShapeUtils<DrawShape>({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
shouldRender(shape, prev) {
|
||||||
|
// return true
|
||||||
|
return shape.points !== prev.points || shape.style !== prev.style
|
||||||
|
},
|
||||||
|
|
||||||
render(shape) {
|
render(shape) {
|
||||||
const { id, points, style } = shape
|
const { id, points, style } = shape
|
||||||
|
|
||||||
|
@ -171,7 +178,6 @@ const draw = registerShapeUtils<DrawShape>({
|
||||||
const styles = { ...shape.style, ...style }
|
const styles = { ...shape.style, ...style }
|
||||||
styles.dash = DashStyle.Solid
|
styles.dash = DashStyle.Solid
|
||||||
shape.style = styles
|
shape.style = styles
|
||||||
shape.points = [...shape.points]
|
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -186,8 +192,6 @@ const draw = registerShapeUtils<DrawShape>({
|
||||||
|
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
|
|
||||||
canStyleFill: true,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export default draw
|
export default draw
|
||||||
|
|
|
@ -108,6 +108,8 @@ const group = registerShapeUtils<GroupShape>({
|
||||||
},
|
},
|
||||||
|
|
||||||
onChildrenChange(shape, children) {
|
onChildrenChange(shape, children) {
|
||||||
|
if (shape.children.length === 0) return
|
||||||
|
|
||||||
const childBounds = getCommonBounds(
|
const childBounds = getCommonBounds(
|
||||||
...children.map((child) => getShapeUtils(child).getRotatedBounds(child))
|
...children.map((child) => getShapeUtils(child).getRotatedBounds(child))
|
||||||
)
|
)
|
||||||
|
|
|
@ -155,6 +155,10 @@ function getDefaultShapeUtil<T extends Shape>(): ShapeUtility<T> {
|
||||||
this.boundsCache.delete(shape)
|
this.boundsCache.delete(shape)
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
|
|
||||||
|
shouldRender() {
|
||||||
|
return true
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -173,6 +173,7 @@ const state = createState({
|
||||||
else: ['zoomCameraToActual'],
|
else: ['zoomCameraToActual'],
|
||||||
},
|
},
|
||||||
on: {
|
on: {
|
||||||
|
RESET_PAGE: 'resetPage',
|
||||||
TOGGLED_READ_ONLY: 'toggleReadOnly',
|
TOGGLED_READ_ONLY: 'toggleReadOnly',
|
||||||
LOADED_FONTS: 'resetShapes',
|
LOADED_FONTS: 'resetShapes',
|
||||||
USED_PEN_DEVICE: 'enablePenLock',
|
USED_PEN_DEVICE: 'enablePenLock',
|
||||||
|
@ -1342,14 +1343,13 @@ const state = createState({
|
||||||
},
|
},
|
||||||
|
|
||||||
// Drawing
|
// Drawing
|
||||||
startDrawSession(data, payload: PointerInfo) {
|
startDrawSession(data) {
|
||||||
const id = Array.from(getSelectedIds(data).values())[0]
|
const id = Array.from(getSelectedIds(data).values())[0]
|
||||||
session.begin(
|
session.begin(
|
||||||
new Sessions.DrawSession(
|
new Sessions.DrawSession(
|
||||||
data,
|
data,
|
||||||
id,
|
id,
|
||||||
screenToWorld(inputs.pointer.origin, data),
|
screenToWorld(inputs.pointer.origin, data)
|
||||||
payload.shiftKey
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -1501,6 +1501,9 @@ const state = createState({
|
||||||
resetShapeBounds(data) {
|
resetShapeBounds(data) {
|
||||||
commands.resetBounds(data)
|
commands.resetBounds(data)
|
||||||
},
|
},
|
||||||
|
resetPage(data) {
|
||||||
|
data.document.pages[data.currentPageId].shapes = {}
|
||||||
|
},
|
||||||
|
|
||||||
/* --------------------- Editing -------------------- */
|
/* --------------------- Editing -------------------- */
|
||||||
|
|
||||||
|
|
4
types.ts
4
types.ts
|
@ -588,5 +588,9 @@ export interface ShapeUtility<K extends Shape> {
|
||||||
// Test whether bounds collide with or contain a shape.
|
// Test whether bounds collide with or contain a shape.
|
||||||
hitTestBounds(this: ShapeUtility<K>, shape: K, bounds: Bounds): boolean
|
hitTestBounds(this: ShapeUtility<K>, shape: K, bounds: Bounds): boolean
|
||||||
|
|
||||||
|
// Get whether the shape should delete
|
||||||
shouldDelete(this: ShapeUtility<K>, shape: K): boolean
|
shouldDelete(this: ShapeUtility<K>, shape: K): boolean
|
||||||
|
|
||||||
|
// Get whether the shape should render
|
||||||
|
shouldRender(this: ShapeUtility<K>, shape: K, previous: K): boolean
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue