From 431ce73476f6116f3234b4d667fd3752f140ff89 Mon Sep 17 00:00:00 2001 From: David Sheldrick Date: Thu, 16 Nov 2023 12:07:33 +0000 Subject: [PATCH] No impure getters pt10 (#2235) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow up to #2189 ### Change Type - [x] `patch` — Bug fix --- .eslintrc.js | 5 + packages/editor/api-report.md | 10 +- packages/editor/api/api.json | 119 ++++++- packages/editor/src/lib/components/Canvas.tsx | 2 +- packages/editor/src/lib/editor/Editor.ts | 53 ++- .../src/lib/editor/managers/ClickManager.ts | 1 + .../editor/managers/HistoryManager.test.ts | 12 +- .../src/lib/editor/managers/HistoryManager.ts | 18 +- .../src/lib/editor/managers/SnapManager.ts | 21 +- .../editor/managers/UserPreferencesManager.ts | 8 + .../editor/src/lib/editor/tools/StateNode.ts | 22 +- packages/editor/src/lib/primitives/Box2d.ts | 21 ++ packages/editor/src/lib/primitives/Vec2d.ts | 1 + .../lib/primitives/geometry/CubicSpline2d.ts | 2 + .../src/lib/primitives/geometry/Edge2d.ts | 1 + .../src/lib/primitives/geometry/Ellipse2d.ts | 1 + .../src/lib/primitives/geometry/Geometry2d.ts | 5 + .../src/lib/primitives/geometry/Polyline2d.ts | 2 + .../src/lib/test/currentToolIdMask.test.ts | 2 +- .../editor/src/lib/utils/SharedStylesMap.ts | 1 + packages/state/api/api.json | 4 +- packages/state/src/lib/core/ArraySet.ts | 1 + packages/state/src/lib/core/Atom.ts | 1 + packages/state/src/lib/core/Computed.ts | 12 +- .../state/src/lib/core/EffectScheduler.ts | 2 + packages/state/src/lib/core/transactions.ts | 1 + packages/store/src/lib/StoreSchema.ts | 1 + .../SelectTool/childStates/DraggingHandle.ts | 4 +- .../lib/tools/SelectTool/childStates/Idle.ts | 2 +- .../childStates/PointingCropHandle.ts | 4 +- .../childStates/PointingRotateHandle.ts | 4 +- .../tools/SelectTool/childStates/Resizing.ts | 4 +- .../tools/SelectTool/childStates/Rotating.ts | 4 +- .../SelectTool/childStates/Translating.ts | 4 +- .../SelectTool/children/DraggingHandle.ts | 308 ++++++++++++++++++ .../tldraw/src/lib/tools/ZoomTool/ZoomTool.ts | 6 +- packages/tldraw/src/test/TestEditor.ts | 10 +- .../test/commands/moveShapesToPage.test.ts | 8 +- .../src/test/commands/resizeShape.test.ts | 8 +- .../src/test/commands/rotateShapes.test.ts | 4 +- .../src/test/commands/setCurrentPage.test.ts | 4 +- packages/tldraw/src/test/frames.test.ts | 12 +- packages/tldraw/src/test/getSnapLines.ts | 2 +- packages/tldraw/src/test/groups.test.ts | 4 +- packages/tldraw/src/test/paste.test.ts | 4 +- packages/tldraw/src/test/resizing.test.ts | 28 +- .../tldraw/src/test/testutils/getSnapLines.ts | 2 +- .../src/test/translating-snapping.test.ts | 92 +++--- packages/tldraw/src/test/translating.test.ts | 75 ++--- 49 files changed, 751 insertions(+), 171 deletions(-) create mode 100644 packages/tldraw/src/lib/tools/SelectTool/children/DraggingHandle.ts diff --git a/.eslintrc.js b/.eslintrc.js index c0d260acd..f20b7cf2a 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -44,6 +44,11 @@ module.exports = { ], 'local/no-export-star': 'error', 'no-only-tests/no-only-tests': 'error', + 'no-restricted-syntax': [ + 'error', + { selector: "MethodDefinition[kind='set']", message: 'Property setters are not allowed' }, + { selector: "MethodDefinition[kind='get']", message: 'Property getters are not allowed' }, + ], }, parser: '@typescript-eslint/parser', parserOptions: { diff --git a/packages/editor/api-report.md b/packages/editor/api-report.md index caa43cdf3..f64de3ab1 100644 --- a/packages/editor/api-report.md +++ b/packages/editor/api-report.md @@ -1803,6 +1803,8 @@ export class SnapManager { // (undocumented) getCurrentCommonAncestor(): TLShapeId | undefined; // (undocumented) + getLines(): SnapLine[]; + // (undocumented) getOutlinesInPageSpace(): Vec2d[][]; // (undocumented) getSnappablePoints(): SnapPoint[]; @@ -1822,7 +1824,7 @@ export class SnapManager { horizontal: Gap[]; vertical: Gap[]; }; - // (undocumented) + // @deprecated (undocumented) get lines(): SnapLine[]; // @deprecated (undocumented) get outlinesInPageSpace(): Vec2d[][]; @@ -1897,7 +1899,7 @@ export abstract class StateNode implements Partial { static children?: () => TLStateNodeConstructor[]; // (undocumented) children?: Record; - // (undocumented) + // @deprecated (undocumented) get currentToolIdMask(): string | undefined; set currentToolIdMask(id: string | undefined); _currentToolIdMask: Atom; @@ -1908,6 +1910,8 @@ export abstract class StateNode implements Partial { // (undocumented) exit: (info: any, from: string) => void; getCurrent(): StateNode | undefined; + // (undocumented) + getCurrentToolIdMask(): string | undefined; getIsActive(): boolean; getPath(): string; // (undocumented) @@ -1959,6 +1963,8 @@ export abstract class StateNode implements Partial { // (undocumented) _path: Computed; // (undocumented) + setCurrentToolIdMask(id: string | undefined): void; + // (undocumented) shapeType?: string; transition: (id: string, info?: any) => this; // (undocumented) diff --git a/packages/editor/api/api.json b/packages/editor/api/api.json index 032180b1b..f9cf5fd0e 100644 --- a/packages/editor/api/api.json +++ b/packages/editor/api/api.json @@ -33889,6 +33889,42 @@ "isAbstract": false, "name": "getCurrentCommonAncestor" }, + { + "kind": "Method", + "canonicalReference": "@tldraw/editor!SnapManager#getLines:member(1)", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "getLines(): " + }, + { + "kind": "Reference", + "text": "SnapLine", + "canonicalReference": "@tldraw/editor!SnapLine:type" + }, + { + "kind": "Content", + "text": "[]" + }, + { + "kind": "Content", + "text": ";" + } + ], + "isStatic": false, + "returnTypeTokenRange": { + "startIndex": 1, + "endIndex": 3 + }, + "releaseTag": "Public", + "isProtected": false, + "overloadIndex": 1, + "parameters": [], + "isOptional": false, + "isAbstract": false, + "name": "getLines" + }, { "kind": "Method", "canonicalReference": "@tldraw/editor!SnapManager#getOutlinesInPageSpace:member(1)", @@ -34209,7 +34245,7 @@ { "kind": "Property", "canonicalReference": "@tldraw/editor!SnapManager#lines:member", - "docComment": "", + "docComment": "/**\n * @deprecated\n *\n * use `getLines` instead\n */\n", "excerptTokens": [ { "kind": "Content", @@ -35318,7 +35354,7 @@ { "kind": "Property", "canonicalReference": "@tldraw/editor!StateNode#currentToolIdMask:member", - "docComment": "", + "docComment": "/**\n * @deprecated\n *\n * use `getCurrentToolIdMask()` instead\n */\n", "excerptTokens": [ { "kind": "Content", @@ -35472,6 +35508,37 @@ "isAbstract": false, "name": "getCurrent" }, + { + "kind": "Method", + "canonicalReference": "@tldraw/editor!StateNode#getCurrentToolIdMask:member(1)", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "getCurrentToolIdMask(): " + }, + { + "kind": "Content", + "text": "string | undefined" + }, + { + "kind": "Content", + "text": ";" + } + ], + "isStatic": false, + "returnTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "releaseTag": "Public", + "isProtected": false, + "overloadIndex": 1, + "parameters": [], + "isOptional": false, + "isAbstract": false, + "name": "getCurrentToolIdMask" + }, { "kind": "Method", "canonicalReference": "@tldraw/editor!StateNode#getIsActive:member(1)", @@ -36329,6 +36396,54 @@ "isProtected": false, "isAbstract": false }, + { + "kind": "Method", + "canonicalReference": "@tldraw/editor!StateNode#setCurrentToolIdMask:member(1)", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "setCurrentToolIdMask(id: " + }, + { + "kind": "Content", + "text": "string | undefined" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "void" + }, + { + "kind": "Content", + "text": ";" + } + ], + "isStatic": false, + "returnTypeTokenRange": { + "startIndex": 3, + "endIndex": 4 + }, + "releaseTag": "Public", + "isProtected": false, + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "id", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isOptional": false + } + ], + "isOptional": false, + "isAbstract": false, + "name": "setCurrentToolIdMask" + }, { "kind": "Property", "canonicalReference": "@tldraw/editor!StateNode#shapeType:member", diff --git a/packages/editor/src/lib/components/Canvas.tsx b/packages/editor/src/lib/components/Canvas.tsx index 9fdaab66f..5474bc2c9 100644 --- a/packages/editor/src/lib/components/Canvas.tsx +++ b/packages/editor/src/lib/components/Canvas.tsx @@ -189,7 +189,7 @@ function ZoomBrushWrapper() { function SnapLinesWrapper() { const editor = useEditor() - const lines = useValue('snapLines', () => editor.snaps.lines, [editor]) + const lines = useValue('snapLines', () => editor.snaps.getLines(), [editor]) const zoomLevel = useValue('zoomLevel', () => editor.getZoomLevel(), [editor]) const { SnapLine } = useEditorComponents() diff --git a/packages/editor/src/lib/editor/Editor.ts b/packages/editor/src/lib/editor/Editor.ts index 5a40b62a8..0800c1fb4 100644 --- a/packages/editor/src/lib/editor/Editor.ts +++ b/packages/editor/src/lib/editor/Editor.ts @@ -799,12 +799,13 @@ export class Editor extends EventEmitter { * @public */ @computed getCanUndo(): boolean { - return this.history.numUndos > 0 + return this.history.getNumUndos() > 0 } /** * @deprecated Use `getCanUndo` instead. */ + // eslint-disable-next-line no-restricted-syntax get canUndo(): boolean { return this.getCanUndo() } @@ -830,12 +831,13 @@ export class Editor extends EventEmitter { * @public */ @computed getCanRedo(): boolean { - return this.history.numRedos > 0 + return this.history.getNumRedos() > 0 } /** * @deprecated Use `getCanRedo` instead. */ + // eslint-disable-next-line no-restricted-syntax get canRedo(): boolean { return this.getCanRedo() } @@ -1141,6 +1143,7 @@ export class Editor extends EventEmitter { * @deprecated Use `getCurrentTool` instead. * @public */ + // eslint-disable-next-line no-restricted-syntax get currentTool() { return this.getCurrentTool() } @@ -1153,12 +1156,13 @@ export class Editor extends EventEmitter { @computed getCurrentToolId(): string { const currentTool = this.getCurrentTool() if (!currentTool) return '' - return currentTool.currentToolIdMask ?? currentTool.id + return currentTool.getCurrentToolIdMask() ?? currentTool.id } /** * @deprecated Use `getCurrentToolId` instead. */ + // eslint-disable-next-line no-restricted-syntax get currentToolId() { return this.getCurrentToolId() } @@ -1203,6 +1207,7 @@ export class Editor extends EventEmitter { /** * @deprecated Use `getDocumentSettings` instead. */ + // eslint-disable-next-line no-restricted-syntax get documentSettings() { return this.getDocumentSettings() } @@ -1231,6 +1236,7 @@ export class Editor extends EventEmitter { /** * @deprecated Use `getInstanceState` instead. */ + // eslint-disable-next-line no-restricted-syntax get instanceState() { return this.getInstanceState() } @@ -1315,6 +1321,7 @@ export class Editor extends EventEmitter { /** * @deprecated Use `getOpenMenus` instead. */ + // eslint-disable-next-line no-restricted-syntax get openMenus() { return this.getOpenMenus() } @@ -1374,6 +1381,7 @@ export class Editor extends EventEmitter { /** * @deprecated Use `getIsMenuOpen` instead. */ + // eslint-disable-next-line no-restricted-syntax get isMenuOpen() { return this.getIsMenuOpen() } @@ -1410,6 +1418,7 @@ export class Editor extends EventEmitter { /** * @deprecated Use `getPageStates` instead. */ + // eslint-disable-next-line no-restricted-syntax get pageStates() { return this.getPageStates() } @@ -1431,6 +1440,7 @@ export class Editor extends EventEmitter { /** * @deprecated Use `getCurrentPageState` instead. */ + // eslint-disable-next-line no-restricted-syntax get currentPageState() { return this.getCurrentPageState() } @@ -1496,6 +1506,7 @@ export class Editor extends EventEmitter { /** * @deprecated Use `getSelectedShapeIds` instead. */ + // eslint-disable-next-line no-restricted-syntax get selectedShapeIds() { return this.getSelectedShapeIds() } @@ -1514,6 +1525,7 @@ export class Editor extends EventEmitter { /** * @deprecated Use `getSelectedShapes` instead. */ + // eslint-disable-next-line no-restricted-syntax get selectedShapes() { return this.getSelectedShapes() } @@ -1690,6 +1702,7 @@ export class Editor extends EventEmitter { /** * @deprecated Use `getOnlySelectedShape` instead. */ + // eslint-disable-next-line no-restricted-syntax get onlySelectedShape() { return this.getOnlySelectedShape() } @@ -1713,6 +1726,7 @@ export class Editor extends EventEmitter { /** * @deprecated Use `getSelectionPageBounds` instead. */ + // eslint-disable-next-line no-restricted-syntax get selectionPageBounds() { return this.getSelectionPageBounds() } @@ -1743,6 +1757,7 @@ export class Editor extends EventEmitter { /** * @deprecated Use `getSelectionRotation` instead. */ + // eslint-disable-next-line no-restricted-syntax get selectionRotation() { return this.getSelectionRotation() } @@ -1790,6 +1805,7 @@ export class Editor extends EventEmitter { /** * @deprecated Use `getSelectionRotatedPageBounds` instead. */ + // eslint-disable-next-line no-restricted-syntax get selectionRotatedPageBounds() { return this.getSelectionRotatedPageBounds() } @@ -1808,6 +1824,7 @@ export class Editor extends EventEmitter { /** * @deprecated Use `getFocusedGroupId` instead. */ + // eslint-disable-next-line no-restricted-syntax get focusedGroupId() { return this.getFocusedGroupId() } @@ -1825,6 +1842,7 @@ export class Editor extends EventEmitter { /** * @deprecated Use `getFocusedGroup` instead. */ + // eslint-disable-next-line no-restricted-syntax get focusedGroup() { return this.getFocusedGroup() } @@ -1922,6 +1940,7 @@ export class Editor extends EventEmitter { /** * @deprecated Use `getEditingShapeId` instead. */ + // eslint-disable-next-line no-restricted-syntax get editingShapeId() { return this.getEditingShapeId() } @@ -1939,6 +1958,7 @@ export class Editor extends EventEmitter { /** * @deprecated Use `getEditingShape` instead. */ + // eslint-disable-next-line no-restricted-syntax get editingShape() { return this.getEditingShape() } @@ -1988,6 +2008,7 @@ export class Editor extends EventEmitter { /** * @deprecated Use `getHoveredShapeId` instead. */ + // eslint-disable-next-line no-restricted-syntax get hoveredShapeId() { return this.getHoveredShapeId() } @@ -2005,6 +2026,7 @@ export class Editor extends EventEmitter { /** * @deprecated Use `getHoveredShape` instead. */ + // eslint-disable-next-line no-restricted-syntax get hoveredShape() { return this.getHoveredShape() } @@ -2043,6 +2065,7 @@ export class Editor extends EventEmitter { /** * @deprecated Use `getHintingShapeIds` instead. */ + // eslint-disable-next-line no-restricted-syntax get hintingShapeIds() { return this.getHintingShapeIds() } @@ -2060,6 +2083,7 @@ export class Editor extends EventEmitter { /** * @deprecated Use `getHintingShape` instead. */ + // eslint-disable-next-line no-restricted-syntax get hintingShape() { return this.getHintingShape() } @@ -2101,6 +2125,7 @@ export class Editor extends EventEmitter { /** * @deprecated Use `getErasingShapeIds` instead. */ + // eslint-disable-next-line no-restricted-syntax get erasingShapeIds() { return this.getErasingShapeIds() } @@ -2118,6 +2143,7 @@ export class Editor extends EventEmitter { /** * @deprecated Use `getErasingShapes` instead. */ + // eslint-disable-next-line no-restricted-syntax get erasingShapes() { return this.getErasingShapes() } @@ -2174,6 +2200,7 @@ export class Editor extends EventEmitter { /** * @deprecated Use `getCroppingShapeId` instead. */ + // eslint-disable-next-line no-restricted-syntax get croppingShapeId() { return this.getCroppingShapeId() } @@ -2237,6 +2264,7 @@ export class Editor extends EventEmitter { /** * @deprecated Use `getZoomLevel` instead. */ + // eslint-disable-next-line no-restricted-syntax get zoomLevel() { return this.getZoomLevel() } @@ -2948,6 +2976,7 @@ export class Editor extends EventEmitter { /** * @deprecated Use `getViewportScreenBounds` instead. */ + // eslint-disable-next-line no-restricted-syntax get viewportScreenBounds() { return this.getViewportScreenBounds() } @@ -2964,6 +2993,7 @@ export class Editor extends EventEmitter { /** * @deprecated Use `getViewportScreenCenter` instead. */ + // eslint-disable-next-line no-restricted-syntax get viewportScreenCenter() { return this.getViewportScreenCenter() } @@ -2982,6 +3012,7 @@ export class Editor extends EventEmitter { /** * @deprecated Use `getViewportPageBounds` instead. */ + // eslint-disable-next-line no-restricted-syntax get viewportPageBounds() { return this.getViewportPageBounds() } @@ -2998,6 +3029,7 @@ export class Editor extends EventEmitter { /** * @deprecated Use `getViewportPageCenter` instead. */ + // eslint-disable-next-line no-restricted-syntax get viewportPageCenter() { return this.getViewportPageCenter() } @@ -3201,6 +3233,7 @@ export class Editor extends EventEmitter { /** * @deprecated Use `getCameraState` instead. */ + // eslint-disable-next-line no-restricted-syntax get cameraState() { return this.getCameraState() } @@ -3376,6 +3409,7 @@ export class Editor extends EventEmitter { /** * @deprecated Use `getRenderingShapes` instead. */ + // eslint-disable-next-line no-restricted-syntax get renderingShapes() { return this.getRenderingShapes() } @@ -3393,6 +3427,7 @@ export class Editor extends EventEmitter { * @deprecated Use `getRenderingBounds` instead. */ + // eslint-disable-next-line no-restricted-syntax get renderingBounds() { return this.getRenderingBounds() } @@ -3413,6 +3448,7 @@ export class Editor extends EventEmitter { /** * @deprecated Use `getRenderingBoundsExpanded` instead. */ + // eslint-disable-next-line no-restricted-syntax get renderingBoundsExpanded() { return this.getRenderingBoundsExpanded() } @@ -3473,6 +3509,7 @@ export class Editor extends EventEmitter { /** * @deprecated Use `getPages` instead. */ + // eslint-disable-next-line no-restricted-syntax get pages() { return this.getPages() } @@ -3482,6 +3519,7 @@ export class Editor extends EventEmitter { * * @public */ + // eslint-disable-next-line no-restricted-syntax get currentPage(): TLPage { const page = this.getPage(this.currentPageId)! return page @@ -3492,6 +3530,7 @@ export class Editor extends EventEmitter { * * @public */ + // eslint-disable-next-line no-restricted-syntax get currentPageId(): TLPageId { return this.getInstanceState().currentPageId } @@ -3521,6 +3560,7 @@ export class Editor extends EventEmitter { * * @public */ + // eslint-disable-next-line no-restricted-syntax get currentPageShapeIds() { return this._currentPageShapeIds.get() } @@ -3873,6 +3913,7 @@ export class Editor extends EventEmitter { /** * @deprecated Use `getAssets` instead. */ + // eslint-disable-next-line no-restricted-syntax get assets() { return this.getAssets() } @@ -4493,6 +4534,7 @@ export class Editor extends EventEmitter { /** * @deprecated Use `getCurrentPageBounds` instead. */ + // eslint-disable-next-line no-restricted-syntax get currentPageBounds() { return this.getCurrentPageBounds() } @@ -4809,6 +4851,7 @@ export class Editor extends EventEmitter { /** * @deprecated Use `getCurrentPageShapes` instead. */ + // eslint-disable-next-line no-restricted-syntax get currentPageShapes() { return this.getCurrentPageShapes() } @@ -4850,6 +4893,7 @@ export class Editor extends EventEmitter { /** * @deprecated Use `getCurrentPageShapesSorted` instead. */ + // eslint-disable-next-line no-restricted-syntax get currentPageShapesSorted() { return this.getCurrentPageShapesSorted() } @@ -4870,6 +4914,7 @@ export class Editor extends EventEmitter { /** * @deprecated Use `getCurrentPageRenderingShapesSorted` instead. */ + // eslint-disable-next-line no-restricted-syntax get currentPageRenderingShapesSorted() { return this.getCurrentPageRenderingShapesSorted() } @@ -7566,6 +7611,7 @@ export class Editor extends EventEmitter { /** * @deprecated Use `editor.sharedStyles` instead. */ + // eslint-disable-next-line no-restricted-syntax get sharedStyles() { return this.getSharedStyles() } @@ -7615,6 +7661,7 @@ export class Editor extends EventEmitter { /** * @deprecated Use `editor.sharedOpacity` instead. */ + // eslint-disable-next-line no-restricted-syntax get sharedOpacity() { return this.getSharedOpacity() } diff --git a/packages/editor/src/lib/editor/managers/ClickManager.ts b/packages/editor/src/lib/editor/managers/ClickManager.ts index aca8f0ad1..05b973a0c 100644 --- a/packages/editor/src/lib/editor/managers/ClickManager.ts +++ b/packages/editor/src/lib/editor/managers/ClickManager.ts @@ -88,6 +88,7 @@ export class ClickManager { * * @public */ + // eslint-disable-next-line no-restricted-syntax get clickState() { return this._clickState } diff --git a/packages/editor/src/lib/editor/managers/HistoryManager.test.ts b/packages/editor/src/lib/editor/managers/HistoryManager.test.ts index 6d03aa25a..5dd0124df 100644 --- a/packages/editor/src/lib/editor/managers/HistoryManager.test.ts +++ b/packages/editor/src/lib/editor/managers/HistoryManager.test.ts @@ -207,7 +207,7 @@ describe(HistoryManager, () => { expect(editor.getCount()).toBe(5) - expect(editor.history.numUndos).toBe(3) + expect(editor.history.getNumUndos()).toBe(3) }) it('allows ephemeral commands that do not affect the stack', () => { @@ -250,11 +250,11 @@ describe(HistoryManager, () => { it('does not allow new history entries to be pushed if a command invokes them while doing or undoing', () => { editor.incrementTwice() - expect(editor.history.numUndos).toBe(1) + expect(editor.history.getNumUndos()).toBe(1) expect(editor.getCount()).toBe(2) editor.history.undo() expect(editor.getCount()).toBe(0) - expect(editor.history.numUndos).toBe(0) + expect(editor.history.getNumUndos()).toBe(0) }) it('does not allow new history entries to be pushed if a command invokes them while bailing', () => { @@ -263,13 +263,13 @@ describe(HistoryManager, () => { editor.history.mark('2') editor.incrementTwice() editor.incrementTwice() - expect(editor.history.numUndos).toBe(5) + expect(editor.history.getNumUndos()).toBe(5) expect(editor.getCount()).toBe(6) editor.history.bail() expect(editor.getCount()).toBe(2) - expect(editor.history.numUndos).toBe(2) + expect(editor.history.getNumUndos()).toBe(2) editor.history.bailToMark('0') - expect(editor.history.numUndos).toBe(0) + expect(editor.history.getNumUndos()).toBe(0) expect(editor.getCount()).toBe(0) }) diff --git a/packages/editor/src/lib/editor/managers/HistoryManager.ts b/packages/editor/src/lib/editor/managers/HistoryManager.ts index b664bbd0f..fd29bcee0 100644 --- a/packages/editor/src/lib/editor/managers/HistoryManager.ts +++ b/packages/editor/src/lib/editor/managers/HistoryManager.ts @@ -33,13 +33,27 @@ export class HistoryManager< private _commands: Record> = {} - get numUndos() { + getNumUndos() { return this._undos.get().length } + /** + * @deprecated use `getNumUndos` instead + */ + // eslint-disable-next-line no-restricted-syntax + get numUndos() { + return this.getNumUndos() + } - get numRedos() { + getNumRedos() { return this._redos.get().length } + /** + * @deprecated use `getNumRedos` instead + */ + // eslint-disable-next-line no-restricted-syntax + get numRedos() { + return this.getNumRedos() + } createCommand = >( name: Name, diff --git a/packages/editor/src/lib/editor/managers/SnapManager.ts b/packages/editor/src/lib/editor/managers/SnapManager.ts index b9560a15f..6337ffa89 100644 --- a/packages/editor/src/lib/editor/managers/SnapManager.ts +++ b/packages/editor/src/lib/editor/managers/SnapManager.ts @@ -73,7 +73,7 @@ type NearestSnap = } | { // selection snaps to create a new gap of equal size to another gap - // on the opposide side of some shape + // on the opposite side of some shape type: 'gap_duplicate' gap: Gap protrusionDirection: 'left' | 'right' | 'top' | 'bottom' @@ -212,12 +212,20 @@ function dedupeGapSnaps(snaps: Array>) { export class SnapManager { private _snapLines = atom('snapLines', undefined) - get lines() { + getLines() { return this._snapLines.get() ?? (EMPTY_ARRAY as SnapLine[]) } + /** + * @deprecated use `getLines` instead + */ + // eslint-disable-next-line no-restricted-syntax + get lines() { + return this.getLines() + } + clear() { - if (this.lines.length) { + if (this.getLines().length) { this._snapLines.set(undefined) } } @@ -244,6 +252,7 @@ export class SnapManager { /** * @deprecated use `getSnapPointsCache` instead */ + // eslint-disable-next-line no-restricted-syntax get snapPointsCache() { return this.getSnapPointsCache() } @@ -255,6 +264,7 @@ export class SnapManager { /** * @deprecated use `getSnapThreshold` instead */ + // eslint-disable-next-line no-restricted-syntax get snapThreshold() { return this.getSnapThreshold() } @@ -302,6 +312,7 @@ export class SnapManager { * @deprecated use `getSnappableShapes` instead */ + // eslint-disable-next-line no-restricted-syntax get snappableShapes() { return this.getSnappableShapes() } @@ -314,6 +325,7 @@ export class SnapManager { /** * @deprecated use `getCurrentCommonAncestor` instead */ + // eslint-disable-next-line no-restricted-syntax get currentCommonAncestor() { return this.getCurrentCommonAncestor() } @@ -337,6 +349,7 @@ export class SnapManager { /** * @deprecated use `getSnappablePoints` instead */ + // eslint-disable-next-line no-restricted-syntax get snappablePoints() { return this.getSnappablePoints() } @@ -441,6 +454,7 @@ export class SnapManager { /** * @deprecated use `getVisibleGaps` instead */ + // eslint-disable-next-line no-restricted-syntax get visibleGaps() { return this.getVisibleGaps() } @@ -551,6 +565,7 @@ export class SnapManager { /** * @deprecated use `getOutlinesInPageSpace` instead */ + // eslint-disable-next-line no-restricted-syntax get outlinesInPageSpace() { return this.getOutlinesInPageSpace() } diff --git a/packages/editor/src/lib/editor/managers/UserPreferencesManager.ts b/packages/editor/src/lib/editor/managers/UserPreferencesManager.ts index b17bfde28..38ee147b6 100644 --- a/packages/editor/src/lib/editor/managers/UserPreferencesManager.ts +++ b/packages/editor/src/lib/editor/managers/UserPreferencesManager.ts @@ -29,6 +29,7 @@ export class UserPreferencesManager { /** * @deprecated use `getUserPreferences` instead */ + // eslint-disable-next-line no-restricted-syntax get userPreferences() { return this.getUserPreferences() } @@ -43,6 +44,7 @@ export class UserPreferencesManager { /** * @deprecated use `getIsDarkMode` instead */ + // eslint-disable-next-line no-restricted-syntax get isDarkMode() { return this.getIsDarkMode() } @@ -54,6 +56,7 @@ export class UserPreferencesManager { /** * @deprecated use `getAnimationSpeed` instead */ + // eslint-disable-next-line no-restricted-syntax get animationSpeed() { return this.getAnimationSpeed() } @@ -65,6 +68,7 @@ export class UserPreferencesManager { /** * @deprecated use `getId` instead */ + // eslint-disable-next-line no-restricted-syntax get id() { return this.getId() } @@ -76,6 +80,7 @@ export class UserPreferencesManager { /** * @deprecated use `getName` instead */ + // eslint-disable-next-line no-restricted-syntax get name() { return this.getName() } @@ -87,6 +92,7 @@ export class UserPreferencesManager { /** * @deprecated use `getLocale` instead */ + // eslint-disable-next-line no-restricted-syntax get locale() { return this.getLocale() } @@ -98,6 +104,7 @@ export class UserPreferencesManager { /** * @deprecated use `getColor` instead */ + // eslint-disable-next-line no-restricted-syntax get color() { return this.getColor() } @@ -109,6 +116,7 @@ export class UserPreferencesManager { /** * @deprecated use `getIsSnapMode` instead */ + // eslint-disable-next-line no-restricted-syntax get isSnapMode() { return this.getIsSnapMode() } diff --git a/packages/editor/src/lib/editor/tools/StateNode.ts b/packages/editor/src/lib/editor/tools/StateNode.ts index 17d492daa..386003805 100644 --- a/packages/editor/src/lib/editor/tools/StateNode.ts +++ b/packages/editor/src/lib/editor/tools/StateNode.ts @@ -75,7 +75,7 @@ export abstract class StateNode implements Partial { * * @public */ - @computed getPath() { + getPath() { return this._path.get() } _path: Computed @@ -85,7 +85,7 @@ export abstract class StateNode implements Partial { * * @public */ - @computed getCurrent() { + getCurrent() { return this._current.get() } private _current: Atom @@ -95,7 +95,7 @@ export abstract class StateNode implements Partial { * * @public */ - @computed getIsActive() { + getIsActive() { return this._isActive.get() } private _isActive: Atom @@ -182,11 +182,23 @@ export abstract class StateNode implements Partial { */ _currentToolIdMask = atom('curent tool id mask', undefined as string | undefined) - @computed get currentToolIdMask() { + /** + * @deprecated use `getCurrentToolIdMask()` instead + */ + // eslint-disable-next-line no-restricted-syntax + get currentToolIdMask() { + return this._currentToolIdMask.get() + } + // eslint-disable-next-line no-restricted-syntax + set currentToolIdMask(id: string | undefined) { + this._currentToolIdMask.set(id) + } + + getCurrentToolIdMask() { return this._currentToolIdMask.get() } - set currentToolIdMask(id: string | undefined) { + setCurrentToolIdMask(id: string | undefined) { this._currentToolIdMask.set(id) } diff --git a/packages/editor/src/lib/primitives/Box2d.ts b/packages/editor/src/lib/primitives/Box2d.ts index 5169da006..072f65442 100644 --- a/packages/editor/src/lib/primitives/Box2d.ts +++ b/packages/editor/src/lib/primitives/Box2d.ts @@ -36,76 +36,94 @@ export class Box2d { w = 0 h = 0 + // eslint-disable-next-line no-restricted-syntax get point() { return new Vec2d(this.x, this.y) } + // eslint-disable-next-line no-restricted-syntax set point(val: Vec2d) { this.x = val.x this.y = val.y } + // eslint-disable-next-line no-restricted-syntax get minX() { return this.x } + // eslint-disable-next-line no-restricted-syntax set minX(n: number) { this.x = n } + // eslint-disable-next-line no-restricted-syntax get midX() { return this.x + this.w / 2 } + // eslint-disable-next-line no-restricted-syntax get maxX() { return this.x + this.w } + // eslint-disable-next-line no-restricted-syntax get minY() { return this.y } + // eslint-disable-next-line no-restricted-syntax set minY(n: number) { this.y = n } + // eslint-disable-next-line no-restricted-syntax get midY() { return this.y + this.h / 2 } + // eslint-disable-next-line no-restricted-syntax get maxY() { return this.y + this.h } + // eslint-disable-next-line no-restricted-syntax get width() { return this.w } + // eslint-disable-next-line no-restricted-syntax set width(n: number) { this.w = n } + // eslint-disable-next-line no-restricted-syntax get height() { return this.h } + // eslint-disable-next-line no-restricted-syntax set height(n: number) { this.h = n } + // eslint-disable-next-line no-restricted-syntax get aspectRatio() { return this.width / this.height } + // eslint-disable-next-line no-restricted-syntax get center() { return new Vec2d(this.midX, this.midY) } + // eslint-disable-next-line no-restricted-syntax set center(v: Vec2d) { this.minX = v.x - this.width / 2 this.minY = v.y - this.height / 2 } + // eslint-disable-next-line no-restricted-syntax get corners() { return [ new Vec2d(this.minX, this.minY), @@ -115,6 +133,7 @@ export class Box2d { ] } + // eslint-disable-next-line no-restricted-syntax get snapPoints() { return [ new Vec2d(this.minX, this.minY), @@ -125,6 +144,7 @@ export class Box2d { ] } + // eslint-disable-next-line no-restricted-syntax get sides(): Array<[Vec2d, Vec2d]> { const { corners } = this return [ @@ -135,6 +155,7 @@ export class Box2d { ] } + // eslint-disable-next-line no-restricted-syntax get size(): Vec2d { return new Vec2d(this.w, this.h) } diff --git a/packages/editor/src/lib/primitives/Vec2d.ts b/packages/editor/src/lib/primitives/Vec2d.ts index 8fd702b6b..4c512a44d 100644 --- a/packages/editor/src/lib/primitives/Vec2d.ts +++ b/packages/editor/src/lib/primitives/Vec2d.ts @@ -8,6 +8,7 @@ export type VecLike = Vec2d | Vec2dModel export class Vec2d { constructor(public x = 0, public y = 0, public z = 1) {} + // eslint-disable-next-line no-restricted-syntax get pressure() { return this.z } diff --git a/packages/editor/src/lib/primitives/geometry/CubicSpline2d.ts b/packages/editor/src/lib/primitives/geometry/CubicSpline2d.ts index 5f3f91ada..bc132ce0e 100644 --- a/packages/editor/src/lib/primitives/geometry/CubicSpline2d.ts +++ b/packages/editor/src/lib/primitives/geometry/CubicSpline2d.ts @@ -15,6 +15,7 @@ export class CubicSpline2d extends Geometry2d { _segments?: CubicBezier2d[] + // eslint-disable-next-line no-restricted-syntax get segments() { if (!this._segments) { this._segments = [] @@ -49,6 +50,7 @@ export class CubicSpline2d extends Geometry2d { _length?: number + // eslint-disable-next-line no-restricted-syntax get length() { if (!this._length) { this._length = this.segments.reduce((acc, segment) => acc + segment.length, 0) diff --git a/packages/editor/src/lib/primitives/geometry/Edge2d.ts b/packages/editor/src/lib/primitives/geometry/Edge2d.ts index 3bd304dea..35f67a061 100644 --- a/packages/editor/src/lib/primitives/geometry/Edge2d.ts +++ b/packages/editor/src/lib/primitives/geometry/Edge2d.ts @@ -24,6 +24,7 @@ export class Edge2d extends Geometry2d { _length?: number + // eslint-disable-next-line no-restricted-syntax get length() { if (!this._length) { return this.d.len() diff --git a/packages/editor/src/lib/primitives/geometry/Ellipse2d.ts b/packages/editor/src/lib/primitives/geometry/Ellipse2d.ts index d802499e6..5084adc2e 100644 --- a/packages/editor/src/lib/primitives/geometry/Ellipse2d.ts +++ b/packages/editor/src/lib/primitives/geometry/Ellipse2d.ts @@ -24,6 +24,7 @@ export class Ellipse2d extends Geometry2d { _edges?: Edge2d[] + // eslint-disable-next-line no-restricted-syntax get edges() { if (!this._edges) { const { vertices } = this diff --git a/packages/editor/src/lib/primitives/geometry/Geometry2d.ts b/packages/editor/src/lib/primitives/geometry/Geometry2d.ts index 18a4b2794..a8c7322e8 100644 --- a/packages/editor/src/lib/primitives/geometry/Geometry2d.ts +++ b/packages/editor/src/lib/primitives/geometry/Geometry2d.ts @@ -78,6 +78,7 @@ export abstract class Geometry2d { _vertices: Vec2d[] | undefined + // eslint-disable-next-line no-restricted-syntax get vertices(): Vec2d[] { if (!this._vertices) { this._vertices = this.getVertices() @@ -92,6 +93,7 @@ export abstract class Geometry2d { _bounds: Box2d | undefined + // eslint-disable-next-line no-restricted-syntax get bounds(): Box2d { if (!this._bounds) { this._bounds = this.getBounds() @@ -101,6 +103,7 @@ export abstract class Geometry2d { _snapPoints: Vec2d[] | undefined + // eslint-disable-next-line no-restricted-syntax get snapPoints() { if (!this._snapPoints) { this._snapPoints = this.bounds.snapPoints @@ -108,12 +111,14 @@ export abstract class Geometry2d { return this._snapPoints } + // eslint-disable-next-line no-restricted-syntax get center() { return this.bounds.center } _area: number | undefined + // eslint-disable-next-line no-restricted-syntax get area() { if (!this._area) { this._area = this.getArea() diff --git a/packages/editor/src/lib/primitives/geometry/Polyline2d.ts b/packages/editor/src/lib/primitives/geometry/Polyline2d.ts index 790ec49cb..b2f6cb492 100644 --- a/packages/editor/src/lib/primitives/geometry/Polyline2d.ts +++ b/packages/editor/src/lib/primitives/geometry/Polyline2d.ts @@ -14,6 +14,7 @@ export class Polyline2d extends Geometry2d { _segments?: Edge2d[] + // eslint-disable-next-line no-restricted-syntax get segments() { if (!this._segments) { this._segments = [] @@ -34,6 +35,7 @@ export class Polyline2d extends Geometry2d { _length?: number + // eslint-disable-next-line no-restricted-syntax get length() { if (!this._length) { this._length = this.segments.reduce((acc, segment) => acc + segment.length, 0) diff --git a/packages/editor/src/lib/test/currentToolIdMask.test.ts b/packages/editor/src/lib/test/currentToolIdMask.test.ts index f877cf853..ea60924ae 100644 --- a/packages/editor/src/lib/test/currentToolIdMask.test.ts +++ b/packages/editor/src/lib/test/currentToolIdMask.test.ts @@ -16,7 +16,7 @@ class C extends StateNode { static override id = 'C' override onEnter = () => { - this.currentToolIdMask = 'A' + this.setCurrentToolIdMask('A') } } diff --git a/packages/editor/src/lib/utils/SharedStylesMap.ts b/packages/editor/src/lib/utils/SharedStylesMap.ts index c4e55356e..a4fb44889 100644 --- a/packages/editor/src/lib/utils/SharedStylesMap.ts +++ b/packages/editor/src/lib/utils/SharedStylesMap.ts @@ -52,6 +52,7 @@ export class ReadonlySharedStyleMap { return value.value } + // eslint-disable-next-line no-restricted-syntax get size() { return this.map.size } diff --git a/packages/state/api/api.json b/packages/state/api/api.json index dff3510f8..ff1844db3 100644 --- a/packages/state/api/api.json +++ b/packages/state/api/api.json @@ -596,7 +596,7 @@ { "kind": "Function", "canonicalReference": "@tldraw/state!computed:function(1)", - "docComment": "/**\n * Creates a computed signal.\n *\n * @param name - The name of the signal.\n *\n * @param compute - The function that computes the value of the signal.\n *\n * @param options - Options for the signal.\n *\n * @example\n * ```ts\n * const name = atom('name', 'John')\n * const greeting = computed('greeting', () => `Hello ${name.value}!`)\n * console.log(greeting.value) // 'Hello John!'\n * ```\n *\n * `computed` may also be used as a decorator for creating computed class properties.\n *\n * @example\n * ```ts\n * class Counter {\n * max = 100\n * count = atom(0)\n *\n * @computed get remaining() {\n * return this.max - this.count.value\n * }\n * }\n * ```\n *\n * You may optionally pass in a [[ComputedOptions]] when used as a decorator:\n *\n * @example\n * ```ts\n * class Counter {\n * max = 100\n * count = atom(0)\n *\n * @computed({isEqual: (a, b) => a === b})\n * get remaining() {\n * return this.max - this.count.value\n * }\n * }\n * ```\n *\n * @public\n */\n", + "docComment": "/**\n * Creates a computed signal.\n *\n * @param name - The name of the signal.\n *\n * @param compute - The function that computes the value of the signal.\n *\n * @param options - Options for the signal.\n *\n * @example\n * ```ts\n * const name = atom('name', 'John')\n * const greeting = computed('greeting', () => `Hello ${name.value}!`)\n * console.log(greeting.value) // 'Hello John!'\n * ```\n *\n * `computed` may also be used as a decorator for creating computed getter methods.\n *\n * @example\n * ```ts\n * class Counter {\n * max = 100\n * count = atom(0)\n *\n * @computed getRemaining() {\n * return this.max - this.count.value\n * }\n * }\n * ```\n *\n * You may optionally pass in a [[ComputedOptions]] when used as a decorator:\n *\n * @example\n * ```ts\n * class Counter {\n * max = 100\n * count = atom(0)\n *\n * @computed({isEqual: (a, b) => a === b})\n * getRemaining() {\n * return this.max - this.count.value\n * }\n * }\n * ```\n *\n * @public\n */\n", "excerptTokens": [ { "kind": "Content", @@ -1434,7 +1434,7 @@ { "kind": "Function", "canonicalReference": "@tldraw/state!getComputedInstance:function(1)", - "docComment": "/**\n * Retrieves the underlying computed instance for a given property created with the [[computed]] decorator.\n *\n * @param obj - The object\n *\n * @param propertyName - The property name\n *\n * @example\n * ```ts\n * class Counter {\n * max = 100\n * count = atom(0)\n *\n * @computed get remaining() {\n * return this.max - this.count.value\n * }\n * }\n *\n * const c = new Counter()\n * const remaining = getComputedInstance(c, 'remaining')\n * remaining.value === 100 // true\n * c.count.set(13)\n * remaining.value === 87 // true\n * ```\n *\n * @public\n */\n", + "docComment": "/**\n * Retrieves the underlying computed instance for a given property created with the [[computed]] decorator.\n *\n * @param obj - The object\n *\n * @param propertyName - The property name\n *\n * @example\n * ```ts\n * class Counter {\n * max = 100\n * count = atom(0)\n *\n * @computed getRemaining() {\n * return this.max - this.count.value\n * }\n * }\n *\n * const c = new Counter()\n * const remaining = getComputedInstance(c, 'getRemaining')\n * remaining.value === 100 // true\n * c.count.set(13)\n * remaining.value === 87 // true\n * ```\n *\n * @public\n */\n", "excerptTokens": [ { "kind": "Content", diff --git a/packages/state/src/lib/core/ArraySet.ts b/packages/state/src/lib/core/ArraySet.ts index 6ac59400d..8de6b089a 100644 --- a/packages/state/src/lib/core/ArraySet.ts +++ b/packages/state/src/lib/core/ArraySet.ts @@ -18,6 +18,7 @@ export class ArraySet { * * @returns True if this ArraySet has any elements, false otherwise. */ + // eslint-disable-next-line no-restricted-syntax get isEmpty() { if (this.array) { return this.arraySize === 0 diff --git a/packages/state/src/lib/core/Atom.ts b/packages/state/src/lib/core/Atom.ts index afd971675..0bf2a9f69 100644 --- a/packages/state/src/lib/core/Atom.ts +++ b/packages/state/src/lib/core/Atom.ts @@ -108,6 +108,7 @@ export class _Atom implements Atom { /** * @deprecated Use [[Atom.get]] instead. */ + // eslint-disable-next-line no-restricted-syntax get value() { logDotValueWarning() return this.get() diff --git a/packages/state/src/lib/core/Computed.ts b/packages/state/src/lib/core/Computed.ts index bc644a8ba..049a37ce3 100644 --- a/packages/state/src/lib/core/Computed.ts +++ b/packages/state/src/lib/core/Computed.ts @@ -135,6 +135,7 @@ export class _Computed implements Computed { children = new ArraySet() + // eslint-disable-next-line no-restricted-syntax get isActivelyListening(): boolean { return !this.children.isEmpty } @@ -212,6 +213,7 @@ export class _Computed implements Computed { /** * @deprecated Use [[get]] instead. */ + // eslint-disable-next-line no-restricted-syntax get value() { logDotValueWarning() return this.get() @@ -311,13 +313,13 @@ const isComputedMethodKey = '@@__isComputedMethod__@@' * max = 100 * count = atom(0) * - * @computed get remaining() { + * @computed getRemaining() { * return this.max - this.count.value * } * } * * const c = new Counter() - * const remaining = getComputedInstance(c, 'remaining') + * const remaining = getComputedInstance(c, 'getRemaining') * remaining.value === 100 // true * c.count.set(13) * remaining.value === 87 // true @@ -355,7 +357,7 @@ export function getComputedInstance( * console.log(greeting.value) // 'Hello John!' * ``` * - * `computed` may also be used as a decorator for creating computed class properties. + * `computed` may also be used as a decorator for creating computed getter methods. * * @example * ```ts @@ -363,7 +365,7 @@ export function getComputedInstance( * max = 100 * count = atom(0) * - * @computed get remaining() { + * @computed getRemaining() { * return this.max - this.count.value * } * } @@ -378,7 +380,7 @@ export function getComputedInstance( * count = atom(0) * * @computed({isEqual: (a, b) => a === b}) - * get remaining() { + * getRemaining() { * return this.max - this.count.value * } * } diff --git a/packages/state/src/lib/core/EffectScheduler.ts b/packages/state/src/lib/core/EffectScheduler.ts index 6b995d76f..773f1db78 100644 --- a/packages/state/src/lib/core/EffectScheduler.ts +++ b/packages/state/src/lib/core/EffectScheduler.ts @@ -60,6 +60,7 @@ export class EffectScheduler { * Whether this scheduler is attached and actively listening to its parents. * @public */ + // eslint-disable-next-line no-restricted-syntax get isActivelyListening() { return this._isActivelyListening } @@ -73,6 +74,7 @@ export class EffectScheduler { * The number of times this effect has been scheduled. * @public */ + // eslint-disable-next-line no-restricted-syntax get scheduleCount() { return this._scheduleCount } diff --git a/packages/state/src/lib/core/transactions.ts b/packages/state/src/lib/core/transactions.ts index adc574847..170228c76 100644 --- a/packages/state/src/lib/core/transactions.ts +++ b/packages/state/src/lib/core/transactions.ts @@ -22,6 +22,7 @@ class Transaction { * * @public */ + // eslint-disable-next-line no-restricted-syntax get isRoot() { return this.parent === null } diff --git a/packages/store/src/lib/StoreSchema.ts b/packages/store/src/lib/StoreSchema.ts index 533856f90..b5bc60fc8 100644 --- a/packages/store/src/lib/StoreSchema.ts +++ b/packages/store/src/lib/StoreSchema.ts @@ -69,6 +69,7 @@ export class StoreSchema { private readonly options: StoreSchemaOptions ) {} + // eslint-disable-next-line no-restricted-syntax get currentStoreVersion(): number { return this.options.snapshotMigrations?.currentVersion ?? 0 } diff --git a/packages/tldraw/src/lib/tools/SelectTool/childStates/DraggingHandle.ts b/packages/tldraw/src/lib/tools/SelectTool/childStates/DraggingHandle.ts index 7362ae33e..740d3f6a8 100644 --- a/packages/tldraw/src/lib/tools/SelectTool/childStates/DraggingHandle.ts +++ b/packages/tldraw/src/lib/tools/SelectTool/childStates/DraggingHandle.ts @@ -50,7 +50,7 @@ export class DraggingHandle extends StateNode { ) => { const { shape, isCreating, handle } = info this.info = info - this.parent.currentToolIdMask = info.onInteractionEnd + this.parent.setCurrentToolIdMask(info.onInteractionEnd) this.shapeId = shape.id this.markId = isCreating ? `creating:${shape.id}` : 'dragging handle' if (!isCreating) this.editor.mark(this.markId) @@ -166,7 +166,7 @@ export class DraggingHandle extends StateNode { } override onExit = () => { - this.parent.currentToolIdMask = undefined + this.parent.setCurrentToolIdMask(undefined) this.editor.setHintingShapes([]) this.editor.snaps.clear() this.editor.updateInstanceState( diff --git a/packages/tldraw/src/lib/tools/SelectTool/childStates/Idle.ts b/packages/tldraw/src/lib/tools/SelectTool/childStates/Idle.ts index c01cd3e3f..b2722b81b 100644 --- a/packages/tldraw/src/lib/tools/SelectTool/childStates/Idle.ts +++ b/packages/tldraw/src/lib/tools/SelectTool/childStates/Idle.ts @@ -22,7 +22,7 @@ export class Idle extends StateNode { static override id = 'idle' override onEnter = () => { - this.parent.currentToolIdMask = undefined + this.parent.setCurrentToolIdMask(undefined) updateHoveredId(this.editor) this.editor.updateInstanceState( { cursor: { type: 'default', rotation: 0 } }, diff --git a/packages/tldraw/src/lib/tools/SelectTool/childStates/PointingCropHandle.ts b/packages/tldraw/src/lib/tools/SelectTool/childStates/PointingCropHandle.ts index 49c9121bc..497b03d12 100644 --- a/packages/tldraw/src/lib/tools/SelectTool/childStates/PointingCropHandle.ts +++ b/packages/tldraw/src/lib/tools/SelectTool/childStates/PointingCropHandle.ts @@ -24,7 +24,7 @@ export class PointingCropHandle extends StateNode { override onEnter = (info: TLPointingCropHandleInfo) => { this.info = info - this.parent.currentToolIdMask = info.onInteractionEnd + this.parent.setCurrentToolIdMask(info.onInteractionEnd) const selectedShape = this.editor.getSelectedShapes()[0] if (!selectedShape) return @@ -37,7 +37,7 @@ export class PointingCropHandle extends StateNode { { cursor: { type: 'default', rotation: 0 } }, { ephemeral: true } ) - this.parent.currentToolIdMask = undefined + this.parent.setCurrentToolIdMask(undefined) } override onPointerMove: TLEventHandlers['onPointerMove'] = () => { diff --git a/packages/tldraw/src/lib/tools/SelectTool/childStates/PointingRotateHandle.ts b/packages/tldraw/src/lib/tools/SelectTool/childStates/PointingRotateHandle.ts index a5360c3f3..8096308d5 100644 --- a/packages/tldraw/src/lib/tools/SelectTool/childStates/PointingRotateHandle.ts +++ b/packages/tldraw/src/lib/tools/SelectTool/childStates/PointingRotateHandle.ts @@ -21,13 +21,13 @@ export class PointingRotateHandle extends StateNode { } override onEnter = (info: PointingRotateHandleInfo) => { - this.parent.currentToolIdMask = info.onInteractionEnd + this.parent.setCurrentToolIdMask(info.onInteractionEnd) this.info = info this.updateCursor() } override onExit = () => { - this.parent.currentToolIdMask = undefined + this.parent.setCurrentToolIdMask(undefined) this.editor.updateInstanceState( { cursor: { type: 'default', rotation: 0 } }, { ephemeral: true } diff --git a/packages/tldraw/src/lib/tools/SelectTool/childStates/Resizing.ts b/packages/tldraw/src/lib/tools/SelectTool/childStates/Resizing.ts index b3c0dabfa..a4af21a27 100644 --- a/packages/tldraw/src/lib/tools/SelectTool/childStates/Resizing.ts +++ b/packages/tldraw/src/lib/tools/SelectTool/childStates/Resizing.ts @@ -51,7 +51,7 @@ export class Resizing extends StateNode { this.info = info - this.parent.currentToolIdMask = info.onInteractionEnd + this.parent.setCurrentToolIdMask(info.onInteractionEnd) this.editAfterComplete = editAfterComplete this.creationCursorOffset = creationCursorOffset @@ -358,7 +358,7 @@ export class Resizing extends StateNode { } override onExit = () => { - this.parent.currentToolIdMask = undefined + this.parent.setCurrentToolIdMask(undefined) this.editor.updateInstanceState( { cursor: { type: 'default', rotation: 0 } }, { ephemeral: true } diff --git a/packages/tldraw/src/lib/tools/SelectTool/childStates/Rotating.ts b/packages/tldraw/src/lib/tools/SelectTool/childStates/Rotating.ts index f9698b9da..d2c06cf2b 100644 --- a/packages/tldraw/src/lib/tools/SelectTool/childStates/Rotating.ts +++ b/packages/tldraw/src/lib/tools/SelectTool/childStates/Rotating.ts @@ -27,7 +27,7 @@ export class Rotating extends StateNode { ) => { // Store the event information this.info = info - this.parent.currentToolIdMask = info.onInteractionEnd + this.parent.setCurrentToolIdMask(info.onInteractionEnd) this.markId = 'rotate start' this.editor.mark(this.markId) @@ -42,7 +42,7 @@ export class Rotating extends StateNode { override onExit = () => { this.editor.setCursor({ type: 'default', rotation: 0 }) - this.parent.currentToolIdMask = undefined + this.parent.setCurrentToolIdMask(undefined) this.snapshot = {} as TLRotationSnapshot } diff --git a/packages/tldraw/src/lib/tools/SelectTool/childStates/Translating.ts b/packages/tldraw/src/lib/tools/SelectTool/childStates/Translating.ts index 148d533ea..d7f1d86f9 100644 --- a/packages/tldraw/src/lib/tools/SelectTool/childStates/Translating.ts +++ b/packages/tldraw/src/lib/tools/SelectTool/childStates/Translating.ts @@ -49,7 +49,7 @@ export class Translating extends StateNode { const { isCreating = false, editAfterComplete = false } = info this.info = info - this.parent.currentToolIdMask = info.onInteractionEnd + this.parent.setCurrentToolIdMask(info.onInteractionEnd) this.isCreating = isCreating this.editAfterComplete = editAfterComplete @@ -76,7 +76,7 @@ export class Translating extends StateNode { } override onExit = () => { - this.parent.currentToolIdMask = undefined + this.parent.setCurrentToolIdMask(undefined) this.editor.off('tick', this.updateParent) this.selectionSnapshot = {} as any this.snapshot = {} as any diff --git a/packages/tldraw/src/lib/tools/SelectTool/children/DraggingHandle.ts b/packages/tldraw/src/lib/tools/SelectTool/children/DraggingHandle.ts new file mode 100644 index 000000000..c58e6fe88 --- /dev/null +++ b/packages/tldraw/src/lib/tools/SelectTool/children/DraggingHandle.ts @@ -0,0 +1,308 @@ +import { + Matrix2d, + StateNode, + TLArrowShape, + TLArrowShapeTerminal, + TLCancelEvent, + TLEnterEventHandler, + TLEventHandlers, + TLHandle, + TLKeyboardEvent, + TLPointerEventInfo, + TLShapeId, + TLShapePartial, + Vec2d, + deepCopy, + snapAngle, + sortByIndex, +} from '@tldraw/editor' + +export class DraggingHandle extends StateNode { + static override id = 'dragging_handle' + + shapeId = '' as TLShapeId + initialHandle = {} as TLHandle + initialAdjacentHandle = null as TLHandle | null + initialPagePoint = {} as Vec2d + + markId = '' + initialPageTransform: any + initialPageRotation: any + + info = {} as TLPointerEventInfo & { + shape: TLArrowShape + target: 'handle' + onInteractionEnd?: string + isCreating: boolean + } + + isPrecise = false + isPreciseId = null as TLShapeId | null + pointingId = null as TLShapeId | null + + override onEnter: TLEnterEventHandler = ( + info: TLPointerEventInfo & { + shape: TLArrowShape + target: 'handle' + onInteractionEnd?: string + isCreating: boolean + } + ) => { + const { shape, isCreating, handle } = info + this.info = info + this.parent.setCurrentToolIdMask(info.onInteractionEnd) + this.shapeId = shape.id + this.markId = isCreating ? `creating:${shape.id}` : 'dragging handle' + if (!isCreating) this.editor.mark(this.markId) + this.initialHandle = deepCopy(handle) + this.initialPageTransform = this.editor.getShapePageTransform(shape)! + this.initialPageRotation = this.initialPageTransform.rotation() + this.initialPagePoint = this.editor.inputs.originPagePoint.clone() + + this.editor.updateInstanceState( + { cursor: { type: isCreating ? 'cross' : 'grabbing', rotation: 0 } }, + { ephemeral: true } + ) + + // + + this.update() + + this.editor.select(this.shapeId) + } + + // Only relevant to arrows + private exactTimeout = -1 as any + + // Only relevant to arrows + private resetExactTimeout() { + if (this.exactTimeout !== -1) { + this.clearExactTimeout() + } + + this.exactTimeout = setTimeout(() => { + if (this.getIsActive() && !this.isPrecise) { + this.isPrecise = true + this.isPreciseId = this.pointingId + this.update() + } + this.exactTimeout = -1 + }, 750) + } + + // Only relevant to arrows + private clearExactTimeout() { + if (this.exactTimeout !== -1) { + clearTimeout(this.exactTimeout) + this.exactTimeout = -1 + } + } + + override onPointerMove: TLEventHandlers['onPointerMove'] = () => { + this.update() + } + + override onKeyDown: TLKeyboardEvent | undefined = () => { + this.update() + } + + override onKeyUp: TLKeyboardEvent | undefined = () => { + this.update() + } + + override onPointerUp: TLEventHandlers['onPointerUp'] = () => { + this.complete() + } + + override onComplete: TLEventHandlers['onComplete'] = () => { + this.complete() + } + + override onCancel: TLCancelEvent = () => { + this.cancel() + } + + override onExit = () => { + this.parent.setCurrentToolIdMask(undefined) + this.editor.setHintingShapes([]) + this.editor.snaps.clear() + this.editor.updateInstanceState( + { cursor: { type: 'default', rotation: 0 } }, + { ephemeral: true } + ) + } + + private complete() { + this.editor.snaps.clear() + + const { onInteractionEnd } = this.info + if (this.editor.getInstanceState().isToolLocked && onInteractionEnd) { + // Return to the tool that was active before this one, + // but only if tool lock is turned on! + this.editor.setCurrentTool(onInteractionEnd, { shapeId: this.shapeId }) + return + } + + this.parent.transition('idle') + } + + private cancel() { + this.editor.bailToMark(this.markId) + this.editor.snaps.clear() + + const { onInteractionEnd } = this.info + if (onInteractionEnd) { + // Return to the tool that was active before this one, + // whether tool lock is turned on or not! + this.editor.setCurrentTool(onInteractionEnd, { shapeId: this.shapeId }) + return + } + + this.parent.transition('idle') + } + + private update() { + const { editor, shapeId, initialPagePoint } = this + const { initialHandle, initialPageRotation, initialAdjacentHandle } = this + const hintingShapeIds = this.editor.getHintingShapeIds() + const { + user: { isSnapMode }, + snaps, + inputs: { currentPagePoint, shiftKey, ctrlKey, altKey, pointerVelocity }, + } = editor + + const initial = this.info.shape + const shape = editor.getShape(shapeId) + if (!shape) return + const util = editor.getShapeUtil(shape) + + let point = currentPagePoint + .clone() + .sub(initialPagePoint) + .rot(-initialPageRotation) + .add(initialHandle) + + if (shiftKey && initialAdjacentHandle && initialHandle.id !== 'middle') { + const angle = Vec2d.Angle(initialAdjacentHandle, point) + const snappedAngle = snapAngle(angle, 24) + const angleDifference = snappedAngle - angle + point = Vec2d.RotWith(point, initialAdjacentHandle, angleDifference) + } + + // Clear any existing snaps + editor.snaps.clear() + + if (initialHandle.canSnap && (isSnapMode ? !ctrlKey : ctrlKey)) { + // We're snapping + const pageTransform = editor.getShapePageTransform(shape.id) + if (!pageTransform) throw Error('Expected a page transform') + + // We want to skip the segments that include the handle, so + // find the index of the handle that shares the same index property + // as the initial dragging handle; this catches a quirk of create handles + const handleIndex = editor + .getShapeHandles(shape)! + .filter(({ type }) => type === 'vertex') + .sort(sortByIndex) + .findIndex(({ index }) => initialHandle.index === index) + + // Get all the outline segments from the shape + const additionalSegments = util + .getOutlineSegments(shape) + .map((segment) => Matrix2d.applyToPoints(pageTransform, segment)) + .filter((_segment, i) => i !== handleIndex - 1 && i !== handleIndex) + + const snapDelta = snaps.getSnappingHandleDelta({ + additionalSegments, + handlePoint: Matrix2d.applyToPoint(pageTransform, point), + }) + + if (snapDelta) { + snapDelta.rot(-editor.getShapeParentTransform(shape)!.rotation()) + point.add(snapDelta) + } + } + + const changes = util.onHandleChange?.(shape, { + handle: { + ...initialHandle, + x: point.x, + y: point.y, + }, + isPrecise: this.isPrecise || altKey, + initial: initial, + }) + + const next: TLShapePartial = { ...shape, ...changes } + + // Arrows + if (initialHandle.canBind) { + const bindingAfter = (next.props as any)[initialHandle.id] as TLArrowShapeTerminal | undefined + + if (bindingAfter?.type === 'binding') { + if (hintingShapeIds[0] !== bindingAfter.boundShapeId) { + editor.setHintingShapes([bindingAfter.boundShapeId]) + this.pointingId = bindingAfter.boundShapeId + this.isPrecise = pointerVelocity.len() < 0.5 || altKey + this.isPreciseId = this.isPrecise ? bindingAfter.boundShapeId : null + this.resetExactTimeout() + } + } else { + if (hintingShapeIds.length > 0) { + editor.setHintingShapes([]) + this.pointingId = null + this.isPrecise = false + this.isPreciseId = null + this.resetExactTimeout() + } + } + } + + if (changes) { + editor.updateShapes([next], { squashing: true }) + } + } +} diff --git a/packages/tldraw/src/lib/tools/ZoomTool/ZoomTool.ts b/packages/tldraw/src/lib/tools/ZoomTool/ZoomTool.ts index bbd902ab0..009ff75d8 100644 --- a/packages/tldraw/src/lib/tools/ZoomTool/ZoomTool.ts +++ b/packages/tldraw/src/lib/tools/ZoomTool/ZoomTool.ts @@ -13,17 +13,17 @@ export class ZoomTool extends StateNode { override onEnter = (info: TLPointerEventInfo & { onInteractionEnd: string }) => { this.info = info - this.currentToolIdMask = info.onInteractionEnd + this.parent.setCurrentToolIdMask(info.onInteractionEnd) this.updateCursor() } override onExit = () => { - this.currentToolIdMask = undefined + this.parent.setCurrentToolIdMask(undefined) this.editor.updateInstanceState( { zoomBrush: null, cursor: { type: 'default', rotation: 0 } }, { ephemeral: true } ) - this.currentToolIdMask = undefined + this.parent.setCurrentToolIdMask(undefined) } override onKeyDown: TLKeyboardEvent | undefined = () => { diff --git a/packages/tldraw/src/test/TestEditor.ts b/packages/tldraw/src/test/TestEditor.ts index 28b5858cd..09409315f 100644 --- a/packages/tldraw/src/test/TestEditor.ts +++ b/packages/tldraw/src/test/TestEditor.ts @@ -477,7 +477,11 @@ export class TestEditor extends Editor { .clone() .rotWith(this.getSelectionRotatedPageBounds()!.point, this.getSelectionRotation()) - const targetHandlePoint = Vec2d.RotWith(handlePoint, this.selectionPageCenter!, angleRadians) + const targetHandlePoint = Vec2d.RotWith( + handlePoint, + this.getSelectionPageCenter()!, + angleRadians + ) this.pointerDown(handlePoint.x, handlePoint.y, { target: 'selection', handle }) this.pointerMove(targetHandlePoint.x, targetHandlePoint.y, { shiftKey }) @@ -491,7 +495,7 @@ export class TestEditor extends Editor { * @readonly * @public */ - get selectionPageCenter() { + getSelectionPageCenter() { const selectionRotation = this.getSelectionRotation() const selectionBounds = this.getSelectionRotatedPageBounds() if (!selectionBounds) return null @@ -504,7 +508,7 @@ export class TestEditor extends Editor { } this.setCurrentTool('select') - const center = this.selectionPageCenter! + const center = this.getSelectionPageCenter()! this.pointerDown(center.x, center.y, this.getSelectedShapeIds()[0]) const numSteps = 10 diff --git a/packages/tldraw/src/test/commands/moveShapesToPage.test.ts b/packages/tldraw/src/test/commands/moveShapesToPage.test.ts index 890f91ff0..24bb49e17 100644 --- a/packages/tldraw/src/test/commands/moveShapesToPage.test.ts +++ b/packages/tldraw/src/test/commands/moveShapesToPage.test.ts @@ -61,25 +61,25 @@ describe('Editor.moveShapesToPage', () => { it('Adds undo items', () => { editor.history.clear() editor.moveShapesToPage([ids.box1], ids.page2) - expect(editor.history.numUndos).toBeGreaterThan(1) + expect(editor.history.getNumUndos()).toBeGreaterThan(1) }) it('Does nothing on an empty ids array', () => { editor.history.clear() editor.moveShapesToPage([], ids.page2) - expect(editor.history.numUndos).toBe(0) + expect(editor.history.getNumUndos()).toBe(0) }) it('Does nothing if the new page is not found or is deleted', () => { editor.history.clear() editor.moveShapesToPage([ids.box1], PageRecordType.createId('missing')) - expect(editor.history.numUndos).toBe(0) + expect(editor.history.getNumUndos()).toBe(0) }) it('Does not move shapes to the current page', () => { editor.history.clear() editor.moveShapesToPage([ids.box1], ids.page1) - expect(editor.history.numUndos).toBe(0) + expect(editor.history.getNumUndos()).toBe(0) }) it('Restores on undo / redo', () => { diff --git a/packages/tldraw/src/test/commands/resizeShape.test.ts b/packages/tldraw/src/test/commands/resizeShape.test.ts index 5b7cdaa35..38d9be05e 100644 --- a/packages/tldraw/src/test/commands/resizeShape.test.ts +++ b/packages/tldraw/src/test/commands/resizeShape.test.ts @@ -24,13 +24,13 @@ describe('resizing a shape', () => { editor.createShapes([{ id: ids.boxA, type: 'geo', props: { w: 100, h: 100 } }]) editor.mark('start') - const startHistoryLength = editor.history.numUndos + const startHistoryLength = editor.history.getNumUndos() editor.resizeShape(ids.boxA, { x: 2, y: 2 }) - expect(editor.history.numUndos).toBe(startHistoryLength + 1) + expect(editor.history.getNumUndos()).toBe(startHistoryLength + 1) editor.resizeShape(ids.boxA, { x: 2, y: 2 }) - expect(editor.history.numUndos).toBe(startHistoryLength + 1) + expect(editor.history.getNumUndos()).toBe(startHistoryLength + 1) editor.resizeShape(ids.boxA, { x: 2, y: 2 }) - expect(editor.history.numUndos).toBe(startHistoryLength + 1) + expect(editor.history.getNumUndos()).toBe(startHistoryLength + 1) expect(editor.getShapePageBounds(ids.boxA)).toCloselyMatchObject({ w: 800, diff --git a/packages/tldraw/src/test/commands/rotateShapes.test.ts b/packages/tldraw/src/test/commands/rotateShapes.test.ts index ffc41db2e..9bd162340 100644 --- a/packages/tldraw/src/test/commands/rotateShapes.test.ts +++ b/packages/tldraw/src/test/commands/rotateShapes.test.ts @@ -57,7 +57,7 @@ describe('editor.rotateShapes', () => { // Select the shape... editor.select(ids.box1, ids.box2) - const { selectionPageCenter } = editor + const selectionPageCenter = editor.getSelectionPageCenter() // Rotate the shape... editor.rotateShapesBy(editor.getSelectedShapeIds(), Math.PI) @@ -77,6 +77,6 @@ describe('editor.rotateShapes', () => { .expectShapeToMatch({ id: ids.box2, rotation: Math.PI }) // Are the centers the same? - expect(selectionPageCenter).toCloselyMatchObject(editor.selectionPageCenter!) + expect(selectionPageCenter).toCloselyMatchObject(editor.getSelectionPageCenter()!) }) }) diff --git a/packages/tldraw/src/test/commands/setCurrentPage.test.ts b/packages/tldraw/src/test/commands/setCurrentPage.test.ts index 44cd8766d..5f670379f 100644 --- a/packages/tldraw/src/test/commands/setCurrentPage.test.ts +++ b/packages/tldraw/src/test/commands/setCurrentPage.test.ts @@ -53,7 +53,7 @@ describe('setCurrentPage', () => { editor.setCurrentPage(editor.getPages()[1].id) editor.setCurrentPage(editor.getPages()[0].id) editor.setCurrentPage(editor.getPages()[0].id) - expect(editor.history.numUndos).toBe(1) + expect(editor.history.getNumUndos()).toBe(1) }) it('preserves the undo stack', () => { @@ -68,7 +68,7 @@ describe('setCurrentPage', () => { editor.setCurrentPage(editor.getPages()[0].id) editor.setCurrentPage(editor.getPages()[0].id) expect(editor.getShape(boxId)).toBeUndefined() - expect(editor.history.numUndos).toBe(1) + expect(editor.history.getNumUndos()).toBe(1) editor.redo() expect(editor.getShape(boxId)).not.toBeUndefined() }) diff --git a/packages/tldraw/src/test/frames.test.ts b/packages/tldraw/src/test/frames.test.ts index 6eca54384..26b82f7f9 100644 --- a/packages/tldraw/src/test/frames.test.ts +++ b/packages/tldraw/src/test/frames.test.ts @@ -127,7 +127,7 @@ describe('creating frames', () => { // x should snap editor.keyDown('Control') - expect(editor.snaps.lines).toHaveLength(1) + expect(editor.snaps.getLines()).toHaveLength(1) expect(editor.getShapePageBounds(editor.getOnlySelectedShape()!)).toMatchObject({ x: 50, y: 100, @@ -346,7 +346,7 @@ describe('frame shapes', () => { expect(editor.getShapePageBounds(ids.boxA)).toMatchObject({ y: 49 }) editor.keyDown('Control') expect(editor.getShapePageBounds(ids.boxA)).toMatchObject({ y: 50 }) - expect(editor.snaps.lines).toHaveLength(1) + expect(editor.snaps.getLines()).toHaveLength(1) }) it("does not allow outside shapes to snap to the frame's children", () => { @@ -382,12 +382,12 @@ describe('frame shapes', () => { w: 5, h: 5, }) - expect(editor.snaps.lines).toHaveLength(0) + expect(editor.snaps.getLines()).toHaveLength(0) // and if we unparent the box it should snap editor.reparentShapes([innerBoxId], editor.currentPageId) editor.pointerMove(287.5, 126.5).pointerMove(277.5, 126.5) - expect(editor.snaps.lines).toHaveLength(1) + expect(editor.snaps.getLines()).toHaveLength(1) expect(editor.getShapePageBounds(editor.getOnlySelectedShape()!)).toMatchObject({ x: 275, y: 125, @@ -414,12 +414,12 @@ describe('frame shapes', () => { editor.setCurrentTool('select') editor.pointerDown(150, 150, innerBoxId).pointerMove(150, 50).pointerMove(150, 148) editor.keyDown('Control') - expect(editor.snaps.lines).toHaveLength(0) + expect(editor.snaps.getLines()).toHaveLength(0) // move shape inside the frame to make sure it snaps in there editor.reparentShapes([outerBoxId], frameId).pointerMove(150, 149, { ctrlKey: true }) - expect(editor.snaps.lines).toHaveLength(1) + expect(editor.snaps.getLines()).toHaveLength(1) }) it('masks its children', () => { diff --git a/packages/tldraw/src/test/getSnapLines.ts b/packages/tldraw/src/test/getSnapLines.ts index 53bede17c..6d4060eda 100644 --- a/packages/tldraw/src/test/getSnapLines.ts +++ b/packages/tldraw/src/test/getSnapLines.ts @@ -8,7 +8,7 @@ const simplifyNumber = (n: number) => { } export const getSnapLines = (scene: Editor) => { const result = [] - for (const snap of scene.snaps.lines) { + for (const snap of scene.snaps.getLines()) { if (snap.type !== 'points') { throw new Error('Expected only points snap') } diff --git a/packages/tldraw/src/test/groups.test.ts b/packages/tldraw/src/test/groups.test.ts index 375b3111c..38f2bd1e5 100644 --- a/packages/tldraw/src/test/groups.test.ts +++ b/packages/tldraw/src/test/groups.test.ts @@ -1922,14 +1922,14 @@ describe('snapping', () => { editor.select(groupCId) editor.pointerDown(10, 5, groupCId) editor.pointerMove(80, 5, groupCId, { ctrlKey: true }) - expect(editor.snaps.lines.length).toBe(0) + expect(editor.snaps.getLines().length).toBe(0) }) it('does not happen between children and thier group', () => { editor.select(ids.boxD) editor.pointerDown(65, 5, ids.boxD) editor.pointerMove(80, 105, ids.boxD, { ctrlKey: true }) - expect(editor.snaps.lines.length).toBe(0) + expect(editor.snaps.getLines().length).toBe(0) }) }) diff --git a/packages/tldraw/src/test/paste.test.ts b/packages/tldraw/src/test/paste.test.ts index b0a6f5ae1..be5124e8a 100644 --- a/packages/tldraw/src/test/paste.test.ts +++ b/packages/tldraw/src/test/paste.test.ts @@ -224,7 +224,7 @@ describe('When pasting', () => { // Should put the pasted shapes centered in the frame editor.select(shapes.new.box1!.id, shapes.new.box1!.id) - expect(editor.selectionPageCenter).toMatchObject( + expect(editor.getSelectionPageCenter()).toMatchObject( editor.getPageCenter(editor.getShape(ids.frame1)!)! ) }) @@ -317,7 +317,7 @@ describe('When pasting', () => { // Should put the pasted shapes centered in the frame editor.select(shapes.new.box1!.id, shapes.new.box1!.id) - expect(editor.selectionPageCenter).toMatchObject(editor.getPageCenter(shapes.old.frame1)!) + expect(editor.getSelectionPageCenter()).toMatchObject(editor.getPageCenter(shapes.old.frame1)!) }) }) diff --git a/packages/tldraw/src/test/resizing.test.ts b/packages/tldraw/src/test/resizing.test.ts index f62dbf585..cc01af2a2 100644 --- a/packages/tldraw/src/test/resizing.test.ts +++ b/packages/tldraw/src/test/resizing.test.ts @@ -940,8 +940,10 @@ describe('When resizing a shape with children', () => { }) function getGapAndPointLines() { - const gapLines = editor.snaps.lines.filter((snap) => snap.type === 'gaps') as GapsSnapLine[] - const pointLines = editor.snaps.lines.filter((snap) => snap.type === 'points') as PointsSnapLine[] + const gapLines = editor.snaps.getLines().filter((snap) => snap.type === 'gaps') as GapsSnapLine[] + const pointLines = editor.snaps + .getLines() + .filter((snap) => snap.type === 'points') as PointsSnapLine[] return { gapLines, pointLines } } @@ -985,12 +987,12 @@ describe('snapping while resizing', () => { .pointerMove(115, 59, { ctrlKey: true }) expect(editor.getShape(ids.boxX)).toMatchObject({ x: 80, y: 60, props: { w: 60, h: 80 } }) - expect(editor.snaps.lines.length).toBe(1) + expect(editor.snaps.getLines().length).toBe(1) // moving the mouse horizontally should not change things editor.pointerMove(15, 65, { ctrlKey: true }) expect(editor.getShape(ids.boxX)).toMatchObject({ x: 80, y: 60, props: { w: 60, h: 80 } }) - expect(editor.snaps.lines.length).toBe(1) + expect(editor.snaps.getLines().length).toBe(1) expect(getGapAndPointLines().pointLines[0].points).toHaveLength(6) @@ -998,7 +1000,7 @@ describe('snapping while resizing', () => { editor.pointerMove(15, 43, { ctrlKey: true }) expect(editor.getShape(ids.boxX)).toMatchObject({ x: 80, y: 40, props: { w: 60, h: 100 } }) - expect(editor.snaps.lines.length).toBe(1) + expect(editor.snaps.getLines().length).toBe(1) expect(getGapAndPointLines().pointLines[0].points).toHaveLength(4) }) @@ -1015,7 +1017,7 @@ describe('snapping while resizing', () => { .pointerMove(156, 115, { ctrlKey: true }) expect(editor.getShape(ids.boxX)).toMatchObject({ x: 80, y: 80, props: { w: 80, h: 60 } }) - expect(editor.snaps.lines.length).toBe(1) + expect(editor.snaps.getLines().length).toBe(1) expect(getGapAndPointLines().pointLines[0].points).toHaveLength(6) @@ -1026,7 +1028,7 @@ describe('snapping while resizing', () => { // snap to left edge of B editor.pointerMove(173, 280, { ctrlKey: true }) expect(editor.getShape(ids.boxX)).toMatchObject({ x: 80, y: 80, props: { w: 100, h: 60 } }) - expect(editor.snaps.lines.length).toBe(1) + expect(editor.snaps.getLines().length).toBe(1) expect(getGapAndPointLines().pointLines[0].points).toHaveLength(4) }) @@ -1041,19 +1043,19 @@ describe('snapping while resizing', () => { .pointerMove(115, 159, { ctrlKey: true }) expect(editor.getShape(ids.boxX)).toMatchObject({ x: 80, y: 80, props: { w: 60, h: 80 } }) - expect(editor.snaps.lines.length).toBe(1) + expect(editor.snaps.getLines().length).toBe(1) expect(getGapAndPointLines().pointLines[0].points).toHaveLength(6) // changing horzontal mouse position should not change things editor.pointerMove(315, 163, { ctrlKey: true }) expect(editor.getShape(ids.boxX)).toMatchObject({ x: 80, y: 80, props: { w: 60, h: 80 } }) - expect(editor.snaps.lines.length).toBe(1) + expect(editor.snaps.getLines().length).toBe(1) // snap to top edge of C editor.pointerMove(115, 183, { ctrlKey: true }) expect(editor.getShape(ids.boxX)).toMatchObject({ x: 80, y: 80, props: { w: 60, h: 100 } }) - expect(editor.snaps.lines.length).toBe(1) + expect(editor.snaps.getLines().length).toBe(1) expect(getGapAndPointLines().pointLines[0].points).toHaveLength(4) }) @@ -1070,7 +1072,7 @@ describe('snapping while resizing', () => { expect(editor.getShape(ids.boxX)).toMatchObject({ x: 60, y: 80, props: { w: 80, h: 60 } }) - expect(editor.snaps.lines.length).toBe(1) + expect(editor.snaps.getLines().length).toBe(1) expect(getGapAndPointLines().pointLines[0].points).toHaveLength(6) // moving the mouse vertically should not change things @@ -1081,7 +1083,7 @@ describe('snapping while resizing', () => { editor.pointerMove(39, 280, { ctrlKey: true }) expect(editor.getShape(ids.boxX)).toMatchObject({ x: 40, y: 80, props: { w: 100, h: 60 } }) - expect(editor.snaps.lines.length).toBe(1) + expect(editor.snaps.getLines().length).toBe(1) expect(getGapAndPointLines().pointLines[0].points).toHaveLength(4) }) it('works for dragging the top left corner', () => { @@ -3020,7 +3022,7 @@ describe('resizing a shape with a child', () => { .pointerDown(0, 0, { target: 'selection', handle: 'top_left' }) .pointerMove(25, 25, { ctrlKey: true }) - expect(editor.snaps.lines.length).toBe(0) + expect(editor.snaps.getLines().length).toBe(0) expect(editor.getShape(ids.boxA)).toMatchObject({ x: 25, y: 25, props: { w: 25, h: 25 } }) expect(editor.getShape(ids.boxB)).toMatchObject({ x: 0.5, y: 0.5, props: { w: 5, h: 5 } }) expect(editor.getShapePageBounds(ids.boxB)).toMatchObject({ diff --git a/packages/tldraw/src/test/testutils/getSnapLines.ts b/packages/tldraw/src/test/testutils/getSnapLines.ts index 53bede17c..6d4060eda 100644 --- a/packages/tldraw/src/test/testutils/getSnapLines.ts +++ b/packages/tldraw/src/test/testutils/getSnapLines.ts @@ -8,7 +8,7 @@ const simplifyNumber = (n: number) => { } export const getSnapLines = (scene: Editor) => { const result = [] - for (const snap of scene.snaps.lines) { + for (const snap of scene.snaps.getLines()) { if (snap.type !== 'points') { throw new Error('Expected only points snap') } diff --git a/packages/tldraw/src/test/translating-snapping.test.ts b/packages/tldraw/src/test/translating-snapping.test.ts index 085e45cdc..1b6c33fad 100644 --- a/packages/tldraw/src/test/translating-snapping.test.ts +++ b/packages/tldraw/src/test/translating-snapping.test.ts @@ -117,8 +117,8 @@ describe.skip('custom snapping points', () => { // └───────┘ x───────x editor.pointerDown(250, 250, ids.box1).pointerMove(250, 51, { ctrlKey: true }) expect(editor.getShape(ids.box1)).toMatchObject({ x: 200, y: 0 }) - expect(editor.snaps.lines?.length).toBe(1) - expect(getNumSnapPoints(editor.snaps.lines![0])).toBe(3) + expect(editor.snaps.getLines()?.length).toBe(1) + expect(getNumSnapPoints(editor.snaps.getLines()![0])).toBe(3) // should not snap to 100 on y axis // x───────┐ @@ -132,7 +132,7 @@ describe.skip('custom snapping points', () => { // x───────x editor.pointerMove(250, 151, { ctrlKey: true }) expect(editor.getShape(ids.box1)).toMatchObject({ x: 200, y: 101 }) - expect(editor.snaps.lines?.length).toBe(0) + expect(editor.snaps.getLines()?.length).toBe(0) // should not snap to 50 on y axis // x───────┐ @@ -144,7 +144,7 @@ describe.skip('custom snapping points', () => { // x───────x editor.pointerMove(250, 101, { ctrlKey: true }) expect(editor.getShape(ids.box1)).toMatchObject({ x: 200, y: 51 }) - expect(editor.snaps.lines?.length).toBe(0) + expect(editor.snaps.getLines()?.length).toBe(0) // should snap to 0 on x axis // x x───────┐ @@ -160,8 +160,8 @@ describe.skip('custom snapping points', () => { // x x───────x editor.pointerMove(51, 250, { ctrlKey: true }) expect(editor.getShape(ids.box1)).toMatchObject({ x: 0, y: 200 }) - expect(editor.snaps.lines?.length).toBe(1) - expect(getNumSnapPoints(editor.snaps.lines![0])).toBe(3) + expect(editor.snaps.getLines()?.length).toBe(1) + expect(getNumSnapPoints(editor.snaps.getLines()![0])).toBe(3) // should not snap to 100 on x axis // x───────┐ @@ -177,7 +177,7 @@ describe.skip('custom snapping points', () => { // x───────x editor.pointerMove(151, 250, { ctrlKey: true }) expect(editor.getShape(ids.box1)).toMatchObject({ x: 101, y: 200 }) - expect(editor.snaps.lines?.length).toBe(0) + expect(editor.snaps.getLines()?.length).toBe(0) // should not snap to 50 on x axis // x───────┐ @@ -193,7 +193,7 @@ describe.skip('custom snapping points', () => { // x───────x editor.pointerMove(101, 250, { ctrlKey: true }) expect(editor.getShape(ids.box1)).toMatchObject({ x: 51, y: 200 }) - expect(editor.snaps.lines?.length).toBe(0) + expect(editor.snaps.getLines()?.length).toBe(0) }) it('allows shapes with custom points to snap to other shapes', () => { @@ -206,8 +206,8 @@ describe.skip('custom snapping points', () => { // └───────┘ x───────x editor.pointerDown(50, 50, ids.boxT).pointerMove(50, 251, { ctrlKey: true }) expect(editor.getShape(ids.boxT)).toMatchObject({ x: 0, y: 200 }) - expect(editor.snaps.lines?.length).toBe(1) - expect(getNumSnapPoints(editor.snaps.lines![0])).toBe(3) + expect(editor.snaps.getLines()?.length).toBe(1) + expect(getNumSnapPoints(editor.snaps.getLines()![0])).toBe(3) // should snap to 250 on y axis // x─────────────────x @@ -220,8 +220,8 @@ describe.skip('custom snapping points', () => { // └───────┘ editor.pointerMove(50, 301, { ctrlKey: true }) expect(editor.getShape(ids.boxT)).toMatchObject({ x: 0, y: 250 }) - expect(editor.snaps.lines?.length).toBe(1) - expect(getNumSnapPoints(editor.snaps.lines![0])).toBe(2) + expect(editor.snaps.getLines()?.length).toBe(1) + expect(getNumSnapPoints(editor.snaps.getLines()![0])).toBe(2) // should snap to 300 on y axis // x─────────────x───────x @@ -236,8 +236,8 @@ describe.skip('custom snapping points', () => { // └───────┘ editor.pointerMove(50, 351, { ctrlKey: true }) expect(editor.getShape(ids.boxT)).toMatchObject({ x: 0, y: 300 }) - expect(editor.snaps.lines?.length).toBe(1) - expect(getNumSnapPoints(editor.snaps.lines![0])).toBe(3) + expect(editor.snaps.getLines()?.length).toBe(1) + expect(getNumSnapPoints(editor.snaps.getLines()![0])).toBe(3) // should snap to 200 on x axis // x x───────┐ @@ -253,8 +253,8 @@ describe.skip('custom snapping points', () => { // x x───────x editor.pointerMove(251, 50, { ctrlKey: true }) expect(editor.getShape(ids.boxT)).toMatchObject({ x: 200, y: 0 }) - expect(editor.snaps.lines?.length).toBe(1) - expect(getNumSnapPoints(editor.snaps.lines![0])).toBe(3) + expect(editor.snaps.getLines()?.length).toBe(1) + expect(getNumSnapPoints(editor.snaps.getLines()![0])).toBe(3) // should snap to 250 on x axis // x x───────┐ @@ -270,8 +270,8 @@ describe.skip('custom snapping points', () => { // x───────x editor.pointerMove(301, 50, { ctrlKey: true }) expect(editor.getShape(ids.boxT)).toMatchObject({ x: 250, y: 0 }) - expect(editor.snaps.lines?.length).toBe(1) - expect(getNumSnapPoints(editor.snaps.lines![0])).toBe(2) + expect(editor.snaps.getLines()?.length).toBe(1) + expect(getNumSnapPoints(editor.snaps.getLines()![0])).toBe(2) // should snap to 300 on x axis // x x───────┐ @@ -287,8 +287,8 @@ describe.skip('custom snapping points', () => { // x x───────x editor.pointerMove(351, 50, { ctrlKey: true }) expect(editor.getShape(ids.boxT)).toMatchObject({ x: 300, y: 0 }) - expect(editor.snaps.lines?.length).toBe(1) - expect(getNumSnapPoints(editor.snaps.lines![0])).toBe(3) + expect(editor.snaps.getLines()?.length).toBe(1) + expect(getNumSnapPoints(editor.snaps.getLines()![0])).toBe(3) }) it('becomes part of the selection bounding box if there is more than one shape in the selection', () => { @@ -315,9 +315,9 @@ describe.skip('custom snapping points', () => { // x───────x editor.select(ids.boxT, ids.box1) editor.pointerDown(50, 50, ids.boxT).pointerMove(351, 50, { ctrlKey: true }) - expect(editor.snaps.lines?.length).toBe(1) - expect(getNumSnapPoints(editor.snaps.lines![0])).toBe(2) - expect(getSnapPoints(editor.snaps.lines![0])?.map(({ x }) => x)).toEqual([450, 450]) + expect(editor.snaps.getLines()?.length).toBe(1) + expect(getNumSnapPoints(editor.snaps.getLines()![0])).toBe(2) + expect(getSnapPoints(editor.snaps.getLines()![0])?.map(({ x }) => x)).toEqual([450, 450]) }) }) @@ -379,8 +379,8 @@ describe.skip('custom snapping points', () => { // └───────┘ x───────x editor.pointerDown(250, 250, ids.box1).pointerMove(250, 51, { ctrlKey: true }) expect(editor.getShape(ids.box1)).toMatchObject({ x: 200, y: 0 }) - expect(editor.snaps.lines?.length).toBe(1) - expect(getNumSnapPoints(editor.snaps.lines![0])).toBe(3) + expect(editor.snaps.getLines()?.length).toBe(1) + expect(getNumSnapPoints(editor.snaps.getLines()![0])).toBe(3) // should not snap to 100 on y axis // x───────┐ @@ -394,7 +394,7 @@ describe.skip('custom snapping points', () => { // x───────x editor.pointerMove(250, 151, { ctrlKey: true }) expect(editor.getShape(ids.box1)).toMatchObject({ x: 200, y: 101 }) - expect(editor.snaps.lines?.length).toBe(0) + expect(editor.snaps.getLines()?.length).toBe(0) // should not snap to 50 on y axis // x───────┐ @@ -406,7 +406,7 @@ describe.skip('custom snapping points', () => { // x───────x editor.pointerMove(250, 101, { ctrlKey: true }) expect(editor.getShape(ids.box1)).toMatchObject({ x: 200, y: 51 }) - expect(editor.snaps.lines?.length).toBe(0) + expect(editor.snaps.getLines()?.length).toBe(0) // should snap to 0 on x axis // x x───────┐ @@ -422,8 +422,8 @@ describe.skip('custom snapping points', () => { // x x───────x editor.pointerMove(51, 250, { ctrlKey: true }) expect(editor.getShape(ids.box1)).toMatchObject({ x: 0, y: 200 }) - expect(editor.snaps.lines?.length).toBe(1) - expect(getNumSnapPoints(editor.snaps.lines![0])).toBe(3) + expect(editor.snaps.getLines()?.length).toBe(1) + expect(getNumSnapPoints(editor.snaps.getLines()![0])).toBe(3) // should not snap to 100 on x axis // x───────┐ @@ -439,7 +439,7 @@ describe.skip('custom snapping points', () => { // x───────x editor.pointerMove(151, 250, { ctrlKey: true }) expect(editor.getShape(ids.box1)).toMatchObject({ x: 101, y: 200 }) - expect(editor.snaps.lines?.length).toBe(0) + expect(editor.snaps.getLines()?.length).toBe(0) // should not snap to 50 on x axis // x───────┐ @@ -455,7 +455,7 @@ describe.skip('custom snapping points', () => { // x───────x editor.pointerMove(101, 250, { ctrlKey: true }) expect(editor.getShape(ids.box1)).toMatchObject({ x: 51, y: 200 }) - expect(editor.snaps.lines?.length).toBe(0) + expect(editor.snaps.getLines()?.length).toBe(0) }) it('allows shapes with custom points to snap to other shapes', () => { @@ -468,8 +468,8 @@ describe.skip('custom snapping points', () => { // └───────┘ x───────x editor.pointerDown(50, 50, ids.boxT).pointerMove(50, 251, { ctrlKey: true }) expect(editor.getShape(ids.boxT)).toMatchObject({ x: 0, y: 200 }) - expect(editor.snaps.lines?.length).toBe(1) - expect(getNumSnapPoints(editor.snaps.lines![0])).toBe(3) + expect(editor.snaps.getLines()?.length).toBe(1) + expect(getNumSnapPoints(editor.snaps.getLines()![0])).toBe(3) // should snap to 250 on y axis // x─────────────────x @@ -482,8 +482,8 @@ describe.skip('custom snapping points', () => { // └───────┘ editor.pointerMove(50, 301, { ctrlKey: true }) expect(editor.getShape(ids.boxT)).toMatchObject({ x: 0, y: 250 }) - expect(editor.snaps.lines?.length).toBe(1) - expect(getNumSnapPoints(editor.snaps.lines![0])).toBe(2) + expect(editor.snaps.getLines()?.length).toBe(1) + expect(getNumSnapPoints(editor.snaps.getLines()![0])).toBe(2) // should snap to 300 on y axis // x─────────────x───────x @@ -498,8 +498,8 @@ describe.skip('custom snapping points', () => { // └───────┘ editor.pointerMove(50, 351, { ctrlKey: true }) expect(editor.getShape(ids.boxT)).toMatchObject({ x: 0, y: 300 }) - expect(editor.snaps.lines?.length).toBe(1) - expect(getNumSnapPoints(editor.snaps.lines![0])).toBe(3) + expect(editor.snaps.getLines()?.length).toBe(1) + expect(getNumSnapPoints(editor.snaps.getLines()![0])).toBe(3) // should snap to 200 on x axis // x x───────┐ @@ -515,8 +515,8 @@ describe.skip('custom snapping points', () => { // x x───────x editor.pointerMove(251, 50, { ctrlKey: true }) expect(editor.getShape(ids.boxT)).toMatchObject({ x: 200, y: 0 }) - expect(editor.snaps.lines?.length).toBe(1) - expect(getNumSnapPoints(editor.snaps.lines![0])).toBe(3) + expect(editor.snaps.getLines()?.length).toBe(1) + expect(getNumSnapPoints(editor.snaps.getLines()![0])).toBe(3) // should snap to 250 on x axis // x x───────┐ @@ -532,8 +532,8 @@ describe.skip('custom snapping points', () => { // x───────x editor.pointerMove(301, 50, { ctrlKey: true }) expect(editor.getShape(ids.boxT)).toMatchObject({ x: 250, y: 0 }) - expect(editor.snaps.lines?.length).toBe(1) - expect(getNumSnapPoints(editor.snaps.lines![0])).toBe(2) + expect(editor.snaps.getLines()?.length).toBe(1) + expect(getNumSnapPoints(editor.snaps.getLines()![0])).toBe(2) // should snap to 300 on x axis // x x───────┐ @@ -549,8 +549,8 @@ describe.skip('custom snapping points', () => { // x x───────x editor.pointerMove(351, 50, { ctrlKey: true }) expect(editor.getShape(ids.boxT)).toMatchObject({ x: 300, y: 0 }) - expect(editor.snaps.lines?.length).toBe(1) - expect(getNumSnapPoints(editor.snaps.lines![0])).toBe(3) + expect(editor.snaps.getLines()?.length).toBe(1) + expect(getNumSnapPoints(editor.snaps.getLines()![0])).toBe(3) }) it('becomes part of the selection bounding box if there is more than one shape in the selection', () => { @@ -577,8 +577,8 @@ describe.skip('custom snapping points', () => { // x───────x editor.select(ids.boxT, ids.box1) editor.pointerDown(50, 50, ids.boxT).pointerMove(351, 50, { ctrlKey: true }) - expect(editor.snaps.lines?.length).toBe(1) - expect(getNumSnapPoints(editor.snaps.lines![0])).toBe(2) - expect(getSnapPoints(editor.snaps.lines![0])?.map(({ x }) => x)).toEqual([450, 450]) + expect(editor.snaps.getLines()?.length).toBe(1) + expect(getNumSnapPoints(editor.snaps.getLines()![0])).toBe(2) + expect(getSnapPoints(editor.snaps.getLines()![0])?.map(({ x }) => x)).toEqual([450, 450]) }) }) diff --git a/packages/tldraw/src/test/translating.test.ts b/packages/tldraw/src/test/translating.test.ts index 77d70a91a..f122f76a2 100644 --- a/packages/tldraw/src/test/translating.test.ts +++ b/packages/tldraw/src/test/translating.test.ts @@ -510,9 +510,9 @@ describe('snapping with single shapes', () => { // ┼ └──────┘ editor.pointerDown(25, 5, ids.box2).pointerMove(16, 35, { ctrlKey: true }) - expect(editor.snaps.lines?.length).toBe(1) + expect(editor.snaps.getLines()?.length).toBe(1) - expect(getNumSnapPoints(editor.snaps.lines![0])).toBe(4) + expect(getNumSnapPoints(editor.snaps.getLines()![0])).toBe(4) }) it('shows all the horizonal lines + points where the bounding boxes align', () => { @@ -523,7 +523,7 @@ describe('snapping with single shapes', () => { // x─────x────────────────────x─────x editor.pointerDown(25, 5, ids.box2).pointerMove(36, 5, { ctrlKey: true }) - const snaps = editor.snaps.lines!.sort((a, b) => getNumSnapPoints(a) - getNumSnapPoints(b)) + const snaps = editor.snaps.getLines()!.sort((a, b) => getNumSnapPoints(a) - getNumSnapPoints(b)) expect(snaps.length).toBe(3) // center snap line @@ -544,7 +544,7 @@ describe('snapping with single shapes', () => { // x └─────┘ x editor.pointerDown(25, 5, ids.box2).pointerMove(5, 45, { ctrlKey: true }) - const snaps = editor.snaps.lines!.sort((a, b) => getNumSnapPoints(a) - getNumSnapPoints(b)) + const snaps = editor.snaps.getLines()!.sort((a, b) => getNumSnapPoints(a) - getNumSnapPoints(b)) expect(snaps.length).toBe(3) // center snap line @@ -560,23 +560,23 @@ describe('snapping with single shapes', () => { editor.updateShapes([{ id: ids.box1, type: 'geo', x: -20 }]) editor.pointerDown(25, 5, ids.box2).pointerMove(36, 5, { ctrlKey: true }) - expect(editor.snaps.lines!.length).toBe(0) + expect(editor.snaps.getLines()!.length).toBe(0) editor.updateShapes([{ id: ids.box1, type: 'geo', x: editor.getViewportScreenBounds().w + 10 }]) editor.pointerMove(33, 5, { ctrlKey: true }) - expect(editor.snaps.lines!.length).toBe(0) + expect(editor.snaps.getLines()!.length).toBe(0) editor.updateShapes([{ id: ids.box1, type: 'geo', y: -20 }]) editor.pointerMove(5, 5, { ctrlKey: true }) - expect(editor.snaps.lines!.length).toBe(0) + expect(editor.snaps.getLines()!.length).toBe(0) editor.updateShapes([ { id: ids.box1, type: 'geo', x: 0, y: editor.getViewportScreenBounds().h + 10 }, ]) editor.pointerMove(5, 5, { ctrlKey: true }) - expect(editor.snaps.lines!.length).toBe(0) + expect(editor.snaps.getLines()!.length).toBe(0) }) it('does not snap on the Y axis if the shift key is pressed', () => { @@ -740,9 +740,10 @@ describe('Snap-between behavior', () => { // the midpoint is 125 and c is 10 wide so it should snap to 120 if we put it at 121 editor.pointerDown(55, 5, ids.line1).pointerMove(126, 67, { ctrlKey: true }) expect(editor.getShape(ids.line1)).toMatchObject({ x: 120, y: 62 }) - expect(editor.snaps.lines?.length).toBe(1) - assertGaps(editor.snaps.lines![0]) - expect(editor.snaps.lines![0].gaps.length).toBe(2) + expect(editor.snaps.getLines()?.length).toBe(1) + const line = editor.snaps.getLines()![0] + assertGaps(line) + expect(line.gaps.length).toBe(2) }) it('shows horizontal point snaps at the same time as horizontal gap snaps', () => { // ┌─────┐ ┌─────┐ @@ -763,7 +764,7 @@ describe('Snap-between behavior', () => { editor.pointerDown(55, 5, ids.line1).pointerMove(126, 94, { ctrlKey: true }) expect(editor.getShape(ids.line1)).toMatchObject({ x: 120, y: 90 }) - const { gapLines, pointLines } = getGapAndPointLines(editor.snaps.lines!) + const { gapLines, pointLines } = getGapAndPointLines(editor.snaps.getLines()!) expect(gapLines).toHaveLength(1) expect(pointLines).toHaveLength(1) expect(gapLines[0].gaps.length).toBe(2) @@ -793,7 +794,7 @@ describe('Snap-between behavior', () => { // the midpoint is 125 and c is 10 wide so it should snap to 120 if we put it at 121 editor.pointerDown(55, 5, ids.line1).pointerMove(126, 67, { ctrlKey: true }) expect(editor.getShape(ids.line1)).toMatchObject({ x: 120, y: 62 }) - const { gapLines, pointLines } = getGapAndPointLines(editor.snaps.lines!) + const { gapLines, pointLines } = getGapAndPointLines(editor.snaps.getLines()!) expect(gapLines).toHaveLength(1) expect(pointLines).toHaveLength(1) @@ -828,9 +829,9 @@ describe('Snap-between behavior', () => { // the midpoint is 125 and c is 10 wide so it should snap to 120 if we put it at 121 editor.pointerDown(55, 155, ids.line1).pointerMove(27, 126, { ctrlKey: true }) expect(editor.getShape(ids.line1)).toMatchObject({ x: 22, y: 120 }) - expect(editor.snaps.lines?.length).toBe(1) - assertGaps(editor.snaps.lines![0]) - const { gapLines } = getGapAndPointLines(editor.snaps.lines!) + expect(editor.snaps.getLines()?.length).toBe(1) + assertGaps(editor.snaps.getLines()![0]) + const { gapLines } = getGapAndPointLines(editor.snaps.getLines()!) expect(gapLines[0].gaps.length).toBe(2) }) it('shows vertical snap points at the same time as vertical gaps', () => { @@ -862,7 +863,7 @@ describe('Snap-between behavior', () => { editor.pointerDown(55, 155, ids.line1).pointerMove(6, 126, { ctrlKey: true }) expect(editor.getShape(ids.line1)).toMatchObject({ x: 0, y: 120 }) - const { gapLines, pointLines } = getGapAndPointLines(editor.snaps.lines!) + const { gapLines, pointLines } = getGapAndPointLines(editor.snaps.getLines()!) expect(gapLines).toHaveLength(1) expect(pointLines).toHaveLength(1) expect(gapLines[0].gaps.length).toBe(2) @@ -898,7 +899,7 @@ describe('Snap-between behavior', () => { editor.pointerDown(55, 155, ids.line1).pointerMove(27, 126, { ctrlKey: true }) expect(editor.getShape(ids.line1)).toMatchObject({ x: 22, y: 120 }) - const { gapLines, pointLines } = getGapAndPointLines(editor.snaps.lines!) + const { gapLines, pointLines } = getGapAndPointLines(editor.snaps.getLines()!) expect(gapLines).toHaveLength(1) expect(pointLines).toHaveLength(1) expect(gapLines[0].gaps).toHaveLength(2) @@ -933,10 +934,10 @@ describe('Snap-between behavior', () => { ]) editor.pointerDown(5, 5, ids.boxE).pointerMove(101, 126, { ctrlKey: true }) expect(editor.getShape(ids.boxE)).toMatchObject({ x: 95, y: 120 }) - expect(editor.snaps.lines?.length).toBe(2) - assertGaps(editor.snaps.lines![0]) - assertGaps(editor.snaps.lines![1]) - const { gapLines } = getGapAndPointLines(editor.snaps.lines!) + expect(editor.snaps.getLines()?.length).toBe(2) + assertGaps(editor.snaps.getLines()![0]) + assertGaps(editor.snaps.getLines()![1]) + const { gapLines } = getGapAndPointLines(editor.snaps.getLines()!) expect(gapLines[0].gaps.length).toBe(2) expect(gapLines[1].gaps.length).toBe(2) }) @@ -978,7 +979,7 @@ describe('Snap-between behavior', () => { editor.pointerDown(5, 5, ids.boxX).pointerMove(46, 46, { ctrlKey: true }) expect(editor.getShape(ids.boxX)).toMatchObject({ x: 40, y: 40 }) - const { gapLines, pointLines } = getGapAndPointLines(editor.snaps.lines!) + const { gapLines, pointLines } = getGapAndPointLines(editor.snaps.getLines()!) expect(gapLines).toHaveLength(2) expect(gapLines[0].gaps).toHaveLength(4) expect(gapLines[1].gaps).toHaveLength(4) @@ -1011,7 +1012,7 @@ describe('Snap-between behavior', () => { editor.pointerDown(65, 25, ids.boxX).pointerMove(16, 25, { ctrlKey: true }) expect(editor.getShape(ids.boxX)).toMatchObject({ x: 0, y: 20 }) - const { gapLines, pointLines } = getGapAndPointLines(editor.snaps.lines!) + const { gapLines, pointLines } = getGapAndPointLines(editor.snaps.getLines()!) expect(gapLines).toHaveLength(2) expect(gapLines[0].gaps).toHaveLength(2) expect(gapLines[1].gaps).toHaveLength(2) @@ -1048,7 +1049,7 @@ describe('Snap-between behavior', () => { editor.pointerDown(50, 55, ids.boxX).pointerMove(51, 66, { ctrlKey: true }) expect(editor.getShape(ids.boxX)).toMatchObject({ x: 1, y: 61 }) - expect(editor.snaps.lines?.length).toBe(0) + expect(editor.snaps.getLines()?.length).toBe(0) }) it('should work if the thing being dragged is a selection', () => { @@ -1076,7 +1077,7 @@ describe('Snap-between behavior', () => { expect(editor.getShape(ids.line1)).toMatchObject({ x: 200, y: 21 }) - const { gapLines, pointLines } = getGapAndPointLines(editor.snaps.lines!) + const { gapLines, pointLines } = getGapAndPointLines(editor.snaps.getLines()!) expect(gapLines).toHaveLength(1) expect(pointLines).toHaveLength(0) @@ -1115,7 +1116,7 @@ describe('Snap-next-to behavior', () => { editor.pointerDown(5, 5, ids.boxX).pointerMove(6, 16, { ctrlKey: true }) expect(editor.getShape(ids.boxX)).toMatchObject({ x: 0, y: 10 }) - const { gapLines, pointLines } = getGapAndPointLines(editor.snaps.lines!) + const { gapLines, pointLines } = getGapAndPointLines(editor.snaps.getLines()!) expect(gapLines).toHaveLength(1) expect(gapLines[0].gaps).toHaveLength(2) @@ -1151,7 +1152,7 @@ describe('Snap-next-to behavior', () => { editor.pointerDown(5, 5, ids.boxX).pointerMove(6, 16, { ctrlKey: true }) expect(editor.getShape(ids.boxX)).toMatchObject({ x: 0, y: 10 }) - const { gapLines, pointLines } = getGapAndPointLines(editor.snaps.lines!) + const { gapLines, pointLines } = getGapAndPointLines(editor.snaps.getLines()!) expect(gapLines).toHaveLength(1) expect(gapLines[0].gaps).toHaveLength(4) @@ -1178,7 +1179,7 @@ describe('Snap-next-to behavior', () => { editor.pointerDown(105, 5, ids.boxX).pointerMove(106, 16, { ctrlKey: true }) expect(editor.getShape(ids.boxX)).toMatchObject({ x: 100, y: 10 }) - const { gapLines, pointLines } = getGapAndPointLines(editor.snaps.lines!) + const { gapLines, pointLines } = getGapAndPointLines(editor.snaps.getLines()!) expect(gapLines).toHaveLength(1) expect(gapLines[0].gaps).toHaveLength(2) @@ -1212,7 +1213,7 @@ describe('Snap-next-to behavior', () => { editor.pointerDown(205, 5, ids.boxX).pointerMove(206, 16, { ctrlKey: true }) expect(editor.getShape(ids.boxX)).toMatchObject({ x: 200, y: 10 }) - const { gapLines, pointLines } = getGapAndPointLines(editor.snaps.lines!) + const { gapLines, pointLines } = getGapAndPointLines(editor.snaps.getLines()!) expect(gapLines).toHaveLength(1) expect(gapLines[0].gaps).toHaveLength(4) @@ -1238,7 +1239,7 @@ describe('Snap-next-to behavior', () => { expect(editor.getShape(ids.boxX)).toMatchObject({ x: 10, y: 0 }) - const { gapLines, pointLines } = getGapAndPointLines(editor.snaps.lines!) + const { gapLines, pointLines } = getGapAndPointLines(editor.snaps.getLines()!) expect(gapLines).toHaveLength(1) expect(gapLines[0].gaps).toHaveLength(2) @@ -1280,7 +1281,7 @@ describe('Snap-next-to behavior', () => { expect(editor.getShape(ids.boxX)).toMatchObject({ x: 10, y: 0 }) - const { gapLines, pointLines } = getGapAndPointLines(editor.snaps.lines!) + const { gapLines, pointLines } = getGapAndPointLines(editor.snaps.getLines()!) expect(gapLines).toHaveLength(1) expect(gapLines[0].gaps).toHaveLength(4) @@ -1307,7 +1308,7 @@ describe('Snap-next-to behavior', () => { expect(editor.getShape(ids.boxX)).toMatchObject({ x: 10, y: 40 }) - const { gapLines, pointLines } = getGapAndPointLines(editor.snaps.lines!) + const { gapLines, pointLines } = getGapAndPointLines(editor.snaps.getLines()!) expect(gapLines).toHaveLength(1) expect(gapLines[0].gaps).toHaveLength(2) @@ -1348,7 +1349,7 @@ describe('Snap-next-to behavior', () => { expect(editor.getShape(ids.boxX)).toMatchObject({ x: 10, y: 80 }) - const { gapLines, pointLines } = getGapAndPointLines(editor.snaps.lines!) + const { gapLines, pointLines } = getGapAndPointLines(editor.snaps.getLines()!) expect(gapLines).toHaveLength(1) expect(gapLines[0].gaps).toHaveLength(4) @@ -1382,7 +1383,7 @@ describe('Snap-next-to behavior', () => { expect(editor.getShape(ids.boxD)).toMatchObject({ x: 200, y: 131 }) - const { gapLines, pointLines } = getGapAndPointLines(editor.snaps.lines!) + const { gapLines, pointLines } = getGapAndPointLines(editor.snaps.getLines()!) expect(gapLines).toHaveLength(1) expect(pointLines).toHaveLength(0) @@ -1533,7 +1534,7 @@ describe('translating a shape with a child', () => { editor.pointerDown(25, 25, ids.box1).pointerMove(50, 25, { ctrlKey: true }) - expect(editor.snaps.lines?.length).toBe(0) + expect(editor.snaps.getLines()?.length).toBe(0) expect(editor.getShape(ids.box1)).toMatchObject({ x: 25, y: 0, @@ -1575,7 +1576,7 @@ describe('translating a shape with a bound shape', () => { editor.pointerDown(50, 50, ids.box1).pointerMove(84, 110, { ctrlKey: true }) - expect(editor.snaps.lines.length).toBe(0) + expect(editor.snaps.getLines().length).toBe(0) }) it('should preserve arrow bindings', () => {