Fixes events with shapes, adds test for selection
This commit is contained in:
parent
e265a85d7b
commit
d5fe5612e1
13 changed files with 13504 additions and 81 deletions
13287
__tests__/__snapshots__/project.test.ts.snap
Normal file
13287
__tests__/__snapshots__/project.test.ts.snap
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,56 +1,19 @@
|
||||||
import * as json from './__mocks__/document.json'
|
|
||||||
import state from 'state'
|
import state from 'state'
|
||||||
import { point } from './test-utils'
|
import * as json from './__mocks__/document.json'
|
||||||
import inputs from 'state/inputs'
|
|
||||||
import { getSelectedIds, setToArray } from 'utils/utils'
|
|
||||||
|
|
||||||
const rectangleId = '1f6c251c-e12e-40b4-8dd2-c1847d80b72f'
|
|
||||||
const arrowId = '5ca167d7-54de-47c9-aa8f-86affa25e44d'
|
|
||||||
|
|
||||||
describe('project', () => {
|
describe('project', () => {
|
||||||
|
state.reset()
|
||||||
|
state.enableLog(true)
|
||||||
|
|
||||||
it('mounts the state', () => {
|
it('mounts the state', () => {
|
||||||
state.enableLog(true)
|
state.send('MOUNTED')
|
||||||
|
expect(state.data.document).toMatchSnapshot('data after initial mount')
|
||||||
state
|
expect(state.isIn('ready')).toBe(true)
|
||||||
.send('MOUNTED')
|
|
||||||
.send('LOADED_FROM_FILE', { json: JSON.stringify(json) })
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('selects and deselects a shape', () => {
|
it('loads file from json', () => {
|
||||||
expect(setToArray(getSelectedIds(state.data))).toStrictEqual([])
|
state.send('LOADED_FROM_FILE', { json: JSON.stringify(json) })
|
||||||
|
expect(state.isIn('ready')).toBe(true)
|
||||||
state
|
expect(state.data.document).toMatchSnapshot('data after mount from file')
|
||||||
.send('POINTED_SHAPE', inputs.pointerDown(point(), rectangleId))
|
|
||||||
.send('STOPPED_POINTING', inputs.pointerUp(point()))
|
|
||||||
|
|
||||||
expect(setToArray(getSelectedIds(state.data))).toStrictEqual([rectangleId])
|
|
||||||
|
|
||||||
state
|
|
||||||
.send('POINTED_CANVAS', inputs.pointerDown(point(), 'canvas'))
|
|
||||||
.send('STOPPED_POINTING', inputs.pointerUp(point()))
|
|
||||||
|
|
||||||
expect(setToArray(getSelectedIds(state.data))).toStrictEqual([])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('selects multiple shapes', () => {
|
|
||||||
expect(setToArray(getSelectedIds(state.data))).toStrictEqual([])
|
|
||||||
|
|
||||||
state
|
|
||||||
.send('POINTED_SHAPE', inputs.pointerDown(point(), rectangleId))
|
|
||||||
.send('STOPPED_POINTING', inputs.pointerUp(point()))
|
|
||||||
|
|
||||||
expect(setToArray(getSelectedIds(state.data))).toStrictEqual([rectangleId])
|
|
||||||
|
|
||||||
state.send(
|
|
||||||
'POINTED_SHAPE',
|
|
||||||
inputs.pointerDown(point({ shiftKey: true }), arrowId)
|
|
||||||
)
|
|
||||||
|
|
||||||
// state.send('STOPPED_POINTING', inputs.pointerUp(point()))
|
|
||||||
|
|
||||||
expect(setToArray(getSelectedIds(state.data))).toStrictEqual([
|
|
||||||
rectangleId,
|
|
||||||
arrowId,
|
|
||||||
])
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
142
__tests__/selection.test.ts
Normal file
142
__tests__/selection.test.ts
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
import state from 'state'
|
||||||
|
import inputs from 'state/inputs'
|
||||||
|
import { idsAreSelected, point } from './test-utils'
|
||||||
|
import * as json from './__mocks__/document.json'
|
||||||
|
|
||||||
|
const rectangleId = '1f6c251c-e12e-40b4-8dd2-c1847d80b72f'
|
||||||
|
const arrowId = '5ca167d7-54de-47c9-aa8f-86affa25e44d'
|
||||||
|
|
||||||
|
// Mount the state and load the test file from json
|
||||||
|
state.reset()
|
||||||
|
state.send('MOUNTED').send('LOADED_FROM_FILE', { json: JSON.stringify(json) })
|
||||||
|
|
||||||
|
describe('selection', () => {
|
||||||
|
it('selects a shape', () => {
|
||||||
|
state
|
||||||
|
.send('CANCELED')
|
||||||
|
.send('POINTED_SHAPE', inputs.pointerDown(point(), rectangleId))
|
||||||
|
.send('STOPPED_POINTING', inputs.pointerUp(point(), rectangleId))
|
||||||
|
|
||||||
|
expect(idsAreSelected(state.data, [rectangleId])).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('selects and deselects a shape', () => {
|
||||||
|
state
|
||||||
|
.send('CANCELED')
|
||||||
|
.send('POINTED_SHAPE', inputs.pointerDown(point(), rectangleId))
|
||||||
|
.send('STOPPED_POINTING', inputs.pointerUp(point(), rectangleId))
|
||||||
|
|
||||||
|
expect(idsAreSelected(state.data, [rectangleId])).toBe(true)
|
||||||
|
|
||||||
|
state
|
||||||
|
.send('POINTED_CANVAS', inputs.pointerDown(point(), 'canvas'))
|
||||||
|
.send('STOPPED_POINTING', inputs.pointerUp(point(), 'canvas'))
|
||||||
|
|
||||||
|
expect(idsAreSelected(state.data, [])).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('selects multiple shapes', () => {
|
||||||
|
expect(idsAreSelected(state.data, [])).toBe(true)
|
||||||
|
|
||||||
|
state
|
||||||
|
.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)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shift-selects to deselect shapes', () => {
|
||||||
|
state
|
||||||
|
.send('CANCELLED')
|
||||||
|
.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)
|
||||||
|
)
|
||||||
|
.send(
|
||||||
|
'POINTED_SHAPE',
|
||||||
|
inputs.pointerDown(point({ shiftKey: true }), rectangleId)
|
||||||
|
)
|
||||||
|
.send(
|
||||||
|
'STOPPED_POINTING',
|
||||||
|
inputs.pointerUp(point({ shiftKey: true }), rectangleId)
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(idsAreSelected(state.data, [arrowId])).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('single-selects shape in selection on pointerup', () => {
|
||||||
|
state
|
||||||
|
.send('CANCELLED')
|
||||||
|
.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('POINTED_SHAPE', inputs.pointerDown(point(), arrowId))
|
||||||
|
|
||||||
|
expect(idsAreSelected(state.data, [rectangleId, arrowId])).toBe(true)
|
||||||
|
|
||||||
|
state.send('STOPPED_POINTING', inputs.pointerUp(point(), arrowId))
|
||||||
|
|
||||||
|
expect(idsAreSelected(state.data, [arrowId])).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('selects shapes if shift key is lifted before pointerup', () => {
|
||||||
|
state
|
||||||
|
.send('CANCELLED')
|
||||||
|
.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)
|
||||||
|
)
|
||||||
|
.send(
|
||||||
|
'POINTED_SHAPE',
|
||||||
|
inputs.pointerDown(point({ shiftKey: true }), arrowId)
|
||||||
|
)
|
||||||
|
.send('STOPPED_POINTING', inputs.pointerUp(point(), arrowId))
|
||||||
|
|
||||||
|
expect(idsAreSelected(state.data, [arrowId])).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('does not select on meta-click', () => {
|
||||||
|
state
|
||||||
|
.send('CANCELLED')
|
||||||
|
.send(
|
||||||
|
'POINTED_SHAPE',
|
||||||
|
inputs.pointerDown(point({ ctrlKey: true }), rectangleId)
|
||||||
|
)
|
||||||
|
.send(
|
||||||
|
'STOPPED_POINTING',
|
||||||
|
inputs.pointerUp(point({ ctrlKey: true }), rectangleId)
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(idsAreSelected(state.data, [])).toBe(true)
|
||||||
|
})
|
||||||
|
})
|
|
@ -1,10 +1,13 @@
|
||||||
|
import { Data } from 'types'
|
||||||
|
import { getSelectedIds } from 'utils/utils'
|
||||||
|
|
||||||
interface PointerOptions {
|
interface PointerOptions {
|
||||||
id?: string
|
id?: string
|
||||||
x?: number
|
x?: number
|
||||||
y?: number
|
y?: number
|
||||||
shiftKey?: boolean
|
shiftKey?: boolean
|
||||||
altKey?: boolean
|
altKey?: boolean
|
||||||
metaKey?: boolean
|
ctrlKey?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export function point(
|
export function point(
|
||||||
|
@ -16,15 +19,27 @@ export function point(
|
||||||
y = 0,
|
y = 0,
|
||||||
shiftKey = false,
|
shiftKey = false,
|
||||||
altKey = false,
|
altKey = false,
|
||||||
metaKey = false,
|
ctrlKey = false,
|
||||||
} = options
|
} = options
|
||||||
|
|
||||||
return {
|
return {
|
||||||
shiftKey,
|
shiftKey,
|
||||||
altKey,
|
altKey,
|
||||||
metaKey,
|
ctrlKey,
|
||||||
pointerId: id,
|
pointerId: id,
|
||||||
clientX: x,
|
clientX: x,
|
||||||
clientY: y,
|
clientY: y,
|
||||||
} as any
|
} as any
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function idsAreSelected(
|
||||||
|
data: Data,
|
||||||
|
ids: string[],
|
||||||
|
strict = true
|
||||||
|
): boolean {
|
||||||
|
const selectedIds = getSelectedIds(data)
|
||||||
|
return (
|
||||||
|
(strict ? selectedIds.size === ids.length : true) &&
|
||||||
|
ids.every((id) => selectedIds.has(id))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ function handlePointerUp(e: React.PointerEvent<SVGRectElement>) {
|
||||||
if (!inputs.canAccept(e.pointerId)) return
|
if (!inputs.canAccept(e.pointerId)) return
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
e.currentTarget.releasePointerCapture(e.pointerId)
|
e.currentTarget.releasePointerCapture(e.pointerId)
|
||||||
state.send('STOPPED_POINTING', inputs.pointerUp(e))
|
state.send('STOPPED_POINTING', inputs.pointerUp(e, 'bounds'))
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function BoundsBg(): JSX.Element {
|
export default function BoundsBg(): JSX.Element {
|
||||||
|
|
|
@ -39,6 +39,6 @@ const Def = memo(function Def({ id }: { id: string }) {
|
||||||
|
|
||||||
return React.cloneElement(
|
return React.cloneElement(
|
||||||
getShapeUtils(shape).render(shape, { isEditing: false }),
|
getShapeUtils(shape).render(shape, { isEditing: false }),
|
||||||
{ ...style }
|
{ id, ...style }
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React, { useRef, memo, useEffect } from 'react'
|
||||||
import { useSelector } from 'state'
|
import { useSelector } from 'state'
|
||||||
import styled from 'styles'
|
import styled from 'styles'
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
import { getPage, isMobile } from 'utils/utils'
|
import { getPage, getSelectedIds, isMobile } from 'utils/utils'
|
||||||
import { Shape as _Shape } from 'types'
|
import { Shape as _Shape } from 'types'
|
||||||
import useShapeEvents from 'hooks/useShapeEvents'
|
import useShapeEvents from 'hooks/useShapeEvents'
|
||||||
import vec from 'utils/vec'
|
import vec from 'utils/vec'
|
||||||
|
@ -22,6 +22,8 @@ function Shape({ id, isSelecting, parentPoint }: ShapeProps): JSX.Element {
|
||||||
|
|
||||||
const isEditing = useSelector((s) => s.data.editingId === id)
|
const isEditing = useSelector((s) => s.data.editingId === id)
|
||||||
|
|
||||||
|
const isSelected = useSelector((s) => getSelectedIds(s.data).has(id))
|
||||||
|
|
||||||
const shape = useSelector((s) => getPage(s.data).shapes[id])
|
const shape = useSelector((s) => getPage(s.data).shapes[id])
|
||||||
|
|
||||||
const events = useShapeEvents(id, getShapeUtils(shape)?.isParent, rGroup)
|
const events = useShapeEvents(id, getShapeUtils(shape)?.isParent, rGroup)
|
||||||
|
@ -62,9 +64,11 @@ function Shape({ id, isSelecting, parentPoint }: ShapeProps): JSX.Element {
|
||||||
id={id + '-group'}
|
id={id + '-group'}
|
||||||
ref={rGroup}
|
ref={rGroup}
|
||||||
transform={transform}
|
transform={transform}
|
||||||
|
isSelected={isSelected}
|
||||||
device={isMobileDevice ? 'mobile' : 'desktop'}
|
device={isMobileDevice ? 'mobile' : 'desktop'}
|
||||||
|
{...events}
|
||||||
>
|
>
|
||||||
{isSelecting && !isShy && (
|
{!isShy && (
|
||||||
<>
|
<>
|
||||||
{isForeignObject ? (
|
{isForeignObject ? (
|
||||||
<HoverIndicator
|
<HoverIndicator
|
||||||
|
@ -73,15 +77,13 @@ function Shape({ id, isSelecting, parentPoint }: ShapeProps): JSX.Element {
|
||||||
height={bounds.height}
|
height={bounds.height}
|
||||||
strokeWidth={1.5}
|
strokeWidth={1.5}
|
||||||
variant={'ghost'}
|
variant={'ghost'}
|
||||||
{...events}
|
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<HoverIndicator
|
<HoverIndicator
|
||||||
as="use"
|
as="use"
|
||||||
href={'#' + id}
|
href={'#' + id}
|
||||||
strokeWidth={+style.strokeWidth + 4}
|
strokeWidth={+style.strokeWidth + 5}
|
||||||
variant={getShapeUtils(shape).canStyleFill ? 'filled' : 'hollow'}
|
variant={getShapeUtils(shape).canStyleFill ? 'filled' : 'hollow'}
|
||||||
{...events}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -201,10 +203,10 @@ const StyledGroup = styled('g', {
|
||||||
isSelected: 'true',
|
isSelected: 'true',
|
||||||
css: {
|
css: {
|
||||||
[`&:hover ${HoverIndicator}`]: {
|
[`&:hover ${HoverIndicator}`]: {
|
||||||
opacity: '0.3',
|
opacity: '0.25',
|
||||||
},
|
},
|
||||||
[`&:active ${HoverIndicator}`]: {
|
[`&:active ${HoverIndicator}`]: {
|
||||||
opacity: '0.3',
|
opacity: '0.25',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -50,7 +50,7 @@ export default function useBoundsEvents(handle: Edge | Corner | 'rotate') {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
e.currentTarget.releasePointerCapture(e.pointerId)
|
e.currentTarget.releasePointerCapture(e.pointerId)
|
||||||
e.currentTarget.replaceWith(e.currentTarget)
|
e.currentTarget.replaceWith(e.currentTarget)
|
||||||
state.send('STOPPED_POINTING', inputs.pointerUp(e))
|
state.send('STOPPED_POINTING', inputs.pointerUp(e, 'bounds'))
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return { onPointerDown, onPointerMove, onPointerUp }
|
return { onPointerDown, onPointerMove, onPointerUp }
|
||||||
|
|
|
@ -54,8 +54,14 @@ export default function useCanvasEvents(
|
||||||
|
|
||||||
const handlePointerUp = useCallback((e: React.PointerEvent) => {
|
const handlePointerUp = useCallback((e: React.PointerEvent) => {
|
||||||
if (!inputs.canAccept(e.pointerId)) return
|
if (!inputs.canAccept(e.pointerId)) return
|
||||||
|
e.stopPropagation()
|
||||||
|
|
||||||
rCanvas.current.releasePointerCapture(e.pointerId)
|
rCanvas.current.releasePointerCapture(e.pointerId)
|
||||||
state.send('STOPPED_POINTING', { id: 'canvas', ...inputs.pointerUp(e) })
|
|
||||||
|
state.send('STOPPED_POINTING', {
|
||||||
|
id: 'canvas',
|
||||||
|
...inputs.pointerUp(e, 'canvas'),
|
||||||
|
})
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const handleTouchStart = useCallback(() => {
|
const handleTouchStart = useCallback(() => {
|
||||||
|
|
|
@ -30,7 +30,7 @@ export default function useHandleEvents(
|
||||||
if (isDoubleClick && !(info.altKey || info.metaKey)) {
|
if (isDoubleClick && !(info.altKey || info.metaKey)) {
|
||||||
state.send('DOUBLE_POINTED_HANDLE', info)
|
state.send('DOUBLE_POINTED_HANDLE', info)
|
||||||
} else {
|
} else {
|
||||||
state.send('STOPPED_POINTING', inputs.pointerUp(e))
|
state.send('STOPPED_POINTING', inputs.pointerUp(e, id))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[id]
|
[id]
|
||||||
|
|
|
@ -35,7 +35,7 @@ export default function useShapeEvents(
|
||||||
if (!inputs.canAccept(e.pointerId)) return
|
if (!inputs.canAccept(e.pointerId)) return
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
rGroup.current.releasePointerCapture(e.pointerId)
|
rGroup.current.releasePointerCapture(e.pointerId)
|
||||||
state.send('STOPPED_POINTING', inputs.pointerUp(e))
|
state.send('STOPPED_POINTING', inputs.pointerUp(e, id))
|
||||||
},
|
},
|
||||||
[id]
|
[id]
|
||||||
)
|
)
|
||||||
|
|
|
@ -70,14 +70,14 @@ const rectangle = registerShapeUtils<RectangleShape>({
|
||||||
|
|
||||||
const sw = strokeWidth * 1.618
|
const sw = strokeWidth * 1.618
|
||||||
|
|
||||||
const w = Math.max(0, size[0])
|
const w = Math.max(0, size[0] - sw / 2)
|
||||||
const h = Math.max(0, size[1])
|
const h = Math.max(0, size[1] - sw / 2)
|
||||||
|
|
||||||
const strokes: [number[], number[], number][] = [
|
const strokes: [number[], number[], number][] = [
|
||||||
[[sw / 2, sw / 2], [w - sw, sw / 2], w - sw],
|
[[sw / 2, sw / 2], [w, sw / 2], w - sw / 2],
|
||||||
[[w - sw / 2, sw / 2], [w - sw / 2, h - sw / 2], h - sw],
|
[[w, sw / 2], [w, h], h - sw / 2],
|
||||||
[[w - sw / 2, h - sw / 2], [sw / 2, h - sw / 2], w - sw],
|
[[w, h], [sw / 2, h], w - sw / 2],
|
||||||
[[sw / 2, h - sw / 2], [sw / 2, sw / 2], h - sw],
|
[[sw / 2, h], [sw / 2, sw / 2], h - sw / 2],
|
||||||
]
|
]
|
||||||
|
|
||||||
const paths = strokes.map(([start, end, length], i) => {
|
const paths = strokes.map(([start, end, length], i) => {
|
||||||
|
@ -108,8 +108,8 @@ const rectangle = registerShapeUtils<RectangleShape>({
|
||||||
<rect
|
<rect
|
||||||
x={sw / 2}
|
x={sw / 2}
|
||||||
y={sw / 2}
|
y={sw / 2}
|
||||||
width={size[0] - sw}
|
width={w}
|
||||||
height={size[1] - sw}
|
height={h}
|
||||||
fill={styles.fill}
|
fill={styles.fill}
|
||||||
stroke="none"
|
stroke="none"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -229,6 +229,7 @@ const state = createState({
|
||||||
initial: 'notPointing',
|
initial: 'notPointing',
|
||||||
states: {
|
states: {
|
||||||
notPointing: {
|
notPointing: {
|
||||||
|
onEnter: 'clearPointedId',
|
||||||
on: {
|
on: {
|
||||||
CANCELLED: 'clearSelectedIds',
|
CANCELLED: 'clearSelectedIds',
|
||||||
STARTED_PINCHING: { to: 'pinching' },
|
STARTED_PINCHING: { to: 'pinching' },
|
||||||
|
@ -282,7 +283,7 @@ const state = createState({
|
||||||
unless: 'isPointedShapeSelected',
|
unless: 'isPointedShapeSelected',
|
||||||
then: {
|
then: {
|
||||||
if: 'isPressingShiftKey',
|
if: 'isPressingShiftKey',
|
||||||
do: 'pushPointedIdToSelectedIds',
|
do: ['pushPointedIdToSelectedIds', 'clearPointedId'],
|
||||||
else: ['clearSelectedIds', 'pushPointedIdToSelectedIds'],
|
else: ['clearSelectedIds', 'pushPointedIdToSelectedIds'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -334,6 +335,7 @@ const state = createState({
|
||||||
},
|
},
|
||||||
pointingBounds: {
|
pointingBounds: {
|
||||||
on: {
|
on: {
|
||||||
|
CANCELLED: { to: 'notPointing' },
|
||||||
STOPPED_POINTING_BOUNDS: [],
|
STOPPED_POINTING_BOUNDS: [],
|
||||||
STOPPED_POINTING: [
|
STOPPED_POINTING: [
|
||||||
{
|
{
|
||||||
|
@ -342,15 +344,17 @@ const state = createState({
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
if: 'isPressingShiftKey',
|
if: 'isPressingShiftKey',
|
||||||
then: [
|
then: {
|
||||||
{
|
if: 'isPointedShapeSelected',
|
||||||
if: 'isPointedShapeSelected',
|
do: 'pullPointedIdFromSelectedIds',
|
||||||
do: 'pullPointedIdFromSelectedIds',
|
},
|
||||||
},
|
|
||||||
],
|
|
||||||
else: {
|
else: {
|
||||||
unless: 'isPointingBounds',
|
if: 'isPointingShape',
|
||||||
do: ['clearSelectedIds', 'pushPointedIdToSelectedIds'],
|
do: [
|
||||||
|
'clearSelectedIds',
|
||||||
|
'setPointedId',
|
||||||
|
'pushPointedIdToSelectedIds',
|
||||||
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ to: 'notPointing' },
|
{ to: 'notPointing' },
|
||||||
|
@ -915,6 +919,9 @@ const state = createState({
|
||||||
screenToWorld(payload.point, data)
|
screenToWorld(payload.point, data)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
hasPointedId(data, payload: PointerInfo) {
|
||||||
|
return getShape(data, payload.target) !== undefined
|
||||||
|
},
|
||||||
isPointingRotationHandle(
|
isPointingRotationHandle(
|
||||||
data,
|
data,
|
||||||
payload: { target: Edge | Corner | 'rotate' }
|
payload: { target: Edge | Corner | 'rotate' }
|
||||||
|
@ -1743,6 +1750,7 @@ function getParentId(data: Data, id: string) {
|
||||||
|
|
||||||
function getPointedId(data: Data, id: string) {
|
function getPointedId(data: Data, id: string) {
|
||||||
const shape = getPage(data).shapes[id]
|
const shape = getPage(data).shapes[id]
|
||||||
|
if (!shape) return id
|
||||||
|
|
||||||
return shape.parentId === data.currentParentId ||
|
return shape.parentId === data.currentParentId ||
|
||||||
shape.parentId === data.currentPageId
|
shape.parentId === data.currentPageId
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue