StateNode atoms (#2213)

This PR extracts some improvements from #2198 into a separate PR.

### Release Notes
- adds computed `StateNode.getPath`
- adds computed StateNode.getCurrent`
- adds computed StateNode.getIsActive`
- adds computed `Editor.getPath()`
- makes transition's second property optional

### Change Type

- [x] `minor` — New feature

### Test Plan

- [x] Unit Tests
- [x] End to end tests
This commit is contained in:
Steve Ruiz 2023-11-14 13:02:50 +00:00 committed by GitHub
parent 6f872c796a
commit 7186368f0d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
60 changed files with 562 additions and 468 deletions

View file

@ -5,18 +5,6 @@ export function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms)) return new Promise((resolve) => setTimeout(resolve, ms))
} }
// export async function expectPathToBe(page: Page, path: string) {
// expect(await page.evaluate(() => editor.root.path.value)).toBe(path)
// }
// export async function expectToHaveNShapes(page: Page, numberOfShapes: number) {
// expect(await page.evaluate(() => editor.currentPageShapes.length)).toBe(numberOfShapes)
// }
// export async function expectToHaveNSelectedShapes(page: Page, numberOfSelectedShapes: number) {
// expect(await page.evaluate(() => editor.selectedShapeIds.length)).toBe(numberOfSelectedShapes)
// }
declare const editor: Editor declare const editor: Editor
export async function setup({ page }: PlaywrightTestArgs & PlaywrightWorkerArgs) { export async function setup({ page }: PlaywrightTestArgs & PlaywrightWorkerArgs) {

View file

@ -711,6 +711,7 @@ export class Editor extends EventEmitter<TLEventMap> {
getPage(page: TLPage | TLPageId): TLPage | undefined; getPage(page: TLPage | TLPageId): TLPage | undefined;
getPageShapeIds(page: TLPage | TLPageId): Set<TLShapeId>; getPageShapeIds(page: TLPage | TLPageId): Set<TLShapeId>;
getPageStates(): TLInstancePageState[]; getPageStates(): TLInstancePageState[];
getPath(): string;
getPointInParentSpace(shape: TLShape | TLShapeId, point: VecLike): Vec2d; getPointInParentSpace(shape: TLShape | TLShapeId, point: VecLike): Vec2d;
getPointInShapeSpace(shape: TLShape | TLShapeId, point: VecLike): Vec2d; getPointInShapeSpace(shape: TLShape | TLShapeId, point: VecLike): Vec2d;
getSelectedShapeAtPoint(point: VecLike): TLShape | undefined; getSelectedShapeAtPoint(point: VecLike): TLShape | undefined;
@ -1848,8 +1849,6 @@ export abstract class StateNode implements Partial<TLEventHandlers> {
// (undocumented) // (undocumented)
children?: Record<string, StateNode>; children?: Record<string, StateNode>;
// (undocumented) // (undocumented)
current: Atom<StateNode | undefined>;
// (undocumented)
get currentToolIdMask(): string | undefined; get currentToolIdMask(): string | undefined;
set currentToolIdMask(id: string | undefined); set currentToolIdMask(id: string | undefined);
_currentToolIdMask: Atom<string | undefined, unknown>; _currentToolIdMask: Atom<string | undefined, unknown>;
@ -1859,6 +1858,9 @@ export abstract class StateNode implements Partial<TLEventHandlers> {
enter: (info: any, from: string) => void; enter: (info: any, from: string) => void;
// (undocumented) // (undocumented)
exit: (info: any, from: string) => void; exit: (info: any, from: string) => void;
getCurrent(): StateNode | undefined;
getIsActive(): boolean;
getPath(): string;
// (undocumented) // (undocumented)
handleEvent: (info: Exclude<TLEventInfo, TLPinchEventInfo>) => void; handleEvent: (info: Exclude<TLEventInfo, TLPinchEventInfo>) => void;
// (undocumented) // (undocumented)
@ -1870,8 +1872,6 @@ export abstract class StateNode implements Partial<TLEventHandlers> {
// (undocumented) // (undocumented)
initial?: string; initial?: string;
// (undocumented) // (undocumented)
isActive: boolean;
// (undocumented)
onCancel?: TLEventHandlers['onCancel']; onCancel?: TLEventHandlers['onCancel'];
// (undocumented) // (undocumented)
onComplete?: TLEventHandlers['onComplete']; onComplete?: TLEventHandlers['onComplete'];
@ -1908,11 +1908,10 @@ export abstract class StateNode implements Partial<TLEventHandlers> {
// (undocumented) // (undocumented)
parent: StateNode; parent: StateNode;
// (undocumented) // (undocumented)
path: Computed<string>; _path: Computed<string>;
// (undocumented) // (undocumented)
shapeType?: string; shapeType?: string;
// (undocumented) transition: (id: string, info?: any) => this;
transition: (id: string, info: any) => this;
// (undocumented) // (undocumented)
type: TLStateNodeType; type: TLStateNodeType;
} }

View file

@ -11223,6 +11223,37 @@
"isAbstract": false, "isAbstract": false,
"name": "getPageStates" "name": "getPageStates"
}, },
{
"kind": "Method",
"canonicalReference": "@tldraw/editor!Editor#getPath:member(1)",
"docComment": "/**\n * The editor's current path of active states.\n *\n * @example\n * ```ts\n * editor.path // \"select.idle\"\n * ```\n *\n * @public\n */\n",
"excerptTokens": [
{
"kind": "Content",
"text": "getPath(): "
},
{
"kind": "Content",
"text": "string"
},
{
"kind": "Content",
"text": ";"
}
],
"isStatic": false,
"returnTypeTokenRange": {
"startIndex": 1,
"endIndex": 2
},
"releaseTag": "Public",
"isProtected": false,
"overloadIndex": 1,
"parameters": [],
"isOptional": false,
"isAbstract": false,
"name": "getPath"
},
{ {
"kind": "Method", "kind": "Method",
"canonicalReference": "@tldraw/editor!Editor#getPointInParentSpace:member(1)", "canonicalReference": "@tldraw/editor!Editor#getPointInParentSpace:member(1)",
@ -34285,6 +34316,41 @@
"isProtected": false, "isProtected": false,
"isAbstract": false "isAbstract": false
}, },
{
"kind": "Property",
"canonicalReference": "@tldraw/editor!StateNode#_path:member",
"docComment": "",
"excerptTokens": [
{
"kind": "Content",
"text": "_path: "
},
{
"kind": "Reference",
"text": "Computed",
"canonicalReference": "@tldraw/state!Computed:interface"
},
{
"kind": "Content",
"text": "<string>"
},
{
"kind": "Content",
"text": ";"
}
],
"isReadonly": false,
"isOptional": false,
"releaseTag": "Public",
"name": "_path",
"propertyTypeTokenRange": {
"startIndex": 1,
"endIndex": 3
},
"isStatic": false,
"isProtected": false,
"isAbstract": false
},
{ {
"kind": "Constructor", "kind": "Constructor",
"canonicalReference": "@tldraw/editor!StateNode:constructor(1)", "canonicalReference": "@tldraw/editor!StateNode:constructor(1)",
@ -34418,50 +34484,6 @@
"isProtected": false, "isProtected": false,
"isAbstract": false "isAbstract": false
}, },
{
"kind": "Property",
"canonicalReference": "@tldraw/editor!StateNode#current:member",
"docComment": "",
"excerptTokens": [
{
"kind": "Content",
"text": "current: "
},
{
"kind": "Reference",
"text": "Atom",
"canonicalReference": "@tldraw/state!Atom:interface"
},
{
"kind": "Content",
"text": "<"
},
{
"kind": "Reference",
"text": "StateNode",
"canonicalReference": "@tldraw/editor!StateNode:class"
},
{
"kind": "Content",
"text": " | undefined>"
},
{
"kind": "Content",
"text": ";"
}
],
"isReadonly": false,
"isOptional": false,
"releaseTag": "Public",
"name": "current",
"propertyTypeTokenRange": {
"startIndex": 1,
"endIndex": 5
},
"isStatic": false,
"isProtected": false,
"isAbstract": false
},
{ {
"kind": "Property", "kind": "Property",
"canonicalReference": "@tldraw/editor!StateNode#currentToolIdMask:member", "canonicalReference": "@tldraw/editor!StateNode#currentToolIdMask:member",
@ -34583,6 +34605,104 @@
"isProtected": false, "isProtected": false,
"isAbstract": false "isAbstract": false
}, },
{
"kind": "Method",
"canonicalReference": "@tldraw/editor!StateNode#getCurrent:member(1)",
"docComment": "/**\n * This node's current active child node, if any.\n *\n * @public\n */\n",
"excerptTokens": [
{
"kind": "Content",
"text": "getCurrent(): "
},
{
"kind": "Reference",
"text": "StateNode",
"canonicalReference": "@tldraw/editor!StateNode:class"
},
{
"kind": "Content",
"text": " | undefined"
},
{
"kind": "Content",
"text": ";"
}
],
"isStatic": false,
"returnTypeTokenRange": {
"startIndex": 1,
"endIndex": 3
},
"releaseTag": "Public",
"isProtected": false,
"overloadIndex": 1,
"parameters": [],
"isOptional": false,
"isAbstract": false,
"name": "getCurrent"
},
{
"kind": "Method",
"canonicalReference": "@tldraw/editor!StateNode#getIsActive:member(1)",
"docComment": "/**\n * Whether this node is active.\n *\n * @public\n */\n",
"excerptTokens": [
{
"kind": "Content",
"text": "getIsActive(): "
},
{
"kind": "Content",
"text": "boolean"
},
{
"kind": "Content",
"text": ";"
}
],
"isStatic": false,
"returnTypeTokenRange": {
"startIndex": 1,
"endIndex": 2
},
"releaseTag": "Public",
"isProtected": false,
"overloadIndex": 1,
"parameters": [],
"isOptional": false,
"isAbstract": false,
"name": "getIsActive"
},
{
"kind": "Method",
"canonicalReference": "@tldraw/editor!StateNode#getPath:member(1)",
"docComment": "/**\n * This node's path of active state nodes\n *\n * @public\n */\n",
"excerptTokens": [
{
"kind": "Content",
"text": "getPath(): "
},
{
"kind": "Content",
"text": "string"
},
{
"kind": "Content",
"text": ";"
}
],
"isStatic": false,
"returnTypeTokenRange": {
"startIndex": 1,
"endIndex": 2
},
"releaseTag": "Public",
"isProtected": false,
"overloadIndex": 1,
"parameters": [],
"isOptional": false,
"isAbstract": false,
"name": "getPath"
},
{ {
"kind": "Property", "kind": "Property",
"canonicalReference": "@tldraw/editor!StateNode#handleEvent:member", "canonicalReference": "@tldraw/editor!StateNode#handleEvent:member",
@ -34760,36 +34880,6 @@
"isProtected": false, "isProtected": false,
"isAbstract": false "isAbstract": false
}, },
{
"kind": "Property",
"canonicalReference": "@tldraw/editor!StateNode#isActive:member",
"docComment": "",
"excerptTokens": [
{
"kind": "Content",
"text": "isActive: "
},
{
"kind": "Content",
"text": "boolean"
},
{
"kind": "Content",
"text": ";"
}
],
"isReadonly": false,
"isOptional": false,
"releaseTag": "Public",
"name": "isActive",
"propertyTypeTokenRange": {
"startIndex": 1,
"endIndex": 2
},
"isStatic": false,
"isProtected": false,
"isAbstract": false
},
{ {
"kind": "Property", "kind": "Property",
"canonicalReference": "@tldraw/editor!StateNode#onCancel:member", "canonicalReference": "@tldraw/editor!StateNode#onCancel:member",
@ -35408,41 +35498,6 @@
"isProtected": false, "isProtected": false,
"isAbstract": false "isAbstract": false
}, },
{
"kind": "Property",
"canonicalReference": "@tldraw/editor!StateNode#path:member",
"docComment": "",
"excerptTokens": [
{
"kind": "Content",
"text": "path: "
},
{
"kind": "Reference",
"text": "Computed",
"canonicalReference": "@tldraw/state!Computed:interface"
},
{
"kind": "Content",
"text": "<string>"
},
{
"kind": "Content",
"text": ";"
}
],
"isReadonly": false,
"isOptional": false,
"releaseTag": "Public",
"name": "path",
"propertyTypeTokenRange": {
"startIndex": 1,
"endIndex": 3
},
"isStatic": false,
"isProtected": false,
"isAbstract": false
},
{ {
"kind": "Property", "kind": "Property",
"canonicalReference": "@tldraw/editor!StateNode#shapeType:member", "canonicalReference": "@tldraw/editor!StateNode#shapeType:member",
@ -35476,7 +35531,7 @@
{ {
"kind": "Property", "kind": "Property",
"canonicalReference": "@tldraw/editor!StateNode#transition:member", "canonicalReference": "@tldraw/editor!StateNode#transition:member",
"docComment": "", "docComment": "/**\n * Transition to a new active child state node.\n *\n * @param id - The id of the child state node to transition to.\n *\n * @param info - Any data to pass to the `onEnter` and `onExit` handlers.\n *\n * @example\n * ```ts\n * parentState.transition('childStateA')\n * parentState.transition('childStateB', { myData: 4 })\n * ```\n *\n * @public\n */\n",
"excerptTokens": [ "excerptTokens": [
{ {
"kind": "Content", "kind": "Content",
@ -35484,7 +35539,7 @@
}, },
{ {
"kind": "Content", "kind": "Content",
"text": "(id: string, info: any) => this" "text": "(id: string, info?: any) => this"
}, },
{ {
"kind": "Content", "kind": "Content",

View file

@ -1008,7 +1008,7 @@ export class Editor extends EventEmitter<TLEventMap> {
willCrashApp, willCrashApp,
}, },
extras: { extras: {
activeStateNode: this.root.path.get(), activeStateNode: this.root.getPath(),
selectedShapes: this.getSelectedShapes(), selectedShapes: this.getSelectedShapes(),
editingShape: editingShapeId ? this.getShape(editingShapeId) : undefined, editingShape: editingShapeId ? this.getShape(editingShapeId) : undefined,
inputs: this.inputs, inputs: this.inputs,
@ -1051,6 +1051,20 @@ export class Editor extends EventEmitter<TLEventMap> {
/* ------------------- Statechart ------------------- */ /* ------------------- Statechart ------------------- */
/**
* The editor's current path of active states.
*
* @example
* ```ts
* editor.path // "select.idle"
* ```
*
* @public
*/
@computed getPath() {
return this.root.getPath().split('root.')[1]
}
/** /**
* Get whether a certain tool (or other state node) is currently active. * Get whether a certain tool (or other state node) is currently active.
* *
@ -1070,7 +1084,7 @@ export class Editor extends EventEmitter<TLEventMap> {
while (ids.length > 0) { while (ids.length > 0) {
const id = ids.pop() const id = ids.pop()
if (!id) return true if (!id) return true
const current = state.current.get() const current = state.getCurrent()
if (current?.id === id) { if (current?.id === id) {
if (ids.length === 0) return true if (ids.length === 0) return true
state = current state = current
@ -1119,7 +1133,7 @@ export class Editor extends EventEmitter<TLEventMap> {
* @public * @public
*/ */
@computed getCurrentTool(): StateNode | undefined { @computed getCurrentTool(): StateNode | undefined {
return this.root.current.get() return this.root.getCurrent()
} }
/** /**
@ -7476,7 +7490,7 @@ export class Editor extends EventEmitter<TLEventMap> {
// If the current tool is associated with a shape, return the styles for that shape. // If the current tool is associated with a shape, return the styles for that shape.
// Otherwise, just return an empty map. // Otherwise, just return an empty map.
const currentTool = this.root.current.get()! const currentTool = this.root.getCurrent()!
const styles = new SharedStyleMap() const styles = new SharedStyleMap()
if (currentTool.shapeType) { if (currentTool.shapeType) {
for (const style of this.styleProps[currentTool.shapeType].keys()) { for (const style of this.styleProps[currentTool.shapeType].keys()) {

View file

@ -112,13 +112,13 @@ export class Pointing extends StateNode {
this.editor.setSelectedShapes([id]) this.editor.setSelectedShapes([id])
if (this.editor.getInstanceState().isToolLocked) { if (this.editor.getInstanceState().isToolLocked) {
this.parent.transition('idle', {}) this.parent.transition('idle')
} else { } else {
this.editor.setCurrentTool('select.idle') this.editor.setCurrentTool('select.idle')
} }
} }
cancel() { cancel() {
this.parent.transition('idle', {}) this.parent.transition('idle')
} }
} }

View file

@ -7,11 +7,12 @@ export class RootState extends StateNode {
static override children = () => [] static override children = () => []
override onKeyDown: TLEventHandlers['onKeyDown'] = (info) => { override onKeyDown: TLEventHandlers['onKeyDown'] = (info) => {
// todo: move this logic up to the @tldraw/tldraw library, as the "zoom" tool only exists there
switch (info.code) { switch (info.code) {
case 'KeyZ': { case 'KeyZ': {
if (!(info.shiftKey || info.ctrlKey)) { if (!(info.shiftKey || info.ctrlKey)) {
const currentTool = this.current.get() const currentTool = this.getCurrent()
if (currentTool && currentTool.current.get()?.id === 'idle') { if (currentTool && currentTool.getCurrent()?.id === 'idle') {
if (this.children!['zoom']) { if (this.children!['zoom']) {
this.editor.setCurrentTool('zoom', { ...info, onInteractionEnd: currentTool.id }) this.editor.setCurrentTool('zoom', { ...info, onInteractionEnd: currentTool.id })
} }

View file

@ -25,11 +25,12 @@ export abstract class StateNode implements Partial<TLEventHandlers> {
const { id, children, initial } = this.constructor as TLStateNodeConstructor const { id, children, initial } = this.constructor as TLStateNodeConstructor
this.id = id this.id = id
this.current = atom<StateNode | undefined>('toolState' + this.id, undefined) this._isActive = atom<boolean>('toolIsActive' + this.id, false)
this._current = atom<StateNode | undefined>('toolState' + this.id, undefined)
this.path = computed('toolPath' + this.id, () => { this._path = computed('toolPath' + this.id, () => {
const current = this.current.get() const current = this.getCurrent()
return this.id + (current ? `.${current.path.get()}` : '') return this.id + (current ? `.${current.getPath()}` : '')
}) })
this.parent = parent ?? ({} as any) this.parent = parent ?? ({} as any)
@ -41,7 +42,7 @@ export abstract class StateNode implements Partial<TLEventHandlers> {
this.children = Object.fromEntries( this.children = Object.fromEntries(
children().map((Ctor) => [Ctor.id, new Ctor(this.editor, this)]) children().map((Ctor) => [Ctor.id, new Ctor(this.editor, this)])
) )
this.current.set(this.children[this.initial]) this._current.set(this.children[this.initial])
} else { } else {
this.type = 'leaf' this.type = 'leaf'
} }
@ -53,35 +54,74 @@ export abstract class StateNode implements Partial<TLEventHandlers> {
this.children = Object.fromEntries( this.children = Object.fromEntries(
children().map((Ctor) => [Ctor.id, new Ctor(this.editor, this)]) children().map((Ctor) => [Ctor.id, new Ctor(this.editor, this)])
) )
this.current.set(this.children[this.initial]) this._current.set(this.children[this.initial])
} }
} }
} }
path: Computed<string>
static id: string static id: string
static initial?: string static initial?: string
static children?: () => TLStateNodeConstructor[] static children?: () => TLStateNodeConstructor[]
id: string id: string
current: Atom<StateNode | undefined>
type: TLStateNodeType type: TLStateNodeType
shapeType?: string shapeType?: string
initial?: string initial?: string
children?: Record<string, StateNode> children?: Record<string, StateNode>
parent: StateNode parent: StateNode
isActive = false /**
* This node's path of active state nodes
*
* @public
*/
@computed getPath() {
return this._path.get()
}
_path: Computed<string>
transition = (id: string, info: any) => { /**
* This node's current active child node, if any.
*
* @public
*/
@computed getCurrent() {
return this._current.get()
}
private _current: Atom<StateNode | undefined>
/**
* Whether this node is active.
*
* @public
*/
@computed getIsActive() {
return this._isActive.get()
}
private _isActive: Atom<boolean>
/**
* Transition to a new active child state node.
*
* @example
* ```ts
* parentState.transition('childStateA')
* parentState.transition('childStateB', { myData: 4 })
*```
*
* @param id - The id of the child state node to transition to.
* @param info - Any data to pass to the `onEnter` and `onExit` handlers.
*
* @public
*/
transition = (id: string, info: any = {}) => {
const path = id.split('.') const path = id.split('.')
let currState = this as StateNode let currState = this as StateNode
for (let i = 0; i < path.length; i++) { for (let i = 0; i < path.length; i++) {
const id = path[i] const id = path[i]
const prevChildState = currState.current.get() const prevChildState = currState.getCurrent()
const nextChildState = currState.children?.[id] const nextChildState = currState.children?.[id]
if (!nextChildState) { if (!nextChildState) {
@ -90,9 +130,9 @@ export abstract class StateNode implements Partial<TLEventHandlers> {
if (prevChildState?.id !== nextChildState.id) { if (prevChildState?.id !== nextChildState.id) {
prevChildState?.exit(info, id) prevChildState?.exit(info, id)
currState.current.set(nextChildState) currState._current.set(nextChildState)
nextChildState.enter(info, prevChildState?.id || 'initial') nextChildState.enter(info, prevChildState?.id || 'initial')
if (!nextChildState.isActive) break if (!nextChildState.getIsActive()) break
} }
currState = nextChildState currState = nextChildState
@ -103,28 +143,30 @@ export abstract class StateNode implements Partial<TLEventHandlers> {
handleEvent = (info: Exclude<TLEventInfo, TLPinchEventInfo>) => { handleEvent = (info: Exclude<TLEventInfo, TLPinchEventInfo>) => {
const cbName = EVENT_NAME_MAP[info.name] const cbName = EVENT_NAME_MAP[info.name]
const x = this.current.get() const x = this.getCurrent()
this[cbName]?.(info as any) this[cbName]?.(info as any)
if (this.current.get() === x && this.isActive) { if (this.getCurrent() === x && this.getIsActive()) {
x?.handleEvent(info) x?.handleEvent(info)
} }
} }
// todo: move this logic into transition
enter = (info: any, from: string) => { enter = (info: any, from: string) => {
this.isActive = true this._isActive.set(true)
this.onEnter?.(info, from) this.onEnter?.(info, from)
if (this.children && this.initial && this.isActive) { if (this.children && this.initial && this.getIsActive()) {
const initial = this.children[this.initial] const initial = this.children[this.initial]
this.current.set(initial) this._current.set(initial)
initial.enter(info, from) initial.enter(info, from)
} }
} }
// todo: move this logic into transition
exit = (info: any, from: string) => { exit = (info: any, from: string) => {
this.isActive = false this._isActive.set(false)
this.onExit?.(info, from) this.onExit?.(info, from)
if (!this.isActive) { if (!this.getIsActive()) {
this.current.get()?.exit(info, from) this.getCurrent()?.exit(info, from)
} }
} }

View file

@ -55,7 +55,7 @@ export function useDocumentEvents() {
if ( if (
e.altKey && e.altKey &&
// todo: When should we allow the alt key to be used? Perhaps states should declare which keys matter to them? // todo: When should we allow the alt key to be used? Perhaps states should declare which keys matter to them?
(editor.isIn('zoom') || !editor.root.path.get().endsWith('.idle')) && (editor.isIn('zoom') || !editor.root.getPath().endsWith('.idle')) &&
!isFocusingInput() !isFocusingInput()
) { ) {
// On windows the alt key opens the menu bar. // On windows the alt key opens the menu bar.

View file

@ -34,7 +34,7 @@ beforeEach(() => {
it('enters the arrow state', () => { it('enters the arrow state', () => {
editor.setCurrentTool('arrow') editor.setCurrentTool('arrow')
expect(editor.getCurrentToolId()).toBe('arrow') expect(editor.getCurrentToolId()).toBe('arrow')
editor.expectPathToBe('root.arrow.idle') editor.expectToBeIn('arrow.idle')
}) })
describe('When in the idle state', () => { describe('When in the idle state', () => {
@ -43,7 +43,7 @@ describe('When in the idle state', () => {
editor.setCurrentTool('arrow').pointerDown(0, 0) editor.setCurrentTool('arrow').pointerDown(0, 0)
const shapesAfter = editor.currentPageShapes.length const shapesAfter = editor.currentPageShapes.length
expect(shapesAfter).toBe(shapesBefore + 1) expect(shapesAfter).toBe(shapesBefore + 1)
editor.expectPathToBe('root.arrow.pointing') editor.expectToBeIn('arrow.pointing')
}) })
it('returns to select on cancel', () => { it('returns to select on cancel', () => {
@ -60,7 +60,7 @@ describe('When in the pointing state', () => {
const shapesAfter = editor.currentPageShapes.length const shapesAfter = editor.currentPageShapes.length
expect(shapesAfter).toBe(shapesBefore) expect(shapesAfter).toBe(shapesBefore)
expect(editor.getHintingShapeIds().length).toBe(0) expect(editor.getHintingShapeIds().length).toBe(0)
editor.expectPathToBe('root.arrow.idle') editor.expectToBeIn('arrow.idle')
}) })
it('bails on cancel', () => { it('bails on cancel', () => {
@ -69,12 +69,12 @@ describe('When in the pointing state', () => {
const shapesAfter = editor.currentPageShapes.length const shapesAfter = editor.currentPageShapes.length
expect(shapesAfter).toBe(shapesBefore) expect(shapesAfter).toBe(shapesBefore)
expect(editor.getHintingShapeIds().length).toBe(0) expect(editor.getHintingShapeIds().length).toBe(0)
editor.expectPathToBe('root.arrow.idle') editor.expectToBeIn('arrow.idle')
}) })
it('enters the dragging state on pointer move', () => { it('enters the dragging state on pointer move', () => {
editor.setCurrentTool('arrow').pointerDown(0, 0).pointerMove(10, 10) editor.setCurrentTool('arrow').pointerDown(0, 0).pointerMove(10, 10)
editor.expectPathToBe('root.select.dragging_handle') editor.expectToBeIn('select.dragging_handle')
}) })
}) })
@ -93,7 +93,7 @@ describe('When dragging the arrow', () => {
end: { type: 'point', x: 10, y: 10 }, end: { type: 'point', x: 10, y: 10 },
}, },
}) })
editor.expectPathToBe('root.select.dragging_handle') editor.expectToBeIn('select.dragging_handle')
}) })
it('returns to select.idle, keeping shape, on pointer up', () => { it('returns to select.idle, keeping shape, on pointer up', () => {
@ -102,7 +102,7 @@ describe('When dragging the arrow', () => {
const shapesAfter = editor.currentPageShapes.length const shapesAfter = editor.currentPageShapes.length
expect(shapesAfter).toBe(shapesBefore + 1) expect(shapesAfter).toBe(shapesBefore + 1)
expect(editor.getHintingShapeIds().length).toBe(0) expect(editor.getHintingShapeIds().length).toBe(0)
editor.expectPathToBe('root.select.idle') editor.expectToBeIn('select.idle')
}) })
it('returns to arrow.idle, keeping shape, on pointer up when tool lock is active', () => { it('returns to arrow.idle, keeping shape, on pointer up when tool lock is active', () => {
@ -112,7 +112,7 @@ describe('When dragging the arrow', () => {
const shapesAfter = editor.currentPageShapes.length const shapesAfter = editor.currentPageShapes.length
expect(shapesAfter).toBe(shapesBefore + 1) expect(shapesAfter).toBe(shapesBefore + 1)
expect(editor.getHintingShapeIds().length).toBe(0) expect(editor.getHintingShapeIds().length).toBe(0)
editor.expectPathToBe('root.arrow.idle') editor.expectToBeIn('arrow.idle')
}) })
it('bails on cancel', () => { it('bails on cancel', () => {
@ -120,7 +120,7 @@ describe('When dragging the arrow', () => {
editor.setCurrentTool('arrow').pointerDown(0, 0).pointerMove(10, 10).cancel() editor.setCurrentTool('arrow').pointerDown(0, 0).pointerMove(10, 10).cancel()
const shapesAfter = editor.currentPageShapes.length const shapesAfter = editor.currentPageShapes.length
expect(shapesAfter).toBe(shapesBefore) expect(shapesAfter).toBe(shapesBefore)
editor.expectPathToBe('root.arrow.idle') editor.expectToBeIn('arrow.idle')
}) })
}) })
@ -409,10 +409,10 @@ describe('reparenting issue', () => {
handle: { id: 'end', type: 'vertex', index: 'a0', x: 100, y: 100 }, handle: { id: 'end', type: 'vertex', index: 'a0', x: 100, y: 100 },
shape: editor.getShape(arrowId)!, shape: editor.getShape(arrowId)!,
}) })
editor.expectPathToBe('root.select.pointing_handle') editor.expectToBeIn('select.pointing_handle')
editor.pointerMove(320, 320) // over box 2 editor.pointerMove(320, 320) // over box 2
editor.expectPathToBe('root.select.dragging_handle') editor.expectToBeIn('select.dragging_handle')
editor.expectShapeToMatch({ editor.expectShapeToMatch({
id: arrowId, id: arrowId,
index: 'a3V', index: 'a3V',

View file

@ -379,7 +379,7 @@ describe('resizing', () => {
.pointerDown(150, 300, { target: 'selection', handle: 'bottom' }) .pointerDown(150, 300, { target: 'selection', handle: 'bottom' })
.pointerMove(150, 600) .pointerMove(150, 600)
.expectPathToBe('root.select.resizing') .expectToBeIn('select.resizing')
expect(editor.getShape(arrow1.id)).toMatchObject({ expect(editor.getShape(arrow1.id)).toMatchObject({
x: 0, x: 0,
@ -436,7 +436,7 @@ describe('resizing', () => {
.pointerDown(150, 300, { target: 'selection', handle: 'bottom' }) .pointerDown(150, 300, { target: 'selection', handle: 'bottom' })
.pointerMove(150, -300) .pointerMove(150, -300)
.expectPathToBe('root.select.resizing') .expectToBeIn('select.resizing')
expect(editor.getShape(arrow1.id)).toCloselyMatchObject({ expect(editor.getShape(arrow1.id)).toCloselyMatchObject({
props: { props: {

View file

@ -26,7 +26,7 @@ export class Idle extends StateNode {
) { ) {
this.editor.setCurrentTool('select') this.editor.setCurrentTool('select')
this.editor.setEditingShape(onlySelectedShape.id) this.editor.setEditingShape(onlySelectedShape.id)
this.editor.root.current.get()!.transition('editing_shape', { this.editor.root.getCurrent()!.transition('editing_shape', {
...info, ...info,
target: 'shape', target: 'shape',
shape: onlySelectedShape, shape: onlySelectedShape,

View file

@ -75,7 +75,7 @@ export class Pointing extends StateNode {
this.editor.bailToMark(this.markId) this.editor.bailToMark(this.markId)
} }
this.editor.setHintingShapes([]) this.editor.setHintingShapes([])
this.parent.transition('idle', {}) this.parent.transition('idle')
} }
createArrowShape() { createArrowShape() {
@ -180,7 +180,7 @@ export class Pointing extends StateNode {
private didTimeout = false private didTimeout = false
private startPreciseTimeout() { private startPreciseTimeout() {
this.preciseTimeout = window.setTimeout(() => { this.preciseTimeout = window.setTimeout(() => {
if (!this.isActive) return if (!this.getIsActive()) return
this.didTimeout = true this.didTimeout = true
}, 320) }, 320)
} }

View file

@ -17,15 +17,15 @@ describe(DrawShapeTool, () => {
describe('When in the idle state', () => { describe('When in the idle state', () => {
it('Returns to select on cancel', () => { it('Returns to select on cancel', () => {
editor.setCurrentTool('draw') editor.setCurrentTool('draw')
editor.expectPathToBe('root.draw.idle') editor.expectToBeIn('draw.idle')
editor.cancel() editor.cancel()
editor.expectPathToBe('root.select.idle') editor.expectToBeIn('select.idle')
}) })
it('Enters the drawing state on pointer down', () => { it('Enters the drawing state on pointer down', () => {
editor.setCurrentTool('draw') editor.setCurrentTool('draw')
editor.pointerDown(50, 50) editor.pointerDown(50, 50)
editor.expectPathToBe('root.draw.drawing') editor.expectToBeIn('draw.drawing')
}) })
}) })
@ -34,19 +34,19 @@ describe('When in the drawing state', () => {
editor.setCurrentTool('draw') editor.setCurrentTool('draw')
editor.pointerDown(50, 50) editor.pointerDown(50, 50)
editor.cancel() editor.cancel()
editor.expectPathToBe('root.draw.idle') editor.expectToBeIn('draw.idle')
}) })
it('Returns to idle on complete', () => { it('Returns to idle on complete', () => {
editor.setCurrentTool('draw') editor.setCurrentTool('draw')
editor.pointerDown(50, 50) editor.pointerDown(50, 50)
editor.pointerUp(50, 50) editor.pointerUp(50, 50)
editor.expectPathToBe('root.draw.idle') editor.expectToBeIn('draw.idle')
editor.pointerDown(50, 50) editor.pointerDown(50, 50)
editor.pointerMove(55, 55) editor.pointerMove(55, 55)
editor.pointerMove(60, 60) editor.pointerMove(60, 60)
editor.pointerUp(60, 60) editor.pointerUp(60, 60)
editor.expectPathToBe('root.draw.idle') editor.expectToBeIn('draw.idle')
}) })
}) })

View file

@ -713,7 +713,7 @@ export class Drawing extends StateNode {
{ id: initialShape.id, type: initialShape.type, props: { isComplete: true } }, { id: initialShape.id, type: initialShape.type, props: { isComplete: true } },
]) ])
this.parent.transition('idle', {}) this.parent.transition('idle')
} }
cancel() { cancel() {

View file

@ -56,7 +56,7 @@ describe(FrameShapeTool, () => {
describe('When selecting the tool', () => { describe('When selecting the tool', () => {
it('selects the tool and enters the idle state', () => { it('selects the tool and enters the idle state', () => {
editor.setCurrentTool('frame') editor.setCurrentTool('frame')
editor.expectPathToBe('root.frame.idle') editor.expectToBeIn('frame.idle')
}) })
}) })
@ -64,19 +64,19 @@ describe('When in the idle state', () => {
it('Enters pointing state on pointer down', () => { it('Enters pointing state on pointer down', () => {
editor.setCurrentTool('frame') editor.setCurrentTool('frame')
editor.pointerDown(100, 100) editor.pointerDown(100, 100)
editor.expectPathToBe('root.frame.pointing') editor.expectToBeIn('frame.pointing')
}) })
it('Switches back to select tool on cancel', () => { it('Switches back to select tool on cancel', () => {
editor.setCurrentTool('frame') editor.setCurrentTool('frame')
editor.cancel() editor.cancel()
editor.expectPathToBe('root.select.idle') editor.expectToBeIn('select.idle')
}) })
it('Does nothing on interrupt', () => { it('Does nothing on interrupt', () => {
editor.setCurrentTool('frame') editor.setCurrentTool('frame')
editor.interrupt() editor.interrupt()
editor.expectPathToBe('root.frame.idle') editor.expectToBeIn('frame.idle')
}) })
}) })
@ -84,25 +84,25 @@ describe('When in the pointing state', () => {
it('Switches back to idle on cancel', () => { it('Switches back to idle on cancel', () => {
editor.setCurrentTool('frame') editor.setCurrentTool('frame')
editor.pointerDown(50, 50) editor.pointerDown(50, 50)
editor.expectPathToBe('root.frame.pointing') editor.expectToBeIn('frame.pointing')
editor.cancel() editor.cancel()
editor.expectPathToBe('root.frame.idle') editor.expectToBeIn('frame.idle')
}) })
it('Enters the select.resizing state on drag start', () => { it('Enters the select.resizing state on drag start', () => {
editor.setCurrentTool('frame') editor.setCurrentTool('frame')
editor.pointerDown(50, 50) editor.pointerDown(50, 50)
editor.pointerMove(51, 51) // not far enough! editor.pointerMove(51, 51) // not far enough!
editor.expectPathToBe('root.frame.pointing') editor.expectToBeIn('frame.pointing')
editor.pointerMove(55, 55) editor.pointerMove(55, 55)
editor.expectPathToBe('root.select.resizing') editor.expectToBeIn('select.resizing')
}) })
it('Enters the select.resizing state on pointer move', () => { it('Enters the select.resizing state on pointer move', () => {
editor.setCurrentTool('frame') editor.setCurrentTool('frame')
editor.pointerDown(50, 50) editor.pointerDown(50, 50)
editor.cancel() editor.cancel()
editor.expectPathToBe('root.frame.idle') editor.expectToBeIn('frame.idle')
}) })
it('Returns to the frame state on cancel', () => { it('Returns to the frame state on cancel', () => {
@ -110,7 +110,7 @@ describe('When in the pointing state', () => {
editor.pointerDown(50, 50) editor.pointerDown(50, 50)
editor.pointerMove(100, 100) editor.pointerMove(100, 100)
editor.cancel() editor.cancel()
editor.expectPathToBe('root.frame.idle') editor.expectToBeIn('frame.idle')
}) })
it('Creates a frame and returns to select tool on pointer up', () => { it('Creates a frame and returns to select tool on pointer up', () => {
@ -118,7 +118,7 @@ describe('When in the pointing state', () => {
editor.setCurrentTool('frame') editor.setCurrentTool('frame')
editor.pointerDown(50, 50) editor.pointerDown(50, 50)
editor.pointerUp(50, 50) editor.pointerUp(50, 50)
editor.expectPathToBe('root.select.idle') editor.expectToBeIn('select.idle')
expect(editor.currentPageShapes.length).toBe(1) expect(editor.currentPageShapes.length).toBe(1)
}) })
@ -128,7 +128,7 @@ describe('When in the pointing state', () => {
editor.setCurrentTool('frame') editor.setCurrentTool('frame')
editor.pointerDown(50, 50) editor.pointerDown(50, 50)
editor.pointerUp(50, 50) editor.pointerUp(50, 50)
editor.expectPathToBe('root.frame.idle') editor.expectToBeIn('frame.idle')
expect(editor.currentPageShapes.length).toBe(1) expect(editor.currentPageShapes.length).toBe(1)
}) })
}) })
@ -138,9 +138,9 @@ describe('When in the resizing state', () => {
editor.setCurrentTool('frame') editor.setCurrentTool('frame')
editor.pointerDown(50, 50) editor.pointerDown(50, 50)
editor.pointerMove(55, 55) editor.pointerMove(55, 55)
editor.expectPathToBe('root.select.resizing') editor.expectToBeIn('select.resizing')
editor.cancel() editor.cancel()
editor.expectPathToBe('root.frame.idle') editor.expectToBeIn('frame.idle')
}) })
it('Returns to select.idle on complete', () => { it('Returns to select.idle on complete', () => {
@ -148,7 +148,7 @@ describe('When in the resizing state', () => {
editor.pointerDown(50, 50) editor.pointerDown(50, 50)
editor.pointerMove(100, 100) editor.pointerMove(100, 100)
editor.pointerUp(100, 100) editor.pointerUp(100, 100)
editor.expectPathToBe('root.select.idle') editor.expectToBeIn('select.idle')
}) })
it('Returns to frame.idle on complete if tool lock is enabled', () => { it('Returns to frame.idle on complete if tool lock is enabled', () => {
@ -157,7 +157,7 @@ describe('When in the resizing state', () => {
editor.pointerDown(50, 50) editor.pointerDown(50, 50)
editor.pointerMove(100, 100) editor.pointerMove(100, 100)
editor.pointerUp(100, 100) editor.pointerUp(100, 100)
editor.expectPathToBe('root.frame.idle') editor.expectToBeIn('frame.idle')
}) })
}) })
@ -165,5 +165,5 @@ it('Returns to the idle state on interrupt', () => {
editor.setCurrentTool('frame') editor.setCurrentTool('frame')
editor.pointerDown(50, 50) editor.pointerDown(50, 50)
editor.interrupt() editor.interrupt()
editor.expectPathToBe('root.frame.idle') editor.expectToBeIn('frame.idle')
}) })

View file

@ -56,7 +56,7 @@ describe(GeoShapeTool, () => {
describe('When selecting the tool', () => { describe('When selecting the tool', () => {
it('selects the tool and enters the idle state', () => { it('selects the tool and enters the idle state', () => {
editor.setCurrentTool('geo') editor.setCurrentTool('geo')
editor.expectPathToBe('root.geo.idle') editor.expectToBeIn('geo.idle')
}) })
}) })
@ -64,19 +64,19 @@ describe('When in the idle state', () => {
it('Enters pointing state on pointer down', () => { it('Enters pointing state on pointer down', () => {
editor.setCurrentTool('geo') editor.setCurrentTool('geo')
editor.pointerDown(100, 100) editor.pointerDown(100, 100)
editor.expectPathToBe('root.geo.pointing') editor.expectToBeIn('geo.pointing')
}) })
it('Switches back to select tool on cancel', () => { it('Switches back to select tool on cancel', () => {
editor.setCurrentTool('geo') editor.setCurrentTool('geo')
editor.cancel() editor.cancel()
editor.expectPathToBe('root.select.idle') editor.expectToBeIn('select.idle')
}) })
it('Does nothing on interrupt', () => { it('Does nothing on interrupt', () => {
editor.setCurrentTool('geo') editor.setCurrentTool('geo')
editor.interrupt() editor.interrupt()
editor.expectPathToBe('root.geo.idle') editor.expectToBeIn('geo.idle')
}) })
it('Enters edit shape state on "Enter" key up when we have one geo shape', () => { it('Enters edit shape state on "Enter" key up when we have one geo shape', () => {
@ -86,7 +86,7 @@ describe('When in the idle state', () => {
editor.pointerUp(100, 100) editor.pointerUp(100, 100)
editor.keyUp('Enter') editor.keyUp('Enter')
editor.expectPathToBe('root.select.editing_shape') editor.expectToBeIn('select.editing_shape')
}) })
it('Does not enter edit shape state on "Enter" key up when multiple geo shapes are selected', () => { it('Does not enter edit shape state on "Enter" key up when multiple geo shapes are selected', () => {
@ -106,7 +106,7 @@ describe('When in the idle state', () => {
expect(editor.getSelectedShapes().length).toBe(2) expect(editor.getSelectedShapes().length).toBe(2)
editor.keyUp('Enter') editor.keyUp('Enter')
editor.expectPathToBe('root.select.idle') editor.expectToBeIn('select.idle')
}) })
}) })
@ -114,32 +114,32 @@ describe('When in the pointing state', () => {
it('Switches back to idle on cancel', () => { it('Switches back to idle on cancel', () => {
editor.setCurrentTool('geo') editor.setCurrentTool('geo')
editor.pointerDown(50, 50) editor.pointerDown(50, 50)
editor.expectPathToBe('root.geo.pointing') editor.expectToBeIn('geo.pointing')
editor.cancel() editor.cancel()
editor.expectPathToBe('root.geo.idle') editor.expectToBeIn('geo.idle')
}) })
it('Enters the select.resizing state on drag start', () => { it('Enters the select.resizing state on drag start', () => {
editor.setCurrentTool('geo') editor.setCurrentTool('geo')
editor.pointerDown(50, 50) editor.pointerDown(50, 50)
editor.pointerMove(51, 51) // not far enough! editor.pointerMove(51, 51) // not far enough!
editor.expectPathToBe('root.geo.pointing') editor.expectToBeIn('geo.pointing')
editor.pointerMove(55, 55) editor.pointerMove(55, 55)
editor.expectPathToBe('root.select.resizing') editor.expectToBeIn('select.resizing')
}) })
it('Enters the select.resizing state on pointer move', () => { it('Enters the select.resizing state on pointer move', () => {
editor.setCurrentTool('geo') editor.setCurrentTool('geo')
editor.pointerDown(50, 50) editor.pointerDown(50, 50)
editor.cancel() editor.cancel()
editor.expectPathToBe('root.geo.idle') editor.expectToBeIn('geo.idle')
}) })
it('Returns to the idle state on interrupt', () => { it('Returns to the idle state on interrupt', () => {
editor.setCurrentTool('geo') editor.setCurrentTool('geo')
editor.pointerDown(50, 50) editor.pointerDown(50, 50)
editor.interrupt() editor.interrupt()
editor.expectPathToBe('root.geo.idle') editor.expectToBeIn('geo.idle')
}) })
it('Creates a geo and returns to select tool on pointer up', () => { it('Creates a geo and returns to select tool on pointer up', () => {
@ -147,7 +147,7 @@ describe('When in the pointing state', () => {
editor.setCurrentTool('geo') editor.setCurrentTool('geo')
editor.pointerDown(50, 50) editor.pointerDown(50, 50)
editor.pointerUp(50, 50) editor.pointerUp(50, 50)
editor.expectPathToBe('root.select.idle') editor.expectToBeIn('select.idle')
expect(editor.currentPageShapes.length).toBe(1) expect(editor.currentPageShapes.length).toBe(1)
}) })
@ -157,7 +157,7 @@ describe('When in the pointing state', () => {
editor.setCurrentTool('geo') editor.setCurrentTool('geo')
editor.pointerDown(50, 50) editor.pointerDown(50, 50)
editor.pointerUp(50, 50) editor.pointerUp(50, 50)
editor.expectPathToBe('root.geo.idle') editor.expectToBeIn('geo.idle')
expect(editor.currentPageShapes.length).toBe(1) expect(editor.currentPageShapes.length).toBe(1)
}) })
}) })
@ -167,9 +167,9 @@ describe('When in the resizing state while creating a geo shape', () => {
editor.setCurrentTool('geo') editor.setCurrentTool('geo')
editor.pointerDown(50, 50) editor.pointerDown(50, 50)
editor.pointerMove(55, 55) editor.pointerMove(55, 55)
editor.expectPathToBe('root.select.resizing') editor.expectToBeIn('select.resizing')
editor.cancel() editor.cancel()
editor.expectPathToBe('root.geo.idle') editor.expectToBeIn('geo.idle')
}) })
it('Returns to select.idle on complete', () => { it('Returns to select.idle on complete', () => {
@ -177,7 +177,7 @@ describe('When in the resizing state while creating a geo shape', () => {
editor.pointerDown(50, 50) editor.pointerDown(50, 50)
editor.pointerMove(100, 100) editor.pointerMove(100, 100)
editor.pointerUp(100, 100) editor.pointerUp(100, 100)
editor.expectPathToBe('root.select.idle') editor.expectToBeIn('select.idle')
}) })
it('Returns to geo.idle on complete if tool lock is enabled', () => { it('Returns to geo.idle on complete if tool lock is enabled', () => {
@ -186,6 +186,6 @@ describe('When in the resizing state while creating a geo shape', () => {
editor.pointerDown(50, 50) editor.pointerDown(50, 50)
editor.pointerMove(100, 100) editor.pointerMove(100, 100)
editor.pointerUp(100, 100) editor.pointerUp(100, 100)
editor.expectPathToBe('root.geo.idle') editor.expectToBeIn('geo.idle')
}) })
}) })

View file

@ -23,7 +23,7 @@ export class Idle extends StateNode {
) { ) {
this.editor.setCurrentTool('select') this.editor.setCurrentTool('select')
this.editor.setEditingShape(onlySelectedShape.id) this.editor.setEditingShape(onlySelectedShape.id)
this.editor.root.current.get()!.transition('editing_shape', { this.editor.root.getCurrent()!.transition('editing_shape', {
...info, ...info,
target: 'shape', target: 'shape',
shape: onlySelectedShape, shape: onlySelectedShape,

View file

@ -118,7 +118,7 @@ export class Pointing extends StateNode {
]) ])
if (this.editor.getInstanceState().isToolLocked) { if (this.editor.getInstanceState().isToolLocked) {
this.parent.transition('idle', {}) this.parent.transition('idle')
} else { } else {
this.editor.setCurrentTool('select', {}) this.editor.setCurrentTool('select', {})
} }
@ -126,6 +126,6 @@ export class Pointing extends StateNode {
private cancel() { private cancel() {
// we should not have created any shapes yet, so no need to bail // we should not have created any shapes yet, so no need to bail
this.parent.transition('idle', {}) this.parent.transition('idle')
} }
} }

View file

@ -10,7 +10,7 @@ beforeEach(() => {
it('enters the line state', () => { it('enters the line state', () => {
editor.setCurrentTool('line') editor.setCurrentTool('line')
expect(editor.getCurrentToolId()).toBe('line') expect(editor.getCurrentToolId()).toBe('line')
editor.expectPathToBe('root.line.idle') editor.expectToBeIn('line.idle')
}) })
describe('When in the idle state', () => { describe('When in the idle state', () => {
@ -19,7 +19,7 @@ describe('When in the idle state', () => {
editor.setCurrentTool('line').pointerDown(0, 0, { target: 'canvas' }) editor.setCurrentTool('line').pointerDown(0, 0, { target: 'canvas' })
const shapesAfter = editor.currentPageShapes.length const shapesAfter = editor.currentPageShapes.length
expect(shapesAfter).toBe(shapesBefore + 1) expect(shapesAfter).toBe(shapesBefore + 1)
editor.expectPathToBe('root.line.pointing') editor.expectToBeIn('line.pointing')
}) })
it('returns to select on cancel', () => { it('returns to select on cancel', () => {
@ -36,7 +36,7 @@ describe('When in the pointing state', () => {
const shapesAfter = editor.currentPageShapes.length const shapesAfter = editor.currentPageShapes.length
expect(shapesAfter).toBe(shapesBefore + 1) expect(shapesAfter).toBe(shapesBefore + 1)
expect(editor.getHintingShapeIds().length).toBe(0) expect(editor.getHintingShapeIds().length).toBe(0)
editor.expectPathToBe('root.line.idle') editor.expectToBeIn('line.idle')
}) })
it('bails on cancel', () => { it('bails on cancel', () => {
@ -45,12 +45,12 @@ describe('When in the pointing state', () => {
const shapesAfter = editor.currentPageShapes.length const shapesAfter = editor.currentPageShapes.length
expect(shapesAfter).toBe(shapesBefore) expect(shapesAfter).toBe(shapesBefore)
expect(editor.getHintingShapeIds().length).toBe(0) expect(editor.getHintingShapeIds().length).toBe(0)
editor.expectPathToBe('root.line.idle') editor.expectToBeIn('line.idle')
}) })
it('enters the dragging state on pointer move', () => { it('enters the dragging state on pointer move', () => {
editor.setCurrentTool('line').pointerDown(0, 0, { target: 'canvas' }).pointerMove(10, 10) editor.setCurrentTool('line').pointerDown(0, 0, { target: 'canvas' }).pointerMove(10, 10)
editor.expectPathToBe('root.select.dragging_handle') editor.expectToBeIn('select.dragging_handle')
}) })
}) })
@ -71,7 +71,7 @@ describe('When dragging the line', () => {
}, },
}, },
}) })
editor.expectPathToBe('root.select.dragging_handle') editor.expectToBeIn('select.dragging_handle')
}) })
it('returns to select.idle, keeping shape, on pointer up', () => { it('returns to select.idle, keeping shape, on pointer up', () => {
@ -84,7 +84,7 @@ describe('When dragging the line', () => {
const shapesAfter = editor.currentPageShapes.length const shapesAfter = editor.currentPageShapes.length
expect(shapesAfter).toBe(shapesBefore + 1) expect(shapesAfter).toBe(shapesBefore + 1)
expect(editor.getHintingShapeIds().length).toBe(0) expect(editor.getHintingShapeIds().length).toBe(0)
editor.expectPathToBe('root.select.idle') editor.expectToBeIn('select.idle')
}) })
it('returns to line.idle, keeping shape, on pointer up if tool lock is enabled', () => { it('returns to line.idle, keeping shape, on pointer up if tool lock is enabled', () => {
@ -98,7 +98,7 @@ describe('When dragging the line', () => {
const shapesAfter = editor.currentPageShapes.length const shapesAfter = editor.currentPageShapes.length
expect(shapesAfter).toBe(shapesBefore + 1) expect(shapesAfter).toBe(shapesBefore + 1)
expect(editor.getHintingShapeIds().length).toBe(0) expect(editor.getHintingShapeIds().length).toBe(0)
editor.expectPathToBe('root.line.idle') editor.expectToBeIn('line.idle')
}) })
it('bails on cancel', () => { it('bails on cancel', () => {
@ -110,7 +110,7 @@ describe('When dragging the line', () => {
.cancel() .cancel()
const shapesAfter = editor.currentPageShapes.length const shapesAfter = editor.currentPageShapes.length
expect(shapesAfter).toBe(shapesBefore) expect(shapesAfter).toBe(shapesBefore)
editor.expectPathToBe('root.line.idle') editor.expectToBeIn('line.idle')
}) })
}) })

View file

@ -142,7 +142,7 @@ describe('Misc', () => {
editor editor
.pointerDown(150, 0, { target: 'selection', handle: 'bottom' }) .pointerDown(150, 0, { target: 'selection', handle: 'bottom' })
.pointerMove(150, 600) // Resize shape by 0, 600 .pointerMove(150, 600) // Resize shape by 0, 600
.expectPathToBe('root.select.resizing') .expectToBeIn('select.resizing')
expect(editor.getShape(id)!).toMatchSnapshot('line shape after resize') expect(editor.getShape(id)!).toMatchSnapshot('line shape after resize')
}) })

View file

@ -147,7 +147,7 @@ export class Pointing extends StateNode {
} }
override onInterrupt: TLInterruptEvent = () => { override onInterrupt: TLInterruptEvent = () => {
this.parent.transition('idle', {}) this.parent.transition('idle')
if (this.markId) this.editor.bailToMark(this.markId) if (this.markId) this.editor.bailToMark(this.markId)
this.editor.snaps.clear() this.editor.snaps.clear()
} }

View file

@ -59,7 +59,7 @@ describe(NoteShapeTool, () => {
describe('When selecting the tool', () => { describe('When selecting the tool', () => {
it('selects the tool and enters the idle state', () => { it('selects the tool and enters the idle state', () => {
editor.setCurrentTool('note') editor.setCurrentTool('note')
editor.expectPathToBe('root.note.idle') editor.expectToBeIn('note.idle')
}) })
}) })
@ -67,19 +67,19 @@ describe('When in the idle state', () => {
it('Enters pointing state on pointer down', () => { it('Enters pointing state on pointer down', () => {
editor.setCurrentTool('note') editor.setCurrentTool('note')
editor.pointerDown(100, 100) editor.pointerDown(100, 100)
editor.expectPathToBe('root.note.pointing') editor.expectToBeIn('note.pointing')
}) })
it('Switches back to select tool on cancel', () => { it('Switches back to select tool on cancel', () => {
editor.setCurrentTool('note') editor.setCurrentTool('note')
editor.cancel() editor.cancel()
editor.expectPathToBe('root.select.idle') editor.expectToBeIn('select.idle')
}) })
it('Does nothing on interrupt', () => { it('Does nothing on interrupt', () => {
editor.setCurrentTool('note') editor.setCurrentTool('note')
editor.interrupt() editor.interrupt()
editor.expectPathToBe('root.note.idle') editor.expectToBeIn('note.idle')
}) })
}) })
@ -87,18 +87,18 @@ describe('When in the pointing state', () => {
it('Switches back to idle on cancel', () => { it('Switches back to idle on cancel', () => {
editor.setCurrentTool('note') editor.setCurrentTool('note')
editor.pointerDown(50, 50) editor.pointerDown(50, 50)
editor.expectPathToBe('root.note.pointing') editor.expectToBeIn('note.pointing')
editor.cancel() editor.cancel()
editor.expectPathToBe('root.note.idle') editor.expectToBeIn('note.idle')
}) })
it('Enters the select.translating state on drag start', () => { it('Enters the select.translating state on drag start', () => {
editor.setCurrentTool('note') editor.setCurrentTool('note')
editor.pointerDown(50, 50) editor.pointerDown(50, 50)
editor.pointerMove(51, 51) // not far enough! editor.pointerMove(51, 51) // not far enough!
editor.expectPathToBe('root.note.pointing') editor.expectToBeIn('note.pointing')
editor.pointerMove(55, 55) editor.pointerMove(55, 55)
editor.expectPathToBe('root.select.translating') editor.expectToBeIn('select.translating')
}) })
it('Returns to the note tool on cancel from translating', () => { it('Returns to the note tool on cancel from translating', () => {
@ -106,7 +106,7 @@ describe('When in the pointing state', () => {
editor.pointerDown(50, 50) editor.pointerDown(50, 50)
editor.pointerMove(55, 55) editor.pointerMove(55, 55)
editor.cancel() editor.cancel()
editor.expectPathToBe('root.note.idle') editor.expectToBeIn('note.idle')
}) })
it('Returns to the note tool on complete from translating when tool lock is enabled', () => { it('Returns to the note tool on complete from translating when tool lock is enabled', () => {
@ -115,14 +115,14 @@ describe('When in the pointing state', () => {
editor.pointerDown(50, 50) editor.pointerDown(50, 50)
editor.pointerMove(55, 55) editor.pointerMove(55, 55)
editor.pointerUp() editor.pointerUp()
editor.expectPathToBe('root.note.idle') editor.expectToBeIn('note.idle')
}) })
it('Returns to the idle state on interrupt', () => { it('Returns to the idle state on interrupt', () => {
editor.setCurrentTool('note') editor.setCurrentTool('note')
editor.pointerDown(50, 50) editor.pointerDown(50, 50)
editor.interrupt() editor.interrupt()
editor.expectPathToBe('root.note.idle') editor.expectToBeIn('note.idle')
}) })
it('Creates a note and begins editing on pointer up', () => { it('Creates a note and begins editing on pointer up', () => {
@ -130,7 +130,7 @@ describe('When in the pointing state', () => {
editor.setCurrentTool('note') editor.setCurrentTool('note')
editor.pointerDown(50, 50) editor.pointerDown(50, 50)
editor.pointerUp(50, 50) editor.pointerUp(50, 50)
editor.expectPathToBe('root.select.editing_shape') editor.expectToBeIn('select.editing_shape')
expect(editor.currentPageShapes.length).toBe(1) expect(editor.currentPageShapes.length).toBe(1)
}) })
@ -140,7 +140,7 @@ describe('When in the pointing state', () => {
editor.setCurrentTool('note') editor.setCurrentTool('note')
editor.pointerDown(50, 50) editor.pointerDown(50, 50)
editor.pointerUp(50, 50) editor.pointerUp(50, 50)
editor.expectPathToBe('root.note.idle') editor.expectToBeIn('note.idle')
expect(editor.currentPageShapes.length).toBe(1) expect(editor.currentPageShapes.length).toBe(1)
}) })
}) })

View file

@ -63,7 +63,7 @@ export class Pointing extends StateNode {
private complete() { private complete() {
if (this.wasFocusedOnEnter) { if (this.wasFocusedOnEnter) {
if (this.editor.getInstanceState().isToolLocked) { if (this.editor.getInstanceState().isToolLocked) {
this.parent.transition('idle', {}) this.parent.transition('idle')
} else { } else {
this.editor.setEditingShape(this.shape.id) this.editor.setEditingShape(this.shape.id)
this.editor.setCurrentTool('select.editing_shape', { this.editor.setCurrentTool('select.editing_shape', {

View file

@ -32,7 +32,7 @@ export class Idle extends StateNode {
) { ) {
this.editor.setCurrentTool('select') this.editor.setCurrentTool('select')
this.editor.setEditingShape(onlySelectedShape.id) this.editor.setEditingShape(onlySelectedShape.id)
this.editor.root.current.get()!.transition('editing_shape', { this.editor.root.getCurrent()!.transition('editing_shape', {
...info, ...info,
target: 'shape', target: 'shape',
shape: onlySelectedShape, shape: onlySelectedShape,

View file

@ -90,11 +90,11 @@ export class Pointing extends StateNode {
this.editor.setEditingShape(id) this.editor.setEditingShape(id)
this.editor.setCurrentTool('select') this.editor.setCurrentTool('select')
this.editor.root.current.get()?.transition('editing_shape', {}) this.editor.root.getCurrent()?.transition('editing_shape')
} }
private cancel() { private cancel() {
this.parent.transition('idle', {}) this.parent.transition('idle')
this.editor.bailToMark(this.markId) this.editor.bailToMark(this.markId)
} }
} }

View file

@ -119,7 +119,7 @@ export class Erasing extends StateNode {
complete() { complete() {
this.editor.deleteShapes(this.editor.getCurrentPageState().erasingShapeIds) this.editor.deleteShapes(this.editor.getCurrentPageState().erasingShapeIds)
this.editor.setErasingShapes([]) this.editor.setErasingShapes([])
this.parent.transition('idle', {}) this.parent.transition('idle')
} }
cancel() { cancel() {

View file

@ -83,11 +83,11 @@ export class Pointing extends StateNode {
} }
this.editor.setErasingShapes([]) this.editor.setErasingShapes([])
this.parent.transition('idle', {}) this.parent.transition('idle')
} }
cancel() { cancel() {
this.editor.setErasingShapes([]) this.editor.setErasingShapes([])
this.parent.transition('idle', {}) this.parent.transition('idle')
} }
} }

View file

@ -40,6 +40,6 @@ export class Dragging extends StateNode {
friction: CAMERA_SLIDE_FRICTION, friction: CAMERA_SLIDE_FRICTION,
}) })
this.parent.transition('idle', {}) this.parent.transition('idle')
} }
} }

View file

@ -34,6 +34,6 @@ export class Pointing extends StateNode {
} }
private complete() { private complete() {
this.parent.transition('idle', {}) this.parent.transition('idle')
} }
} }

View file

@ -44,10 +44,10 @@ export class Lasering extends StateNode {
} }
private complete() { private complete() {
this.parent.transition('idle', {}) this.parent.transition('idle')
} }
private cancel() { private cancel() {
this.parent.transition('idle', {}) this.parent.transition('idle')
} }
} }

View file

@ -89,7 +89,7 @@ export class Brushing extends StateNode {
} }
private complete() { private complete() {
this.parent.transition('idle', {}) this.parent.transition('idle')
} }
private hitTestShapes() { private hitTestShapes() {

View file

@ -209,7 +209,7 @@ export class Cropping extends StateNode {
this.editor.setCurrentTool(this.info.onInteractionEnd, this.info) this.editor.setCurrentTool(this.info.onInteractionEnd, this.info)
} else { } else {
this.editor.setCroppingShape(null) this.editor.setCroppingShape(null)
this.parent.transition('idle', {}) this.parent.transition('idle')
} }
} }
@ -219,7 +219,7 @@ export class Cropping extends StateNode {
this.editor.setCurrentTool(this.info.onInteractionEnd, this.info) this.editor.setCurrentTool(this.info.onInteractionEnd, this.info)
} else { } else {
this.editor.setCroppingShape(null) this.editor.setCroppingShape(null)
this.parent.transition('idle', {}) this.parent.transition('idle')
} }
} }

View file

@ -124,7 +124,7 @@ export class DraggingHandle extends StateNode {
} }
this.exactTimeout = setTimeout(() => { this.exactTimeout = setTimeout(() => {
if (this.isActive && !this.isPrecise) { if (this.getIsActive() && !this.isPrecise) {
this.isPrecise = true this.isPrecise = true
this.isPreciseId = this.pointingId this.isPreciseId = this.pointingId
this.update() this.update()
@ -186,7 +186,7 @@ export class DraggingHandle extends StateNode {
return return
} }
this.parent.transition('idle', {}) this.parent.transition('idle')
} }
private cancel() { private cancel() {
@ -201,7 +201,7 @@ export class DraggingHandle extends StateNode {
return return
} }
this.parent.transition('idle', {}) this.parent.transition('idle')
} }
private update() { private update() {

View file

@ -32,10 +32,10 @@ export class PointingCanvas extends StateNode {
} }
override onInterrupt = () => { override onInterrupt = () => {
this.parent.transition('idle', {}) this.parent.transition('idle')
} }
private complete() { private complete() {
this.parent.transition('idle', {}) this.parent.transition('idle')
} }
} }

View file

@ -56,7 +56,7 @@ export class PointingCropHandle extends StateNode {
this.editor.setCurrentTool(this.info.onInteractionEnd, this.info) this.editor.setCurrentTool(this.info.onInteractionEnd, this.info)
} else { } else {
this.editor.setCroppingShape(null) this.editor.setCroppingShape(null)
this.parent.transition('idle', {}) this.parent.transition('idle')
} }
} }
@ -77,7 +77,7 @@ export class PointingCropHandle extends StateNode {
this.editor.setCurrentTool(this.info.onInteractionEnd, this.info) this.editor.setCurrentTool(this.info.onInteractionEnd, this.info)
} else { } else {
this.editor.setCroppingShape(null) this.editor.setCroppingShape(null)
this.parent.transition('idle', {}) this.parent.transition('idle')
} }
} }
} }

View file

@ -51,6 +51,6 @@ export class PointingHandle extends StateNode {
} }
private cancel() { private cancel() {
this.parent.transition('idle', {}) this.parent.transition('idle')
} }
} }

View file

@ -76,7 +76,7 @@ export class PointingResizeHandle extends StateNode {
if (this.info.onInteractionEnd) { if (this.info.onInteractionEnd) {
this.editor.setCurrentTool(this.info.onInteractionEnd, {}) this.editor.setCurrentTool(this.info.onInteractionEnd, {})
} else { } else {
this.parent.transition('idle', {}) this.parent.transition('idle')
} }
} }
@ -84,7 +84,7 @@ export class PointingResizeHandle extends StateNode {
if (this.info.onInteractionEnd) { if (this.info.onInteractionEnd) {
this.editor.setCurrentTool(this.info.onInteractionEnd, {}) this.editor.setCurrentTool(this.info.onInteractionEnd, {})
} else { } else {
this.parent.transition('idle', {}) this.parent.transition('idle')
} }
} }
} }

View file

@ -62,7 +62,7 @@ export class PointingRotateHandle extends StateNode {
if (this.info.onInteractionEnd) { if (this.info.onInteractionEnd) {
this.editor.setCurrentTool(this.info.onInteractionEnd, {}) this.editor.setCurrentTool(this.info.onInteractionEnd, {})
} else { } else {
this.parent.transition('idle', {}) this.parent.transition('idle')
} }
} }
@ -70,7 +70,7 @@ export class PointingRotateHandle extends StateNode {
if (this.info.onInteractionEnd) { if (this.info.onInteractionEnd) {
this.editor.setCurrentTool(this.info.onInteractionEnd, {}) this.editor.setCurrentTool(this.info.onInteractionEnd, {})
} else { } else {
this.parent.transition('idle', {}) this.parent.transition('idle')
} }
} }
} }

View file

@ -43,7 +43,7 @@ export class PointingSelection extends StateNode {
if (hitShape) { if (hitShape) {
// todo: extract the double click shape logic from idle so that we can share it here // todo: extract the double click shape logic from idle so that we can share it here
this.parent.transition('idle', {}) this.parent.transition('idle')
this.parent.onDoubleClick?.({ this.parent.onDoubleClick?.({
...info, ...info,
target: 'shape', target: 'shape',
@ -66,6 +66,6 @@ export class PointingSelection extends StateNode {
} }
private cancel() { private cancel() {
this.parent.transition('idle', {}) this.parent.transition('idle')
} }
} }

View file

@ -213,6 +213,6 @@ export class PointingShape extends StateNode {
} }
private cancel() { private cancel() {
this.parent.transition('idle', {}) this.parent.transition('idle')
} }
} }

View file

@ -102,7 +102,7 @@ export class Resizing extends StateNode {
if (this.info.onInteractionEnd) { if (this.info.onInteractionEnd) {
this.editor.setCurrentTool(this.info.onInteractionEnd, {}) this.editor.setCurrentTool(this.info.onInteractionEnd, {})
} else { } else {
this.parent.transition('idle', {}) this.parent.transition('idle')
} }
} }
@ -121,7 +121,7 @@ export class Resizing extends StateNode {
return return
} }
this.parent.transition('idle', {}) this.parent.transition('idle')
} }
private handleResizeStart() { private handleResizeStart() {

View file

@ -65,7 +65,7 @@ export class ScribbleBrushing extends StateNode {
override onKeyUp = () => { override onKeyUp = () => {
if (!this.editor.inputs.altKey) { if (!this.editor.inputs.altKey) {
this.parent.transition('brushing', {}) this.parent.transition('brushing')
} else { } else {
this.updateScribbleSelection(false) this.updateScribbleSelection(false)
} }
@ -157,11 +157,11 @@ export class ScribbleBrushing extends StateNode {
} }
private complete() { private complete() {
this.parent.transition('idle', {}) this.parent.transition('idle')
} }
private cancel() { private cancel() {
this.editor.setSelectedShapes([...this.initialSelectedShapeIds], { squashing: true }) this.editor.setSelectedShapes([...this.initialSelectedShapeIds], { squashing: true })
this.parent.transition('idle', {}) this.parent.transition('idle')
} }
} }

View file

@ -172,7 +172,7 @@ export class Translating extends StateNode {
this.editor.setCurrentTool('select.editing_shape') this.editor.setCurrentTool('select.editing_shape')
} }
} else { } else {
this.parent.transition('idle', {}) this.parent.transition('idle')
} }
} }
} }

View file

@ -47,7 +47,7 @@ export class ZoomTool extends StateNode {
if (this.info.onInteractionEnd && this.info.onInteractionEnd !== 'select') { if (this.info.onInteractionEnd && this.info.onInteractionEnd !== 'select') {
this.editor.setCurrentTool(this.info.onInteractionEnd, this.info) this.editor.setCurrentTool(this.info.onInteractionEnd, this.info)
} else { } else {
this.parent.transition('select', {}) this.parent.transition('select')
} }
} }

View file

@ -65,7 +65,7 @@ export const DebugPanel = React.memo(function DebugPanel({
const CurrentState = track(function CurrentState() { const CurrentState = track(function CurrentState() {
const editor = useEditor() const editor = useEditor()
return <div className="tlui-debug-panel__current-state">{editor.root.path.get()}</div> return <div className="tlui-debug-panel__current-state">{editor.root.getPath()}</div>
}) })
const ShapeCount = function ShapeCount() { const ShapeCount = function ShapeCount() {

View file

@ -279,12 +279,12 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
readonlyOk: true, readonlyOk: true,
kbd: 'z', kbd: 'z',
onSelect(source) { onSelect(source) {
if (editor.root.current.get()?.id === 'zoom') return if (editor.root.getCurrent()?.id === 'zoom') return
trackEvent('zoom-tool', { source }) trackEvent('zoom-tool', { source })
if (!(editor.inputs.shiftKey || editor.inputs.ctrlKey)) { if (!(editor.inputs.shiftKey || editor.inputs.ctrlKey)) {
const currentTool = editor.root.current.get() const currentTool = editor.root.getCurrent()
if (currentTool && currentTool.current.get()?.id === 'idle') { if (currentTool && currentTool.getCurrent()?.id === 'idle') {
editor.setCurrentTool('zoom', { onInteractionEnd: currentTool.id, maskAs: 'zoom' }) editor.setCurrentTool('zoom', { onInteractionEnd: currentTool.id, maskAs: 'zoom' })
} }
} }

View file

@ -22,7 +22,7 @@ export function useRelevantStyles(): {
() => { () => {
const styles = new SharedStyleMap(editor.sharedStyles) const styles = new SharedStyleMap(editor.sharedStyles)
const hasShape = const hasShape =
editor.getSelectedShapeIds().length > 0 || !!editor.root.current.get()?.shapeType editor.getSelectedShapeIds().length > 0 || !!editor.root.getCurrent()?.shapeType
if (styles.size === 0 && editor.isIn('select') && editor.getSelectedShapeIds().length === 0) { if (styles.size === 0 && editor.isIn('select') && editor.getSelectedShapeIds().length === 0) {
for (const style of selectToolStyles) { for (const style of selectToolStyles) {

View file

@ -322,7 +322,7 @@ describe('currentToolId', () => {
editor.pointerMove(100, 100) editor.pointerMove(100, 100)
expect(editor.getCurrentToolId()).toBe('geo') expect(editor.getCurrentToolId()).toBe('geo')
expect(editor.root.path.get()).toBe('root.select.resizing') editor.expectToBeIn('select.resizing')
}) })
it('reverts back to select if we finish the interaction', () => { it('reverts back to select if we finish the interaction', () => {
@ -333,7 +333,7 @@ describe('currentToolId', () => {
editor.pointerMove(100, 100) editor.pointerMove(100, 100)
expect(editor.getCurrentToolId()).toBe('geo') expect(editor.getCurrentToolId()).toBe('geo')
expect(editor.root.path.get()).toBe('root.select.resizing') editor.expectToBeIn('select.resizing')
editor.pointerUp(100, 100) editor.pointerUp(100, 100)
@ -348,7 +348,7 @@ describe('currentToolId', () => {
editor.pointerMove(100, 100) editor.pointerMove(100, 100)
expect(editor.getCurrentToolId()).toBe('geo') expect(editor.getCurrentToolId()).toBe('geo')
expect(editor.root.path.get()).toBe('root.select.resizing') editor.expectToBeIn('select.resizing')
editor.cancel() editor.cancel()

View file

@ -97,14 +97,14 @@ describe('When clicking', () => {
editor.setCurrentTool('eraser') editor.setCurrentTool('eraser')
// Starts in idle // Starts in idle
editor.expectPathToBe('root.eraser.idle') editor.expectToBeIn('eraser.idle')
const shapesBeforeCount = editor.currentPageShapes.length const shapesBeforeCount = editor.currentPageShapes.length
editor.pointerDown(0, 0) // near enough to box1 editor.pointerDown(0, 0) // near enough to box1
// Enters the pointing state // Enters the pointing state
editor.expectPathToBe('root.eraser.pointing') editor.expectToBeIn('eraser.pointing')
// Sets the erasingShapeIds array // Sets the erasingShapeIds array
expect(editor.getErasingShapeIds()).toEqual([ids.box1]) expect(editor.getErasingShapeIds()).toEqual([ids.box1])
@ -121,7 +121,7 @@ describe('When clicking', () => {
expect(editor.getErasingShapeIds()).toEqual([]) expect(editor.getErasingShapeIds()).toEqual([])
// Returns to idle // Returns to idle
editor.expectPathToBe('root.eraser.idle') editor.expectToBeIn('eraser.idle')
editor.undo() editor.undo()
@ -240,12 +240,12 @@ describe('When clicking', () => {
it('Clears erasing ids and does not erase shapes on cancel', () => { it('Clears erasing ids and does not erase shapes on cancel', () => {
editor.setCurrentTool('eraser') editor.setCurrentTool('eraser')
editor.expectPathToBe('root.eraser.idle') editor.expectToBeIn('eraser.idle')
const shapesBeforeCount = editor.currentPageShapes.length const shapesBeforeCount = editor.currentPageShapes.length
editor.pointerDown(0, 0) // in box1 editor.pointerDown(0, 0) // in box1
editor.expectPathToBe('root.eraser.pointing') editor.expectToBeIn('eraser.pointing')
expect(editor.getErasingShapeIds()).toEqual([ids.box1]) expect(editor.getErasingShapeIds()).toEqual([ids.box1])
@ -255,7 +255,7 @@ describe('When clicking', () => {
const shapesAfterCount = editor.currentPageShapes.length const shapesAfterCount = editor.currentPageShapes.length
editor.expectPathToBe('root.eraser.idle') editor.expectToBeIn('eraser.idle')
// Does NOT erase the shape // Does NOT erase the shape
expect(editor.getErasingShapeIds()).toEqual([]) expect(editor.getErasingShapeIds()).toEqual([])
@ -265,12 +265,12 @@ describe('When clicking', () => {
it('Clears erasing ids and does not erase shapes on interrupt', () => { it('Clears erasing ids and does not erase shapes on interrupt', () => {
editor.setCurrentTool('eraser') editor.setCurrentTool('eraser')
editor.expectPathToBe('root.eraser.idle') editor.expectToBeIn('eraser.idle')
const shapesBeforeCount = editor.currentPageShapes.length const shapesBeforeCount = editor.currentPageShapes.length
editor.pointerDown(0, 0) // near to box1 editor.pointerDown(0, 0) // near to box1
editor.expectPathToBe('root.eraser.pointing') editor.expectToBeIn('eraser.pointing')
expect(editor.getErasingShapeIds()).toEqual([ids.box1]) expect(editor.getErasingShapeIds()).toEqual([ids.box1])
@ -280,7 +280,7 @@ describe('When clicking', () => {
const shapesAfterCount = editor.currentPageShapes.length const shapesAfterCount = editor.currentPageShapes.length
editor.expectPathToBe('root.eraser.idle') editor.expectToBeIn('eraser.idle')
// Does NOT erase the shape // Does NOT erase the shape
expect(editor.getErasingShapeIds()).toEqual([]) expect(editor.getErasingShapeIds()).toEqual([])
@ -293,16 +293,16 @@ describe('When clicking and dragging', () => {
it('Enters erasing state on pointer move, adds contacted shapes to the apps.erasingShapeIds array, deletes them and clears erasingShapeIds on pointer up, restores shapes on undo and deletes again on redo', () => { it('Enters erasing state on pointer move, adds contacted shapes to the apps.erasingShapeIds array, deletes them and clears erasingShapeIds on pointer up, restores shapes on undo and deletes again on redo', () => {
editor.setCurrentTool('eraser') editor.setCurrentTool('eraser')
editor.expectPathToBe('root.eraser.idle') editor.expectToBeIn('eraser.idle')
editor.pointerDown(-100, -100) // outside of any shapes editor.pointerDown(-100, -100) // outside of any shapes
editor.expectPathToBe('root.eraser.pointing') editor.expectToBeIn('eraser.pointing')
expect(editor.getInstanceState().scribbles.length).toBe(0) expect(editor.getInstanceState().scribbles.length).toBe(0)
editor.pointerMove(50, 50) // inside of box1 editor.pointerMove(50, 50) // inside of box1
editor.expectPathToBe('root.eraser.erasing') editor.expectToBeIn('eraser.erasing')
jest.advanceTimersByTime(16) jest.advanceTimersByTime(16)
expect(editor.getInstanceState().scribbles.length).toBe(1) expect(editor.getInstanceState().scribbles.length).toBe(1)
@ -310,7 +310,7 @@ describe('When clicking and dragging', () => {
expect(editor.getErasingShapeIds()).toEqual([ids.box1]) expect(editor.getErasingShapeIds()).toEqual([ids.box1])
// editor.pointerUp() // editor.pointerUp()
// editor.expectPathToBe('root.eraser.idle') // editor.expectToBeIn('eraser.idle')
// expect(editor.erasingShapeIds).toEqual([]) // expect(editor.erasingShapeIds).toEqual([])
// expect(editor.getShape(ids.box1)).not.toBeDefined() // expect(editor.getShape(ids.box1)).not.toBeDefined()
@ -327,14 +327,14 @@ describe('When clicking and dragging', () => {
it('Clears erasing ids and does not erase shapes on cancel', () => { it('Clears erasing ids and does not erase shapes on cancel', () => {
editor.setCurrentTool('eraser') editor.setCurrentTool('eraser')
editor.expectPathToBe('root.eraser.idle') editor.expectToBeIn('eraser.idle')
editor.pointerDown(-100, -100) // outside of any shapes editor.pointerDown(-100, -100) // outside of any shapes
editor.pointerMove(50, 50) // inside of box1 editor.pointerMove(50, 50) // inside of box1
jest.advanceTimersByTime(16) jest.advanceTimersByTime(16)
expect(editor.getInstanceState().scribbles.length).toBe(1) expect(editor.getInstanceState().scribbles.length).toBe(1)
expect(editor.getErasingShapeIds()).toEqual([ids.box1]) expect(editor.getErasingShapeIds()).toEqual([ids.box1])
editor.cancel() editor.cancel()
editor.expectPathToBe('root.eraser.idle') editor.expectToBeIn('eraser.idle')
expect(editor.getErasingShapeIds()).toEqual([]) expect(editor.getErasingShapeIds()).toEqual([])
expect(editor.getShape(ids.box1)).toBeDefined() expect(editor.getShape(ids.box1)).toBeDefined()
}) })
@ -342,7 +342,7 @@ describe('When clicking and dragging', () => {
it('Excludes a group if it was hovered when the drag started', () => { it('Excludes a group if it was hovered when the drag started', () => {
editor.groupShapes([ids.box2, ids.box3], ids.group1) editor.groupShapes([ids.box2, ids.box3], ids.group1)
editor.setCurrentTool('eraser') editor.setCurrentTool('eraser')
editor.expectPathToBe('root.eraser.idle') editor.expectToBeIn('eraser.idle')
editor.pointerDown(275, 275) // in between box2 AND box3, so over of the new group editor.pointerDown(275, 275) // in between box2 AND box3, so over of the new group
editor.pointerMove(280, 280) // still outside of the new group editor.pointerMove(280, 280) // still outside of the new group
jest.advanceTimersByTime(16) jest.advanceTimersByTime(16)
@ -394,7 +394,7 @@ describe('When clicking and dragging', () => {
editor.pointerDown(-100, -100) editor.pointerDown(-100, -100)
editor.pointerMove(50, 50) editor.pointerMove(50, 50)
editor.interrupt() editor.interrupt()
editor.expectPathToBe('root.eraser.erasing') editor.expectToBeIn('eraser.erasing')
}) })
it('Starts a scribble on pointer down, updates it on pointer move, stops it on exit', () => { it('Starts a scribble on pointer down, updates it on pointer move, stops it on exit', () => {
@ -439,8 +439,8 @@ describe('When shift clicking', () => {
describe('When in the idle state', () => { describe('When in the idle state', () => {
it('Returns to select on cancel', () => { it('Returns to select on cancel', () => {
editor.setCurrentTool('hand') editor.setCurrentTool('hand')
editor.expectPathToBe('root.hand.idle') editor.expectToBeIn('hand.idle')
editor.cancel() editor.cancel()
editor.expectPathToBe('root.select.idle') editor.expectToBeIn('select.idle')
}) })
}) })

View file

@ -74,16 +74,16 @@ describe(HandTool, () => {
describe('When in the idle state', () => { describe('When in the idle state', () => {
it('Returns to select on cancel', () => { it('Returns to select on cancel', () => {
editor.setCurrentTool('hand') editor.setCurrentTool('hand')
editor.expectPathToBe('root.hand.idle') editor.expectToBeIn('hand.idle')
editor.cancel() editor.cancel()
editor.expectPathToBe('root.select.idle') editor.expectToBeIn('select.idle')
}) })
}) })
describe('When selecting the tool', () => { describe('When selecting the tool', () => {
it('selects the tool and enters the idle state', () => { it('selects the tool and enters the idle state', () => {
editor.setCurrentTool('hand') editor.setCurrentTool('hand')
editor.expectPathToBe('root.hand.idle') editor.expectToBeIn('hand.idle')
}) })
}) })
@ -91,19 +91,19 @@ describe('When in the idle state', () => {
it('Enters pointing state on pointer down', () => { it('Enters pointing state on pointer down', () => {
editor.setCurrentTool('hand') editor.setCurrentTool('hand')
editor.pointerDown(100, 100) editor.pointerDown(100, 100)
editor.expectPathToBe('root.hand.pointing') editor.expectToBeIn('hand.pointing')
}) })
it('Switches back to select tool on cancel', () => { it('Switches back to select tool on cancel', () => {
editor.setCurrentTool('hand') editor.setCurrentTool('hand')
editor.cancel() editor.cancel()
editor.expectPathToBe('root.select.idle') editor.expectToBeIn('select.idle')
}) })
it('Does nothing on interrupt', () => { it('Does nothing on interrupt', () => {
editor.setCurrentTool('hand') editor.setCurrentTool('hand')
editor.interrupt() editor.interrupt()
editor.expectPathToBe('root.hand.idle') editor.expectToBeIn('hand.idle')
}) })
}) })
@ -111,32 +111,32 @@ describe('When in the pointing state', () => {
it('Switches back to idle on cancel', () => { it('Switches back to idle on cancel', () => {
editor.setCurrentTool('hand') editor.setCurrentTool('hand')
editor.pointerDown(50, 50) editor.pointerDown(50, 50)
editor.expectPathToBe('root.hand.pointing') editor.expectToBeIn('hand.pointing')
editor.cancel() editor.cancel()
editor.expectPathToBe('root.hand.idle') editor.expectToBeIn('hand.idle')
}) })
it('Enters the dragging state on drag start', () => { it('Enters the dragging state on drag start', () => {
editor.setCurrentTool('hand') editor.setCurrentTool('hand')
editor.pointerDown(50, 50) editor.pointerDown(50, 50)
editor.pointerMove(51, 51) // not far enough! editor.pointerMove(51, 51) // not far enough!
editor.expectPathToBe('root.hand.pointing') editor.expectToBeIn('hand.pointing')
editor.pointerMove(55, 55) editor.pointerMove(55, 55)
editor.expectPathToBe('root.hand.dragging') editor.expectToBeIn('hand.dragging')
}) })
it('Returns to the idle state on cancel', () => { it('Returns to the idle state on cancel', () => {
editor.setCurrentTool('hand') editor.setCurrentTool('hand')
editor.pointerDown(50, 50) editor.pointerDown(50, 50)
editor.cancel() editor.cancel()
editor.expectPathToBe('root.hand.idle') editor.expectToBeIn('hand.idle')
}) })
it('Returns to the idle state on interrupt', () => { it('Returns to the idle state on interrupt', () => {
editor.setCurrentTool('hand') editor.setCurrentTool('hand')
editor.pointerDown(50, 50) editor.pointerDown(50, 50)
editor.interrupt() editor.interrupt()
editor.expectPathToBe('root.hand.idle') editor.expectToBeIn('hand.idle')
}) })
}) })
@ -146,11 +146,11 @@ describe('When in the dragging state', () => {
expect(editor.getCamera().x).toBe(0) expect(editor.getCamera().x).toBe(0)
expect(editor.getCamera().y).toBe(0) expect(editor.getCamera().y).toBe(0)
editor.pointerDown(50, 50) editor.pointerDown(50, 50)
editor.expectPathToBe('root.hand.pointing') editor.expectToBeIn('hand.pointing')
editor.pointerMove(75, 75) editor.pointerMove(75, 75)
expect(editor.getCamera().x).toBe(25) expect(editor.getCamera().x).toBe(25)
expect(editor.getCamera().y).toBe(25) expect(editor.getCamera().y).toBe(25)
editor.expectPathToBe('root.hand.dragging') editor.expectToBeIn('hand.dragging')
editor.pointerMove(100, 100) editor.pointerMove(100, 100)
expect(editor.getCamera().x).toBe(50) expect(editor.getCamera().x).toBe(50)
expect(editor.getCamera().y).toBe(50) expect(editor.getCamera().y).toBe(50)
@ -186,6 +186,6 @@ describe('When in the dragging state', () => {
editor.pointerDown(50, 50) editor.pointerDown(50, 50)
editor.pointerMove(100, 100) editor.pointerMove(100, 100)
editor.cancel() editor.cancel()
editor.expectPathToBe('root.hand.idle') editor.expectToBeIn('hand.idle')
}) })
}) })

View file

@ -189,12 +189,7 @@ export class TestEditor extends Editor {
} }
expectToBeIn = (path: string) => { expectToBeIn = (path: string) => {
expect(this.root.current.get()!.path.get()).toBe(path) expect(this.getPath()).toBe(path)
return this
}
expectPathToBe = (path: string) => {
expect(this.root.path.get()).toBe(path)
return this return this
} }

View file

@ -13,11 +13,11 @@ afterEach(() => {
describe('Set selected tool', () => { describe('Set selected tool', () => {
it('Selects a tool by its name', () => { it('Selects a tool by its name', () => {
editor.setCurrentTool('hand') editor.setCurrentTool('hand')
editor.expectPathToBe('root.hand.idle') editor.expectToBeIn('hand.idle')
}) })
it('Selects a tool by its deep path', () => { it('Selects a tool by its deep path', () => {
editor.setCurrentTool('hand.pointing') editor.setCurrentTool('hand.pointing')
editor.expectPathToBe('root.hand.pointing') editor.expectToBeIn('hand.pointing')
}) })
}) })

View file

@ -90,12 +90,12 @@ describe('When in the select.idle state', () => {
it('double clicking an image should transition to select.crop', () => { it('double clicking an image should transition to select.crop', () => {
editor.select(ids.boxA) editor.select(ids.boxA)
editor.expectPathToBe('root.select.idle') editor.expectToBeIn('select.idle')
expect(editor.getSelectedShapeIds()).toMatchObject([ids.boxA]) expect(editor.getSelectedShapeIds()).toMatchObject([ids.boxA])
expect(editor.croppingShapeId).toBe(null) expect(editor.croppingShapeId).toBe(null)
editor.doubleClick(550, 550, ids.imageB) editor.doubleClick(550, 550, ids.imageB)
editor.expectPathToBe('root.select.crop.idle') editor.expectToBeIn('select.crop.idle')
expect(editor.getSelectedShapeIds()).toMatchObject([ids.imageB]) expect(editor.getSelectedShapeIds()).toMatchObject([ids.imageB])
expect(editor.croppingShapeId).toBe(ids.imageB) expect(editor.croppingShapeId).toBe(ids.imageB)
@ -103,14 +103,14 @@ describe('When in the select.idle state', () => {
editor.undo() editor.undo()
// first selection should have been a mark // first selection should have been a mark
editor.expectPathToBe('root.select.idle') editor.expectToBeIn('select.idle')
expect(editor.getSelectedShapeIds()).toMatchObject([ids.imageB]) expect(editor.getSelectedShapeIds()).toMatchObject([ids.imageB])
expect(editor.croppingShapeId).toBe(null) expect(editor.croppingShapeId).toBe(null)
editor.undo() editor.undo()
// back to start // back to start
editor.expectPathToBe('root.select.idle') editor.expectToBeIn('select.idle')
expect(editor.getSelectedShapeIds()).toMatchObject([ids.boxA]) expect(editor.getSelectedShapeIds()).toMatchObject([ids.boxA])
expect(editor.croppingShapeId).toBe(null) expect(editor.croppingShapeId).toBe(null)
@ -118,7 +118,7 @@ describe('When in the select.idle state', () => {
.redo() // select again .redo() // select again
.redo() // crop again .redo() // crop again
editor.expectPathToBe('root.select.crop.idle') editor.expectToBeIn('select.crop.idle')
expect(editor.getSelectedShapeIds()).toMatchObject([ids.imageB]) expect(editor.getSelectedShapeIds()).toMatchObject([ids.imageB])
expect(editor.croppingShapeId).toBe(ids.imageB) expect(editor.croppingShapeId).toBe(ids.imageB)
}) })
@ -128,49 +128,49 @@ describe('When in the select.idle state', () => {
// corner (two shapes) // corner (two shapes)
editor editor
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
.select(ids.imageA, ids.imageB) .select(ids.imageA, ids.imageB)
.doubleClick(550, 550, { .doubleClick(550, 550, {
target: 'selection', target: 'selection',
handle: 'bottom_right', handle: 'bottom_right',
}) })
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
expect(editor.croppingShapeId).toBe(null) expect(editor.croppingShapeId).toBe(null)
// edge (two shapes) // edge (two shapes)
editor editor
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
.doubleClick(550, 550, { .doubleClick(550, 550, {
target: 'selection', target: 'selection',
handle: 'bottom', handle: 'bottom',
}) })
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
expect(editor.croppingShapeId).toBe(null) expect(editor.croppingShapeId).toBe(null)
// corner (one shape) // corner (one shape)
editor editor
.cancel() .cancel()
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
.select(ids.imageB) .select(ids.imageB)
.doubleClick(550, 550, { .doubleClick(550, 550, {
target: 'selection', target: 'selection',
handle: 'bottom_right', handle: 'bottom_right',
}) })
.expectPathToBe('root.select.crop.idle') .expectToBeIn('select.crop.idle')
expect(editor.croppingShapeId).toBe(ids.imageB) expect(editor.croppingShapeId).toBe(ids.imageB)
// edge (one shape) // edge (one shape)
editor editor
.cancel() .cancel()
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
.doubleClick(550, 550, { .doubleClick(550, 550, {
target: 'selection', target: 'selection',
handle: 'bottom', handle: 'bottom',
}) })
.expectPathToBe('root.select.crop.idle') .expectToBeIn('select.crop.idle')
expect(editor.croppingShapeId).toBe(ids.imageB) expect(editor.croppingShapeId).toBe(ids.imageB)
}) })
@ -178,19 +178,19 @@ describe('When in the select.idle state', () => {
it('when only an image is selected pressing enter should transition to select.crop', () => { it('when only an image is selected pressing enter should transition to select.crop', () => {
// two shapes // two shapes
editor editor
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
.select(ids.imageA, ids.imageB) .select(ids.imageA, ids.imageB)
.keyDown('Enter') .keyDown('Enter')
.keyUp('Enter') .keyUp('Enter')
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
// one image // one image
editor editor
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
.select(ids.imageB) .select(ids.imageB)
.keyDown('Enter') .keyDown('Enter')
.keyUp('Enter') .keyUp('Enter')
.expectPathToBe('root.select.crop.idle') .expectToBeIn('select.crop.idle')
expect(editor.croppingShapeId).toBe(ids.imageB) expect(editor.croppingShapeId).toBe(ids.imageB)
}) })
@ -199,101 +199,101 @@ describe('When in the select.idle state', () => {
// two shapes / edge // two shapes / edge
editor editor
.cancel() .cancel()
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
.select(ids.imageA, ids.imageB) .select(ids.imageA, ids.imageB)
.pointerDown(500, 550, { target: 'selection', handle: 'bottom', ctrlKey: true }) .pointerDown(500, 550, { target: 'selection', handle: 'bottom', ctrlKey: true })
.expectPathToBe('root.select.brushing') .expectToBeIn('select.brushing')
// two shapes / corner // two shapes / corner
editor editor
.cancel() .cancel()
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
.select(ids.imageA, ids.imageB) .select(ids.imageA, ids.imageB)
.pointerDown(500, 600, { target: 'selection', handle: 'bottom_left', ctrlKey: true }) .pointerDown(500, 600, { target: 'selection', handle: 'bottom_left', ctrlKey: true })
.expectPathToBe('root.select.brushing') .expectToBeIn('select.brushing')
// one shape / edge // one shape / edge
editor editor
.cancel() .cancel()
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
.select(ids.imageB) .select(ids.imageB)
.pointerDown(500, 550, { target: 'selection', handle: 'bottom', ctrlKey: true }) .pointerDown(500, 550, { target: 'selection', handle: 'bottom', ctrlKey: true })
.expectPathToBe('root.select.pointing_crop_handle') .expectToBeIn('select.pointing_crop_handle')
// one shape / corner // one shape / corner
editor editor
.cancel() .cancel()
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
.select(ids.imageB) .select(ids.imageB)
.pointerDown(500, 600, { target: 'selection', handle: 'bottom_left', ctrlKey: true }) .pointerDown(500, 600, { target: 'selection', handle: 'bottom_left', ctrlKey: true })
.expectPathToBe('root.select.pointing_crop_handle') .expectToBeIn('select.pointing_crop_handle')
}) })
}) })
describe('When in the crop.idle state', () => { describe('When in the crop.idle state', () => {
it('pressing escape should transition to select.idle', () => { it('pressing escape should transition to select.idle', () => {
editor editor
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
.doubleClick(550, 550, ids.imageB) .doubleClick(550, 550, ids.imageB)
.expectPathToBe('root.select.crop.idle') .expectToBeIn('select.crop.idle')
.cancel() .cancel()
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
}) })
it('pressing enter should transition to select.idle', () => { it('pressing enter should transition to select.idle', () => {
editor editor
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
.doubleClick(550, 550, ids.imageB) .doubleClick(550, 550, ids.imageB)
.expectPathToBe('root.select.crop.idle') .expectToBeIn('select.crop.idle')
.keyDown('Enter') .keyDown('Enter')
.keyUp('Enter') .keyUp('Enter')
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
}) })
it('pointing the canvas should point canvas', () => { it('pointing the canvas should point canvas', () => {
editor editor
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
.pointerMove(-100, -100) .pointerMove(-100, -100)
.pointerDown() .pointerDown()
.expectPathToBe('root.select.pointing_canvas') .expectToBeIn('select.pointing_canvas')
}) })
it('pointing some other shape should start pointing the shape', () => { it('pointing some other shape should start pointing the shape', () => {
editor editor
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
.pointerMove(550, 500) .pointerMove(550, 500)
.pointerDown() .pointerDown()
.expectPathToBe('root.select.pointing_shape') .expectToBeIn('select.pointing_shape')
}) })
it('pointing a selection handle should enter the select.pointing_crop_handle state', () => { it('pointing a selection handle should enter the select.pointing_crop_handle state', () => {
// corner // corner
editor editor
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
.doubleClick(550, 550, ids.imageB) .doubleClick(550, 550, ids.imageB)
.expectPathToBe('root.select.crop.idle') .expectToBeIn('select.crop.idle')
.pointerDown(500, 600, { target: 'selection', handle: 'bottom_left', ctrlKey: false }) .pointerDown(500, 600, { target: 'selection', handle: 'bottom_left', ctrlKey: false })
.expectPathToBe('root.select.pointing_crop_handle') .expectToBeIn('select.pointing_crop_handle')
//reset //reset
editor.cancel().cancel() editor.cancel().cancel()
// edge // edge
editor editor
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
.doubleClick(550, 550, ids.imageB) .doubleClick(550, 550, ids.imageB)
.expectPathToBe('root.select.crop.idle') .expectToBeIn('select.crop.idle')
.pointerDown(500, 600, { target: 'selection', handle: 'bottom', ctrlKey: false }) .pointerDown(500, 600, { target: 'selection', handle: 'bottom', ctrlKey: false })
.expectPathToBe('root.select.pointing_crop_handle') .expectToBeIn('select.pointing_crop_handle')
}) })
it('pointing the cropping image should enter the select.crop.translating_crop state', () => { it('pointing the cropping image should enter the select.crop.translating_crop state', () => {
editor editor
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
.doubleClick(550, 550, ids.imageB) .doubleClick(550, 550, ids.imageB)
.expectPathToBe('root.select.crop.idle') .expectToBeIn('select.crop.idle')
.pointerDown(500, 600, { target: 'shape', shape: editor.getShape(ids.imageB) }) .pointerDown(500, 600, { target: 'shape', shape: editor.getShape(ids.imageB) })
.expectPathToBe('root.select.crop.pointing_crop') .expectToBeIn('select.crop.pointing_crop')
expect(editor.croppingShapeId).toBe(ids.imageB) expect(editor.croppingShapeId).toBe(ids.imageB)
expect(editor.getSelectedShapeIds()).toMatchObject([ids.imageB]) expect(editor.getSelectedShapeIds()).toMatchObject([ids.imageB])
@ -301,11 +301,11 @@ describe('When in the crop.idle state', () => {
it('clicking another image shape should set that shape as the new cropping shape and transition to pointing_crop', () => { it('clicking another image shape should set that shape as the new cropping shape and transition to pointing_crop', () => {
editor editor
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
.doubleClick(550, 550, ids.imageB) .doubleClick(550, 550, ids.imageB)
.expectPathToBe('root.select.crop.idle') .expectToBeIn('select.crop.idle')
.pointerDown(100, 100, { target: 'shape', shape: editor.getShape(ids.imageA) }) .pointerDown(100, 100, { target: 'shape', shape: editor.getShape(ids.imageA) })
.expectPathToBe('root.select.crop.pointing_crop') .expectToBeIn('select.crop.pointing_crop')
expect(editor.croppingShapeId).toBe(ids.imageA) expect(editor.croppingShapeId).toBe(ids.imageA)
expect(editor.getSelectedShapeIds()).toMatchObject([ids.imageA]) expect(editor.getSelectedShapeIds()).toMatchObject([ids.imageA])
@ -313,22 +313,22 @@ describe('When in the crop.idle state', () => {
it('rotating will return to select.crop.idle', () => { it('rotating will return to select.crop.idle', () => {
editor editor
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
.doubleClick(550, 550, ids.imageB) .doubleClick(550, 550, ids.imageB)
.expectPathToBe('root.select.crop.idle') .expectToBeIn('select.crop.idle')
.pointerDown(500, 600, { target: 'selection', handle: 'top_left_rotate' }) .pointerDown(500, 600, { target: 'selection', handle: 'top_left_rotate' })
.expectPathToBe('root.select.pointing_rotate_handle') .expectToBeIn('select.pointing_rotate_handle')
.pointerMove(510, 590) .pointerMove(510, 590)
.expectPathToBe('root.select.rotating') .expectToBeIn('select.rotating')
.pointerUp() .pointerUp()
.expectPathToBe('root.select.crop.idle') .expectToBeIn('select.crop.idle')
}) })
it('nudges the cropped image', () => { it('nudges the cropped image', () => {
editor editor
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
.doubleClick(550, 550, ids.imageB) .doubleClick(550, 550, ids.imageB)
.expectPathToBe('root.select.crop.idle') .expectToBeIn('select.crop.idle')
const crop = () => editor.getShape<TLImageShape>(ids.imageB)!.props.crop! const crop = () => editor.getShape<TLImageShape>(ids.imageB)!.props.crop!
@ -374,34 +374,34 @@ describe('When in the crop.idle state', () => {
describe('When in the select.crop.pointing_crop state', () => { describe('When in the select.crop.pointing_crop state', () => {
it('pressing escape / cancel returns to select.crop.idle', () => { it('pressing escape / cancel returns to select.crop.idle', () => {
editor editor
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
.doubleClick(550, 550, ids.imageB) .doubleClick(550, 550, ids.imageB)
.expectPathToBe('root.select.crop.idle') .expectToBeIn('select.crop.idle')
.pointerDown(550, 550, { target: 'shape', shape: editor.getShape(ids.imageB) }) .pointerDown(550, 550, { target: 'shape', shape: editor.getShape(ids.imageB) })
.expectPathToBe('root.select.crop.pointing_crop') .expectToBeIn('select.crop.pointing_crop')
.cancel() .cancel()
.expectPathToBe('root.select.crop.idle') .expectToBeIn('select.crop.idle')
}) })
it('dragging enters select.crop.translating_crop', () => { it('dragging enters select.crop.translating_crop', () => {
editor editor
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
.doubleClick(550, 550, ids.imageB) .doubleClick(550, 550, ids.imageB)
.expectPathToBe('root.select.crop.idle') .expectToBeIn('select.crop.idle')
.pointerDown(550, 550, { target: 'shape', shape: editor.getShape(ids.imageB) }) .pointerDown(550, 550, { target: 'shape', shape: editor.getShape(ids.imageB) })
.expectPathToBe('root.select.crop.pointing_crop') .expectToBeIn('select.crop.pointing_crop')
.pointerMove(560, 560) .pointerMove(560, 560)
.expectPathToBe('root.select.crop.translating_crop') .expectToBeIn('select.crop.translating_crop')
}) })
}) })
describe('When in the select.crop.translating_crop state', () => { describe('When in the select.crop.translating_crop state', () => {
it('moving the pointer should adjust the crop', () => { it('moving the pointer should adjust the crop', () => {
editor editor
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
.doubleClick(550, 550, ids.imageB) .doubleClick(550, 550, ids.imageB)
.expectPathToBe('root.select.crop.idle') .expectToBeIn('select.crop.idle')
.pointerDown(550, 550, { target: 'shape', shape: editor.getShape(ids.imageB) }) .pointerDown(550, 550, { target: 'shape', shape: editor.getShape(ids.imageB) })
.expectPathToBe('root.select.crop.pointing_crop') .expectToBeIn('select.crop.pointing_crop')
const before = editor.getShape<TLImageShape>(ids.imageB)!.props.crop! const before = editor.getShape<TLImageShape>(ids.imageB)!.props.crop!
@ -413,7 +413,7 @@ describe('When in the select.crop.translating_crop state', () => {
// Move the pointer to the left // Move the pointer to the left
editor editor
.pointerMove(550 - imageWidth / 4, 550 - imageHeight / 4) .pointerMove(550 - imageWidth / 4, 550 - imageHeight / 4)
.expectPathToBe('root.select.crop.translating_crop') .expectToBeIn('select.crop.translating_crop')
// Update should have run right away // Update should have run right away
const afterFirst = editor.getShape<TLImageShape>(ids.imageB)!.props.crop! const afterFirst = editor.getShape<TLImageShape>(ids.imageB)!.props.crop!
@ -456,7 +456,7 @@ describe('When in the select.crop.translating_crop state', () => {
.pointerDown(550, 550, { target: 'shape', shape: editor.getShape(ids.imageB) }) .pointerDown(550, 550, { target: 'shape', shape: editor.getShape(ids.imageB) })
.keyDown('Shift') .keyDown('Shift')
.pointerMove(550 - imageWidth / 8, 550 - imageHeight / 8) .pointerMove(550 - imageWidth / 8, 550 - imageHeight / 8)
.expectPathToBe('root.select.crop.translating_crop') .expectToBeIn('select.crop.translating_crop')
// Update should have run right away // Update should have run right away
const afterShiftDown = editor.getShape<TLImageShape>(ids.imageB)!.props.crop! const afterShiftDown = editor.getShape<TLImageShape>(ids.imageB)!.props.crop!
@ -487,17 +487,17 @@ describe('When in the select.crop.translating_crop state', () => {
const before = editor.getShape<TLImageShape>(ids.imageB)!.props.crop! const before = editor.getShape<TLImageShape>(ids.imageB)!.props.crop!
editor editor
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
.doubleClick(550, 550, ids.imageB) .doubleClick(550, 550, ids.imageB)
.expectPathToBe('root.select.crop.idle') .expectToBeIn('select.crop.idle')
.pointerDown(550, 550, { target: 'shape', shape: editor.getShape(ids.imageB) }) .pointerDown(550, 550, { target: 'shape', shape: editor.getShape(ids.imageB) })
.expectPathToBe('root.select.crop.pointing_crop') .expectToBeIn('select.crop.pointing_crop')
.pointerMove(250, 250) .pointerMove(250, 250)
.expectPathToBe('root.select.crop.translating_crop') .expectToBeIn('select.crop.translating_crop')
expect(editor.getShape<TLImageShape>(ids.imageB)!.props.crop!).not.toMatchObject(before) expect(editor.getShape<TLImageShape>(ids.imageB)!.props.crop!).not.toMatchObject(before)
editor.cancel().expectPathToBe('root.select.crop.idle') editor.cancel().expectToBeIn('select.crop.idle')
expect(editor.getShape<TLImageShape>(ids.imageB)!.props.crop!).toMatchObject(before) expect(editor.getShape<TLImageShape>(ids.imageB)!.props.crop!).toMatchObject(before)
}) })
@ -506,17 +506,17 @@ describe('When in the select.crop.translating_crop state', () => {
const before = editor.getShape<TLImageShape>(ids.imageB)!.props.crop! const before = editor.getShape<TLImageShape>(ids.imageB)!.props.crop!
editor editor
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
.doubleClick(550, 550, ids.imageB) .doubleClick(550, 550, ids.imageB)
.expectPathToBe('root.select.crop.idle') .expectToBeIn('select.crop.idle')
.pointerDown(550, 550, { target: 'shape', shape: editor.getShape(ids.imageB) }) .pointerDown(550, 550, { target: 'shape', shape: editor.getShape(ids.imageB) })
.expectPathToBe('root.select.crop.pointing_crop') .expectToBeIn('select.crop.pointing_crop')
.pointerMove(250, 250) .pointerMove(250, 250)
.expectPathToBe('root.select.crop.translating_crop') .expectToBeIn('select.crop.translating_crop')
expect(editor.getShape<TLImageShape>(ids.imageB)!.props.crop!).not.toMatchObject(before) expect(editor.getShape<TLImageShape>(ids.imageB)!.props.crop!).not.toMatchObject(before)
editor.keyDown('Enter').keyUp('Enter').expectPathToBe('root.select.crop.idle') editor.keyDown('Enter').keyUp('Enter').expectToBeIn('select.crop.idle')
expect(editor.getShape<TLImageShape>(ids.imageB)!.props.crop!).not.toMatchObject(before) expect(editor.getShape<TLImageShape>(ids.imageB)!.props.crop!).not.toMatchObject(before)
}) })
@ -526,24 +526,24 @@ describe('When in the select.pointing_crop_handle state', () => {
it('moving the pointer should transition to select.cropping', () => { it('moving the pointer should transition to select.cropping', () => {
editor editor
.cancel() .cancel()
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
.select(ids.imageB) .select(ids.imageB)
.pointerDown(500, 600, { target: 'selection', handle: 'bottom_left', ctrlKey: true }) .pointerDown(500, 600, { target: 'selection', handle: 'bottom_left', ctrlKey: true })
.expectPathToBe('root.select.pointing_crop_handle') .expectToBeIn('select.pointing_crop_handle')
.pointerMove(510, 590) .pointerMove(510, 590)
.expectPathToBe('root.select.cropping') .expectToBeIn('select.cropping')
}) })
it('when entered from select.idle, pressing escape / cancel should return to idle and clear cropping idle', () => { it('when entered from select.idle, pressing escape / cancel should return to idle and clear cropping idle', () => {
// coming from select.idle // coming from select.idle
editor editor
.cancel() .cancel()
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
.select(ids.imageB) .select(ids.imageB)
.pointerDown(500, 600, { target: 'selection', handle: 'bottom_left', ctrlKey: true }) .pointerDown(500, 600, { target: 'selection', handle: 'bottom_left', ctrlKey: true })
.expectPathToBe('root.select.pointing_crop_handle') .expectToBeIn('select.pointing_crop_handle')
.cancel() .cancel()
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
expect(editor.croppingShapeId).toBe(null) expect(editor.croppingShapeId).toBe(null)
}) })
@ -552,13 +552,13 @@ describe('When in the select.pointing_crop_handle state', () => {
// coming from idle // coming from idle
editor editor
.cancel() .cancel()
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
.doubleClick(550, 550, ids.imageB) .doubleClick(550, 550, ids.imageB)
.expectPathToBe('root.select.crop.idle') .expectToBeIn('select.crop.idle')
.pointerDown(500, 600, { target: 'selection', handle: 'bottom_left', ctrlKey: false }) .pointerDown(500, 600, { target: 'selection', handle: 'bottom_left', ctrlKey: false })
.expectPathToBe('root.select.pointing_crop_handle') .expectToBeIn('select.pointing_crop_handle')
.cancel() .cancel()
.expectPathToBe('root.select.crop.idle') .expectToBeIn('select.crop.idle')
expect(editor.croppingShapeId).toBe(ids.imageB) expect(editor.croppingShapeId).toBe(ids.imageB)
}) })
@ -570,12 +570,12 @@ describe('When in the select.cropping state', () => {
editor editor
.cancel() .cancel()
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
.select(ids.imageB) .select(ids.imageB)
.pointerDown(500, 600, { target: 'selection', handle: 'bottom_left', ctrlKey: true }) .pointerDown(500, 600, { target: 'selection', handle: 'bottom_left', ctrlKey: true })
.expectPathToBe('root.select.pointing_crop_handle') .expectToBeIn('select.pointing_crop_handle')
.pointerMove(510, 590) .pointerMove(510, 590)
.expectPathToBe('root.select.cropping') .expectToBeIn('select.cropping')
expect(editor.getShape<TLImageShape>(ids.imageB)!.props.crop!).not.toMatchObject(before) expect(editor.getShape<TLImageShape>(ids.imageB)!.props.crop!).not.toMatchObject(before)
}) })
@ -585,14 +585,14 @@ describe('When in the select.cropping state', () => {
editor editor
.cancel() .cancel()
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
.select(ids.imageB) .select(ids.imageB)
.pointerDown(500, 600, { target: 'selection', handle: 'bottom_left', ctrlKey: true }) .pointerDown(500, 600, { target: 'selection', handle: 'bottom_left', ctrlKey: true })
.expectPathToBe('root.select.pointing_crop_handle') .expectToBeIn('select.pointing_crop_handle')
.pointerMove(510, 590) .pointerMove(510, 590)
.expectPathToBe('root.select.cropping') .expectToBeIn('select.cropping')
.cancel() .cancel()
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
expect(editor.getShape<TLImageShape>(ids.imageB)!.props.crop!).toMatchObject(before) expect(editor.getShape<TLImageShape>(ids.imageB)!.props.crop!).toMatchObject(before)
}) })
@ -602,15 +602,15 @@ describe('When in the select.cropping state', () => {
editor editor
.cancel() .cancel()
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
.doubleClick(550, 550, ids.imageB) .doubleClick(550, 550, ids.imageB)
.expectPathToBe('root.select.crop.idle') .expectToBeIn('select.crop.idle')
.pointerDown(500, 600, { target: 'selection', handle: 'bottom', ctrlKey: false }) .pointerDown(500, 600, { target: 'selection', handle: 'bottom', ctrlKey: false })
.expectPathToBe('root.select.pointing_crop_handle') .expectToBeIn('select.pointing_crop_handle')
.pointerMove(510, 590) .pointerMove(510, 590)
.expectPathToBe('root.select.cropping') .expectToBeIn('select.cropping')
.cancel() .cancel()
.expectPathToBe('root.select.crop.idle') .expectToBeIn('select.crop.idle')
expect(editor.getShape<TLImageShape>(ids.imageB)!.props.crop!).toMatchObject(before) expect(editor.getShape<TLImageShape>(ids.imageB)!.props.crop!).toMatchObject(before)
}) })
@ -620,15 +620,15 @@ describe('When in the select.cropping state', () => {
editor editor
.cancel() .cancel()
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
.doubleClick(550, 550, ids.imageB) .doubleClick(550, 550, ids.imageB)
.expectPathToBe('root.select.crop.idle') .expectToBeIn('select.crop.idle')
.pointerDown(500, 600, { target: 'selection', handle: 'bottom', ctrlKey: false }) .pointerDown(500, 600, { target: 'selection', handle: 'bottom', ctrlKey: false })
.expectPathToBe('root.select.pointing_crop_handle') .expectToBeIn('select.pointing_crop_handle')
.pointerMove(510, 590) .pointerMove(510, 590)
.expectPathToBe('root.select.cropping') .expectToBeIn('select.cropping')
.pointerUp() .pointerUp()
.expectPathToBe('root.select.crop.idle') .expectToBeIn('select.crop.idle')
expect(editor.getShape<TLImageShape>(ids.imageB)!.props.crop!).not.toMatchObject(before) expect(editor.getShape<TLImageShape>(ids.imageB)!.props.crop!).not.toMatchObject(before)
editor.undo() editor.undo()
@ -642,14 +642,14 @@ describe('When in the select.cropping state', () => {
editor editor
.cancel() .cancel()
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
.select(ids.imageB) .select(ids.imageB)
.pointerDown(500, 600, { target: 'selection', handle: 'bottom_left', ctrlKey: true }) .pointerDown(500, 600, { target: 'selection', handle: 'bottom_left', ctrlKey: true })
.expectPathToBe('root.select.pointing_crop_handle') .expectToBeIn('select.pointing_crop_handle')
.pointerMove(510, 590) .pointerMove(510, 590)
.expectPathToBe('root.select.cropping') .expectToBeIn('select.cropping')
.pointerUp() .pointerUp()
.expectPathToBe('root.select.idle') .expectToBeIn('select.idle')
expect(editor.getShape<TLImageShape>(ids.imageB)!.props.crop!).not.toMatchObject(before) expect(editor.getShape<TLImageShape>(ids.imageB)!.props.crop!).not.toMatchObject(before)
editor.undo() editor.undo()

View file

@ -49,7 +49,7 @@ describe('creating frames', () => {
it('can be canceled while dragging', () => { it('can be canceled while dragging', () => {
editor.setCurrentTool('frame') editor.setCurrentTool('frame')
editor.pointerDown(100, 100).pointerMove(200, 200) editor.pointerDown(100, 100).pointerMove(200, 200)
editor.expectPathToBe('root.select.resizing') editor.expectToBeIn('select.resizing')
editor.cancel() editor.cancel()
editor.pointerUp() editor.pointerUp()
expect(editor.getOnlySelectedShape()?.type).toBe(undefined) expect(editor.getOnlySelectedShape()?.type).toBe(undefined)
@ -139,7 +139,7 @@ describe('creating frames', () => {
it('switches back to the select tool after creating', () => { it('switches back to the select tool after creating', () => {
editor.setCurrentTool('frame') editor.setCurrentTool('frame')
editor.pointerDown(100, 100).pointerMove(49, 149).pointerUp() editor.pointerDown(100, 100).pointerMove(49, 149).pointerUp()
editor.expectPathToBe('root.select.idle') editor.expectToBeIn('select.idle')
}) })
}) })
@ -534,7 +534,7 @@ describe('frame shapes', () => {
// select from outside the frame // select from outside the frame
editor.setCurrentTool('select') editor.setCurrentTool('select')
editor.pointerDown(50, 50).pointerMove(150, 150) editor.pointerDown(50, 50).pointerMove(150, 150)
editor.expectPathToBe('root.select.brushing') editor.expectToBeIn('select.brushing')
expect(editor.getSelectedShapeIds()).toHaveLength(0) expect(editor.getSelectedShapeIds()).toHaveLength(0)
@ -547,13 +547,13 @@ describe('frame shapes', () => {
it('can be selected with scribble brushing only if the drag starts outside the frame', () => { it('can be selected with scribble brushing only if the drag starts outside the frame', () => {
editor.setCurrentTool('frame') editor.setCurrentTool('frame')
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200) editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
editor.expectPathToBe('root.select.idle') editor.expectToBeIn('select.idle')
// select from inside the frame // select from inside the frame
editor.selectNone() editor.selectNone()
editor.setCurrentTool('select') editor.setCurrentTool('select')
editor.pointerDown(150, 150).pointerMove(250, 250) editor.pointerDown(150, 150).pointerMove(250, 250)
editor.expectPathToBe('root.select.brushing') editor.expectToBeIn('select.brushing')
expect(editor.getSelectedShapeIds()).toHaveLength(0) expect(editor.getSelectedShapeIds()).toHaveLength(0)
}) })
@ -590,7 +590,7 @@ describe('frame shapes', () => {
editor.keyDown('alt').pointerDown(500, 500).pointerMove(300, 300) editor.keyDown('alt').pointerDown(500, 500).pointerMove(300, 300)
// Check if in scribble brushing mode // Check if in scribble brushing mode
editor.expectPathToBe('root.select.brushing') editor.expectToBeIn('select.brushing')
// Check if the inner box was selected // Check if the inner box was selected
editor.pointerUp(300, 300) editor.pointerUp(300, 300)

View file

@ -1034,7 +1034,7 @@ describe('the select tool', () => {
// it('should work while focused in a group if you start the drag from within the group', () => { // it('should work while focused in a group if you start the drag from within the group', () => {
// editor.select(ids.boxA) // editor.select(ids.boxA)
// editor.pointerDown(15, 5, groupAId).pointerMove(25, 9, ids.boxB) // editor.pointerDown(15, 5, groupAId).pointerMove(25, 9, ids.boxB)
// expect(editor.root.path.value).toBe(`root.select.brushing`) // expect(editor.getPath()).toBe(`select.brushing`)
// expect(editor.selectedShapeIds.includes(ids.boxA)).toBe(false) // expect(editor.selectedShapeIds.includes(ids.boxA)).toBe(false)
// expect(editor.selectedShapeIds.includes(ids.boxB)).toBe(true) // expect(editor.selectedShapeIds.includes(ids.boxB)).toBe(true)
@ -1048,7 +1048,7 @@ describe('the select tool', () => {
expect(editor.getShapesAtPoint({ x: -305, y: -5 })).toMatchObject([]) expect(editor.getShapesAtPoint({ x: -305, y: -5 })).toMatchObject([])
editor.pointerDown(-305, -5, { target: 'canvas' }).pointerMove(35, 9, ids.boxB) editor.pointerDown(-305, -5, { target: 'canvas' }).pointerMove(35, 9, ids.boxB)
expect(editor.root.path.get()).toBe(`root.select.brushing`) editor.expectToBeIn(`select.brushing`)
expect(editor.getSelectedShapeIds().includes(ids.boxA)).toBe(true) expect(editor.getSelectedShapeIds().includes(ids.boxA)).toBe(true)
expect(editor.getSelectedShapeIds().includes(ids.boxB)).toBe(true) expect(editor.getSelectedShapeIds().includes(ids.boxB)).toBe(true)

View file

@ -862,7 +862,7 @@ describe('When resizing a shape with children', () => {
handle: 'top_left', handle: 'top_left',
}) })
.pointerMove(0, 0) .pointerMove(0, 0)
.expectPathToBe('root.select.resizing') .expectToBeIn('select.resizing')
// A's model should have changed by the offset // A's model should have changed by the offset
.expectShapeToMatch({ .expectShapeToMatch({
id: ids.boxA, id: ids.boxA,
@ -926,7 +926,7 @@ describe('When resizing a shape with children', () => {
}) })
.pointerMove(0, 0) .pointerMove(0, 0)
// .pointerMove(10, 10) // .pointerMove(10, 10)
.expectPathToBe('root.select.resizing') .expectToBeIn('select.resizing')
// A's model should have changed by the offset // A's model should have changed by the offset
.expectShapeToMatch({ .expectShapeToMatch({
id: ids.boxB, id: ids.boxB,

View file

@ -19,7 +19,7 @@ describe(SelectTool, () => {
editor._transformPointerDownSpy.mockRestore() editor._transformPointerDownSpy.mockRestore()
editor._transformPointerUpSpy.mockRestore() editor._transformPointerUpSpy.mockRestore()
editor.setCurrentTool('select') editor.setCurrentTool('select')
editor.expectPathToBe('root.select.idle') editor.expectToBeIn('select.idle')
editor.doubleClick(50, 50, shapeId) editor.doubleClick(50, 50, shapeId)
expect(editor.getCurrentPageState().editingShapeId).toBe(shapeId) expect(editor.getCurrentPageState().editingShapeId).toBe(shapeId)
@ -37,7 +37,7 @@ describe(SelectTool, () => {
editor.pointerDown(150, 150).pointerUp() editor.pointerDown(150, 150).pointerUp()
expect(editor.getCurrentPageState().editingShapeId).toBe(null) expect(editor.getCurrentPageState().editingShapeId).toBe(null)
expect(editor.root.path.get()).toEqual('root.select.idle') editor.expectToBeIn('select.idle')
}) })
}) })
it('does not allow pressing undo to end up in the editing state', () => { it('does not allow pressing undo to end up in the editing state', () => {
@ -55,7 +55,7 @@ describe(SelectTool, () => {
editor.pointerDown(150, 150).pointerUp() editor.pointerDown(150, 150).pointerUp()
expect(editor.getCurrentPageState().editingShapeId).toBe(null) expect(editor.getCurrentPageState().editingShapeId).toBe(null)
expect(editor.root.path.get()).toEqual('root.select.idle') editor.expectToBeIn('select.idle')
editor.undo() editor.undo()
@ -144,7 +144,7 @@ describe('When brushing arrows', () => {
editor.setCurrentTool('select') editor.setCurrentTool('select')
editor.pointerDown(0, 45) editor.pointerDown(0, 45)
editor.pointerMove(100, 55) editor.pointerMove(100, 55)
editor.expectPathToBe('root.select.brushing') editor.expectToBeIn('select.brushing')
expect(editor.getSelectedShapeIds()).toStrictEqual([ids.arrow1]) expect(editor.getSelectedShapeIds()).toStrictEqual([ids.arrow1])
}) })
@ -166,7 +166,7 @@ describe('When brushing arrows', () => {
editor.setCurrentTool('select') editor.setCurrentTool('select')
editor.pointerDown(55, 45) editor.pointerDown(55, 45)
editor.pointerMove(45, 55) editor.pointerMove(45, 55)
editor.expectPathToBe('root.select.brushing') editor.expectToBeIn('select.brushing')
expect(editor.getSelectedShapeIds()).toStrictEqual([]) expect(editor.getSelectedShapeIds()).toStrictEqual([])
}) })
}) })

View file

@ -663,7 +663,7 @@ describe('when a frame has multiple children', () => {
expect(editor.getHoveredShapeId()).toBe(null) expect(editor.getHoveredShapeId()).toBe(null)
editor.pointerDown() editor.pointerDown()
editor.pointerMove(160, 160) editor.pointerMove(160, 160)
editor.expectPathToBe('root.select.brushing') editor.expectToBeIn('select.brushing')
editor.pointerUp() editor.pointerUp()
expect(editor.getSelectedShapeIds()).toEqual([]) expect(editor.getSelectedShapeIds()).toEqual([])
}) })
@ -673,7 +673,7 @@ describe('when a frame has multiple children', () => {
expect(editor.getHoveredShapeId()).toBe(null) expect(editor.getHoveredShapeId()).toBe(null)
editor.pointerDown() editor.pointerDown()
editor.pointerMove(30, 30) editor.pointerMove(30, 30)
editor.expectPathToBe('root.select.brushing') editor.expectToBeIn('select.brushing')
editor.pointerUp() editor.pointerUp()
expect(editor.getSelectedShapeIds()).toEqual([ids.box1]) expect(editor.getSelectedShapeIds()).toEqual([ids.box1])
}) })
@ -683,7 +683,7 @@ describe('when a frame has multiple children', () => {
expect(editor.getHoveredShapeId()).toBe(null) expect(editor.getHoveredShapeId()).toBe(null)
editor.pointerDown() editor.pointerDown()
editor.pointerMove(30, 30) editor.pointerMove(30, 30)
editor.expectPathToBe('root.select.brushing') editor.expectToBeIn('select.brushing')
editor.pointerUp() editor.pointerUp()
expect(editor.getSelectedShapeIds()).toEqual([ids.box1]) expect(editor.getSelectedShapeIds()).toEqual([ids.box1])
}) })
@ -696,7 +696,7 @@ describe('when a frame has multiple children', () => {
expect(editor.getHoveredShapeId()).toBe(null) expect(editor.getHoveredShapeId()).toBe(null)
editor.pointerDown() editor.pointerDown()
editor.pointerMove(99, 99) editor.pointerMove(99, 99)
editor.expectPathToBe('root.select.brushing') editor.expectToBeIn('select.brushing')
editor.pointerUp() editor.pointerUp()
expect(editor.getSelectedShapeIds()).toEqual([ids.box1, ids.box2]) expect(editor.getSelectedShapeIds()).toEqual([ids.box1, ids.box2])
}) })
@ -709,7 +709,7 @@ describe('when a frame has multiple children', () => {
expect(editor.getHoveredShapeId()).toBe(null) expect(editor.getHoveredShapeId()).toBe(null)
editor.pointerDown() editor.pointerDown()
editor.pointerMove(150, 150) editor.pointerMove(150, 150)
editor.expectPathToBe('root.select.brushing') editor.expectToBeIn('select.brushing')
editor.pointerUp() editor.pointerUp()
expect(editor.getSelectedShapeIds()).toEqual([ids.box1, ids.box2]) expect(editor.getSelectedShapeIds()).toEqual([ids.box1, ids.box2])
}) })
@ -722,7 +722,7 @@ describe('when a frame has multiple children', () => {
expect(editor.getHoveredShapeId()).toBe(null) expect(editor.getHoveredShapeId()).toBe(null)
editor.pointerDown() editor.pointerDown()
editor.pointerMove(150, 150) editor.pointerMove(150, 150)
editor.expectPathToBe('root.select.brushing') editor.expectToBeIn('select.brushing')
editor.pointerUp() editor.pointerUp()
expect(editor.getSelectedShapeIds()).toEqual([ids.frame1]) expect(editor.getSelectedShapeIds()).toEqual([ids.frame1])
}) })
@ -732,7 +732,7 @@ describe('when a frame has multiple children', () => {
expect(editor.getHoveredShapeId()).toBe(null) expect(editor.getHoveredShapeId()).toBe(null)
editor.pointerDown() editor.pointerDown()
editor.pointerMove(150, 150) editor.pointerMove(150, 150)
editor.expectPathToBe('root.select.brushing') editor.expectToBeIn('select.brushing')
editor.pointerUp() editor.pointerUp()
expect(editor.getSelectedShapeIds()).toEqual([ids.frame1]) expect(editor.getSelectedShapeIds()).toEqual([ids.frame1])
}) })

View file

@ -122,12 +122,12 @@ describe('When interacting with a shape...', () => {
handle: 'bottom_right', handle: 'bottom_right',
}) })
editor.expectPathToBe('root.select.pointing_resize_handle') editor.expectToBeIn('select.pointing_resize_handle')
editor.pointerMove(200, 200) editor.pointerMove(200, 200)
editor.expectPathToBe('root.select.resizing') editor.expectToBeIn('select.resizing')
editor.pointerMove(200, 210) editor.pointerMove(200, 210)
editor.pointerUp(200, 210) editor.pointerUp(200, 210)
editor.expectPathToBe('root.select.idle') editor.expectToBeIn('select.idle')
// Once on start (for frame only) // Once on start (for frame only)
expect(fnStart).toHaveBeenCalledTimes(1) expect(fnStart).toHaveBeenCalledTimes(1)

View file

@ -280,15 +280,15 @@ describe('When cloning...', () => {
const count1 = editor.currentPageShapes.length const count1 = editor.currentPageShapes.length
editor.pointerDown(50, 50, { shape: editor.getShape(groupId)!, target: 'shape' }) editor.pointerDown(50, 50, { shape: editor.getShape(groupId)!, target: 'shape' })
editor.expectPathToBe('root.select.pointing_shape') editor.expectToBeIn('select.pointing_shape')
editor.pointerMove(199, 199) editor.pointerMove(199, 199)
editor.expectPathToBe('root.select.translating') editor.expectToBeIn('select.translating')
expect(editor.currentPageShapes.length).toBe(count1) // 2 new box and group expect(editor.currentPageShapes.length).toBe(count1) // 2 new box and group
editor.keyDown('Alt') editor.keyDown('Alt')
editor.expectPathToBe('root.select.translating') editor.expectToBeIn('select.translating')
expect(editor.currentPageShapes.length).toBe(count1 + 3) // 2 new box and group expect(editor.currentPageShapes.length).toBe(count1 + 3) // 2 new box and group
editor.keyUp('Alt') editor.keyUp('Alt')