@tldraw/editor 3.16.0-internal.a478398270c6 → 3.16.0-next.15f085081fd5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-cjs/index.d.ts +243 -16
- package/dist-cjs/index.js +8 -1
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/TldrawEditor.js +8 -2
- package/dist-cjs/lib/TldrawEditor.js.map +2 -2
- package/dist-cjs/lib/components/MenuClickCapture.js +0 -5
- package/dist-cjs/lib/components/MenuClickCapture.js.map +2 -2
- package/dist-cjs/lib/components/SVGContainer.js +1 -1
- package/dist-cjs/lib/components/SVGContainer.js.map +2 -2
- package/dist-cjs/lib/components/Shape.js +11 -36
- package/dist-cjs/lib/components/Shape.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultBrush.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultBrush.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js +5 -24
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultCollaboratorHint.js +2 -2
- package/dist-cjs/lib/components/default-components/DefaultCollaboratorHint.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultCursor.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultCursor.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultGrid.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultGrid.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultHandles.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultHandles.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultScribble.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultScribble.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js +9 -1
- package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultShapeWrapper.js +53 -0
- package/dist-cjs/lib/components/default-components/DefaultShapeWrapper.js.map +7 -0
- package/dist-cjs/lib/components/default-components/DefaultSnapIndictor.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultSnapIndictor.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultSpinner.js +27 -15
- package/dist-cjs/lib/components/default-components/DefaultSpinner.js.map +3 -3
- package/dist-cjs/lib/config/TLUserPreferences.js +15 -3
- package/dist-cjs/lib/config/TLUserPreferences.js.map +2 -2
- package/dist-cjs/lib/editor/Editor.js +151 -67
- package/dist-cjs/lib/editor/Editor.js.map +2 -2
- package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js +14 -4
- package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js.map +2 -2
- package/dist-cjs/lib/editor/shapes/ShapeUtil.js +13 -0
- package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
- package/dist-cjs/lib/editor/tools/StateNode.js +20 -1
- package/dist-cjs/lib/editor/tools/StateNode.js.map +2 -2
- package/dist-cjs/lib/editor/types/misc-types.js.map +1 -1
- package/dist-cjs/lib/exports/getSvgJsx.js +35 -16
- package/dist-cjs/lib/exports/getSvgJsx.js.map +2 -2
- package/dist-cjs/lib/hooks/useCanvasEvents.js +31 -25
- package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/useEditor.js +1 -4
- package/dist-cjs/lib/hooks/useEditor.js.map +2 -2
- package/dist-cjs/lib/hooks/useEditorComponents.js +2 -0
- package/dist-cjs/lib/hooks/useEditorComponents.js.map +2 -2
- package/dist-cjs/lib/hooks/usePassThroughWheelEvents.js +4 -1
- package/dist-cjs/lib/hooks/usePassThroughWheelEvents.js.map +2 -2
- package/dist-cjs/lib/{utils/nearestMultiple.js → hooks/useStateAttribute.js} +15 -14
- package/dist-cjs/lib/hooks/useStateAttribute.js.map +7 -0
- package/dist-cjs/lib/license/Watermark.js +8 -8
- package/dist-cjs/lib/license/Watermark.js.map +2 -2
- package/dist-cjs/lib/options.js +7 -0
- package/dist-cjs/lib/options.js.map +2 -2
- package/dist-cjs/lib/primitives/Box.js +3 -0
- package/dist-cjs/lib/primitives/Box.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Arc2d.js +1 -1
- package/dist-cjs/lib/primitives/geometry/Arc2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Circle2d.js +1 -1
- package/dist-cjs/lib/primitives/geometry/Circle2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/CubicBezier2d.js +3 -1
- package/dist-cjs/lib/primitives/geometry/CubicBezier2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Ellipse2d.js +1 -1
- package/dist-cjs/lib/primitives/geometry/Ellipse2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/geometry-constants.js +2 -2
- package/dist-cjs/lib/primitives/geometry/geometry-constants.js.map +2 -2
- package/dist-cjs/lib/primitives/intersect.js +4 -4
- package/dist-cjs/lib/primitives/intersect.js.map +2 -2
- package/dist-cjs/lib/primitives/utils.js +4 -0
- package/dist-cjs/lib/primitives/utils.js.map +2 -2
- package/dist-cjs/lib/utils/EditorAtom.js +45 -0
- package/dist-cjs/lib/utils/EditorAtom.js.map +7 -0
- package/dist-cjs/lib/utils/sync/TLLocalSyncClient.js +0 -1
- package/dist-cjs/lib/utils/sync/TLLocalSyncClient.js.map +2 -2
- package/dist-cjs/version.js +3 -3
- package/dist-cjs/version.js.map +1 -1
- package/dist-esm/index.d.mts +243 -16
- package/dist-esm/index.mjs +16 -2
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/TldrawEditor.mjs +8 -2
- package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
- package/dist-esm/lib/components/MenuClickCapture.mjs +0 -5
- package/dist-esm/lib/components/MenuClickCapture.mjs.map +2 -2
- package/dist-esm/lib/components/SVGContainer.mjs +1 -1
- package/dist-esm/lib/components/SVGContainer.mjs.map +2 -2
- package/dist-esm/lib/components/Shape.mjs +11 -36
- package/dist-esm/lib/components/Shape.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultBrush.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultBrush.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +5 -24
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultCollaboratorHint.mjs +2 -2
- package/dist-esm/lib/components/default-components/DefaultCollaboratorHint.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultCursor.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultCursor.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultGrid.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultGrid.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultHandles.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultHandles.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultScribble.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultScribble.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs +9 -1
- package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultShapeWrapper.mjs +23 -0
- package/dist-esm/lib/components/default-components/DefaultShapeWrapper.mjs.map +7 -0
- package/dist-esm/lib/components/default-components/DefaultSnapIndictor.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultSnapIndictor.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultSpinner.mjs +17 -15
- package/dist-esm/lib/components/default-components/DefaultSpinner.mjs.map +2 -2
- package/dist-esm/lib/config/TLUserPreferences.mjs +15 -3
- package/dist-esm/lib/config/TLUserPreferences.mjs.map +2 -2
- package/dist-esm/lib/editor/Editor.mjs +151 -67
- package/dist-esm/lib/editor/Editor.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs +14 -4
- package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs.map +2 -2
- package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +13 -0
- package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/editor/tools/StateNode.mjs +20 -1
- package/dist-esm/lib/editor/tools/StateNode.mjs.map +2 -2
- package/dist-esm/lib/exports/getSvgJsx.mjs +36 -16
- package/dist-esm/lib/exports/getSvgJsx.mjs.map +2 -2
- package/dist-esm/lib/hooks/useCanvasEvents.mjs +32 -26
- package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useEditor.mjs +1 -4
- package/dist-esm/lib/hooks/useEditor.mjs.map +2 -2
- package/dist-esm/lib/hooks/useEditorComponents.mjs +4 -0
- package/dist-esm/lib/hooks/useEditorComponents.mjs.map +2 -2
- package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs +4 -1
- package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useStateAttribute.mjs +15 -0
- package/dist-esm/lib/hooks/useStateAttribute.mjs.map +7 -0
- package/dist-esm/lib/license/Watermark.mjs +8 -8
- package/dist-esm/lib/license/Watermark.mjs.map +2 -2
- package/dist-esm/lib/options.mjs +7 -0
- package/dist-esm/lib/options.mjs.map +2 -2
- package/dist-esm/lib/primitives/Box.mjs +4 -1
- package/dist-esm/lib/primitives/Box.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Arc2d.mjs +2 -2
- package/dist-esm/lib/primitives/geometry/Arc2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Circle2d.mjs +2 -2
- package/dist-esm/lib/primitives/geometry/Circle2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/CubicBezier2d.mjs +3 -1
- package/dist-esm/lib/primitives/geometry/CubicBezier2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Ellipse2d.mjs +2 -2
- package/dist-esm/lib/primitives/geometry/Ellipse2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/geometry-constants.mjs +2 -2
- package/dist-esm/lib/primitives/geometry/geometry-constants.mjs.map +2 -2
- package/dist-esm/lib/primitives/intersect.mjs +5 -5
- package/dist-esm/lib/primitives/intersect.mjs.map +2 -2
- package/dist-esm/lib/primitives/utils.mjs +4 -0
- package/dist-esm/lib/primitives/utils.mjs.map +2 -2
- package/dist-esm/lib/utils/EditorAtom.mjs +25 -0
- package/dist-esm/lib/utils/EditorAtom.mjs.map +7 -0
- package/dist-esm/lib/utils/sync/TLLocalSyncClient.mjs +0 -1
- package/dist-esm/lib/utils/sync/TLLocalSyncClient.mjs.map +2 -2
- package/dist-esm/version.mjs +3 -3
- package/dist-esm/version.mjs.map +1 -1
- package/editor.css +320 -313
- package/package.json +16 -38
- package/src/index.ts +15 -1
- package/src/lib/TldrawEditor.tsx +13 -6
- package/src/lib/components/MenuClickCapture.tsx +0 -8
- package/src/lib/components/SVGContainer.tsx +1 -1
- package/src/lib/components/Shape.tsx +12 -33
- package/src/lib/components/default-components/DefaultBrush.tsx +1 -1
- package/src/lib/components/default-components/DefaultCanvas.tsx +6 -23
- package/src/lib/components/default-components/DefaultCollaboratorHint.tsx +2 -2
- package/src/lib/components/default-components/DefaultCursor.tsx +1 -1
- package/src/lib/components/default-components/DefaultErrorFallback.tsx +1 -1
- package/src/lib/components/default-components/DefaultGrid.tsx +1 -1
- package/src/lib/components/default-components/DefaultHandles.tsx +5 -1
- package/src/lib/components/default-components/DefaultScribble.tsx +1 -1
- package/src/lib/components/default-components/DefaultShapeIndicator.tsx +6 -2
- package/src/lib/components/default-components/DefaultShapeWrapper.tsx +35 -0
- package/src/lib/components/default-components/DefaultSnapIndictor.tsx +1 -1
- package/src/lib/components/default-components/DefaultSpinner.tsx +12 -12
- package/src/lib/config/TLUserPreferences.ts +15 -1
- package/src/lib/editor/Editor.test.ts +416 -8
- package/src/lib/editor/Editor.ts +195 -92
- package/src/lib/editor/managers/ClickManager/ClickManager.test.ts +15 -14
- package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.test.ts +16 -15
- package/src/lib/editor/managers/FocusManager/FocusManager.test.ts +49 -48
- package/src/lib/editor/managers/FontManager/FontManager.test.ts +24 -23
- package/src/lib/editor/managers/HistoryManager/HistoryManager.test.ts +7 -6
- package/src/lib/editor/managers/ScribbleManager/ScribbleManager.test.ts +12 -11
- package/src/lib/editor/managers/SnapManager/SnapManager.test.ts +57 -50
- package/src/lib/editor/managers/TextManager/TextManager.test.ts +51 -26
- package/src/lib/editor/managers/TickManager/TickManager.test.ts +14 -13
- package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.test.ts +55 -26
- package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.ts +14 -1
- package/src/lib/editor/shapes/ShapeUtil.ts +71 -0
- package/src/lib/editor/tools/StateNode.test.ts +285 -0
- package/src/lib/editor/tools/StateNode.ts +27 -1
- package/src/lib/editor/types/misc-types.ts +73 -1
- package/src/lib/exports/getSvgJsx.test.ts +868 -0
- package/src/lib/exports/getSvgJsx.tsx +78 -21
- package/src/lib/hooks/useCanvasEvents.ts +45 -38
- package/src/lib/hooks/useEditor.tsx +6 -5
- package/src/lib/hooks/useEditorComponents.tsx +8 -2
- package/src/lib/hooks/usePassThroughWheelEvents.ts +6 -1
- package/src/lib/hooks/useStateAttribute.ts +15 -0
- package/src/lib/license/LicenseManager.test.ts +3 -1
- package/src/lib/license/Watermark.test.tsx +2 -1
- package/src/lib/license/Watermark.tsx +8 -8
- package/src/lib/options.ts +8 -0
- package/src/lib/primitives/Box.test.ts +126 -0
- package/src/lib/primitives/Box.ts +10 -1
- package/src/lib/primitives/geometry/Arc2d.ts +2 -2
- package/src/lib/primitives/geometry/Circle2d.ts +2 -2
- package/src/lib/primitives/geometry/CubicBezier2d.ts +4 -1
- package/src/lib/primitives/geometry/Ellipse2d.ts +2 -2
- package/src/lib/primitives/geometry/geometry-constants.ts +2 -1
- package/src/lib/primitives/intersect.test.ts +946 -0
- package/src/lib/primitives/intersect.ts +12 -5
- package/src/lib/primitives/utils.ts +11 -0
- package/src/lib/utils/EditorAtom.ts +37 -0
- package/src/lib/utils/sync/LocalIndexedDb.test.ts +2 -1
- package/src/lib/utils/sync/TLLocalSyncClient.test.ts +15 -15
- package/src/lib/utils/sync/TLLocalSyncClient.ts +0 -1
- package/src/version.ts +3 -3
- package/dist-cjs/lib/utils/nearestMultiple.js.map +0 -7
- package/dist-esm/lib/utils/nearestMultiple.mjs +0 -14
- package/dist-esm/lib/utils/nearestMultiple.mjs.map +0 -7
- package/src/lib/test/currentToolIdMask.test.ts +0 -49
- package/src/lib/utils/nearestMultiple.ts +0 -13
package/src/lib/editor/Editor.ts
CHANGED
|
@@ -176,8 +176,10 @@ import {
|
|
|
176
176
|
RequiredKeys,
|
|
177
177
|
TLCameraMoveOptions,
|
|
178
178
|
TLCameraOptions,
|
|
179
|
+
TLGetShapeAtPointOptions,
|
|
179
180
|
TLImageExportOptions,
|
|
180
181
|
TLSvgExportOptions,
|
|
182
|
+
TLUpdatePointerOptions,
|
|
181
183
|
} from './types/misc-types'
|
|
182
184
|
import { TLAdjacentDirection, TLResizeHandle } from './types/selection-types'
|
|
183
185
|
|
|
@@ -1803,7 +1805,9 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1803
1805
|
}
|
|
1804
1806
|
|
|
1805
1807
|
/**
|
|
1806
|
-
* Select all
|
|
1808
|
+
* Select all shapes. If the user has selected shapes that share a parent,
|
|
1809
|
+
* select all shapes within that parent. If the user has not selected any shapes,
|
|
1810
|
+
* or if the shapes shapes are only on select all shapes on the current page.
|
|
1807
1811
|
*
|
|
1808
1812
|
* @example
|
|
1809
1813
|
* ```ts
|
|
@@ -1813,11 +1817,34 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1813
1817
|
* @public
|
|
1814
1818
|
*/
|
|
1815
1819
|
selectAll(): this {
|
|
1816
|
-
|
|
1817
|
-
|
|
1820
|
+
let parentToSelectWithinId: TLParentId | null = null
|
|
1821
|
+
|
|
1822
|
+
const selectedShapeIds = this.getSelectedShapeIds()
|
|
1823
|
+
|
|
1824
|
+
// If we have selected shapes, try to find a parent to select within
|
|
1825
|
+
if (selectedShapeIds.length > 0) {
|
|
1826
|
+
for (const id of selectedShapeIds) {
|
|
1827
|
+
const shape = this.getShape(id)
|
|
1828
|
+
if (!shape) continue
|
|
1829
|
+
if (parentToSelectWithinId === null) {
|
|
1830
|
+
// If we haven't found a parent yet, set this parent as the parent to select within
|
|
1831
|
+
parentToSelectWithinId = shape.parentId
|
|
1832
|
+
} else if (parentToSelectWithinId !== shape.parentId) {
|
|
1833
|
+
// If we've found two different parents, we can't select all, do nothing
|
|
1834
|
+
return this
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
// If we haven't found a parent from our selected shapes, select the current page
|
|
1840
|
+
if (!parentToSelectWithinId) {
|
|
1841
|
+
parentToSelectWithinId = this.getCurrentPageId()
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1844
|
+
// Select all the unlocked shapes within the parent
|
|
1845
|
+
const ids = this.getSortedChildIdsForParent(parentToSelectWithinId)
|
|
1818
1846
|
if (ids.length <= 0) return this
|
|
1819
1847
|
this.setSelectedShapes(this._getUnlockedShapeIds(ids))
|
|
1820
|
-
|
|
1821
1848
|
return this
|
|
1822
1849
|
}
|
|
1823
1850
|
|
|
@@ -1838,10 +1865,11 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1838
1865
|
firstParentId &&
|
|
1839
1866
|
selectedShapeIds.every((shapeId) => this.getShape(shapeId)?.parentId === firstParentId) &&
|
|
1840
1867
|
!isPageId(firstParentId)
|
|
1868
|
+
const filteredShapes = isSelectedWithinContainer
|
|
1869
|
+
? this.getCurrentPageShapes().filter((shape) => shape.parentId === firstParentId)
|
|
1870
|
+
: this.getCurrentPageShapes().filter((shape) => isPageId(shape.parentId))
|
|
1841
1871
|
const readingOrderShapes = isSelectedWithinContainer
|
|
1842
|
-
? this._getShapesInReadingOrder(
|
|
1843
|
-
this.getCurrentPageShapes().filter((shape) => shape.parentId === firstParentId)
|
|
1844
|
-
)
|
|
1872
|
+
? this._getShapesInReadingOrder(filteredShapes)
|
|
1845
1873
|
: this.getCurrentPageShapesInReadingOrder()
|
|
1846
1874
|
const currentShapeId: TLShapeId | undefined =
|
|
1847
1875
|
selectedShapeIds.length === 1
|
|
@@ -1858,7 +1886,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1858
1886
|
adjacentShapeId = shapeIds[adjacentIndex]
|
|
1859
1887
|
} else {
|
|
1860
1888
|
if (!currentShapeId) return
|
|
1861
|
-
adjacentShapeId = this.getNearestAdjacentShape(currentShapeId, direction)
|
|
1889
|
+
adjacentShapeId = this.getNearestAdjacentShape(filteredShapes, currentShapeId, direction)
|
|
1862
1890
|
}
|
|
1863
1891
|
|
|
1864
1892
|
const shape = this.getShape(adjacentShapeId)
|
|
@@ -1957,6 +1985,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1957
1985
|
* @public
|
|
1958
1986
|
*/
|
|
1959
1987
|
getNearestAdjacentShape(
|
|
1988
|
+
shapes: TLShape[],
|
|
1960
1989
|
currentShapeId: TLShapeId,
|
|
1961
1990
|
direction: 'left' | 'right' | 'up' | 'down'
|
|
1962
1991
|
): TLShapeId {
|
|
@@ -1964,7 +1993,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1964
1993
|
const currentShape = this.getShape(currentShapeId)
|
|
1965
1994
|
if (!currentShape) return currentShapeId
|
|
1966
1995
|
|
|
1967
|
-
const shapes = this.getCurrentPageShapes()
|
|
1968
1996
|
const tabbableShapes = shapes.filter(
|
|
1969
1997
|
(shape) => this.getShapeUtil(shape).canTabTo(shape) && shape.id !== currentShapeId
|
|
1970
1998
|
)
|
|
@@ -3046,7 +3074,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3046
3074
|
// Dispatch a new pointer move because the pointer's page will have changed
|
|
3047
3075
|
// (its screen position will compute to a new page position given the new camera position)
|
|
3048
3076
|
const { currentScreenPoint, currentPagePoint } = this.inputs
|
|
3049
|
-
const { screenBounds } = this.store.unsafeGetWithoutCapture(TLINSTANCE_ID)!
|
|
3050
3077
|
|
|
3051
3078
|
// compare the next page point (derived from the current camera) to the current page point
|
|
3052
3079
|
if (
|
|
@@ -3054,27 +3081,10 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3054
3081
|
currentScreenPoint.y / z - y !== currentPagePoint.y
|
|
3055
3082
|
) {
|
|
3056
3083
|
// If it's changed, dispatch a pointer event
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
target: 'canvas',
|
|
3060
|
-
name: 'pointer_move',
|
|
3061
|
-
// weird but true: we need to put the screen point back into client space
|
|
3062
|
-
point: Vec.AddXY(currentScreenPoint, screenBounds.x, screenBounds.y),
|
|
3084
|
+
this.updatePointer({
|
|
3085
|
+
immediate: opts?.immediate,
|
|
3063
3086
|
pointerId: INTERNAL_POINTER_IDS.CAMERA_MOVE,
|
|
3064
|
-
|
|
3065
|
-
altKey: this.inputs.altKey,
|
|
3066
|
-
shiftKey: this.inputs.shiftKey,
|
|
3067
|
-
metaKey: this.inputs.metaKey,
|
|
3068
|
-
accelKey: isAccelKey(this.inputs),
|
|
3069
|
-
button: 0,
|
|
3070
|
-
isPen: this.getInstanceState().isPenMode ?? false,
|
|
3071
|
-
}
|
|
3072
|
-
|
|
3073
|
-
if (opts?.immediate) {
|
|
3074
|
-
this._flushEventForTick(event)
|
|
3075
|
-
} else {
|
|
3076
|
-
this.dispatch(event)
|
|
3077
|
-
}
|
|
3087
|
+
})
|
|
3078
3088
|
}
|
|
3079
3089
|
|
|
3080
3090
|
this._tickCameraState()
|
|
@@ -4395,21 +4405,28 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
4395
4405
|
*/
|
|
4396
4406
|
deletePage(page: TLPageId | TLPage): this {
|
|
4397
4407
|
const id = typeof page === 'string' ? page : page.id
|
|
4398
|
-
this.run(
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4408
|
+
this.run(
|
|
4409
|
+
() => {
|
|
4410
|
+
if (this.getIsReadonly()) return
|
|
4411
|
+
const pages = this.getPages()
|
|
4412
|
+
if (pages.length === 1) return
|
|
4402
4413
|
|
|
4403
|
-
|
|
4404
|
-
|
|
4414
|
+
const deletedPage = this.getPage(id)
|
|
4415
|
+
if (!deletedPage) return
|
|
4405
4416
|
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4417
|
+
if (id === this.getCurrentPageId()) {
|
|
4418
|
+
const index = pages.findIndex((page) => page.id === id)
|
|
4419
|
+
const next = pages[index - 1] ?? pages[index + 1]
|
|
4420
|
+
this.setCurrentPage(next.id)
|
|
4421
|
+
}
|
|
4422
|
+
|
|
4423
|
+
const shapes = this.getSortedChildIdsForParent(deletedPage.id)
|
|
4424
|
+
this.deleteShapes(shapes)
|
|
4425
|
+
|
|
4426
|
+
this.store.remove([deletedPage.id])
|
|
4427
|
+
},
|
|
4428
|
+
{ ignoreShapeLock: true }
|
|
4429
|
+
)
|
|
4413
4430
|
return this
|
|
4414
4431
|
}
|
|
4415
4432
|
|
|
@@ -5138,20 +5155,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5138
5155
|
*
|
|
5139
5156
|
* @returns The shape at the given point, or undefined if there is no shape at the point.
|
|
5140
5157
|
*/
|
|
5141
|
-
getShapeAtPoint(
|
|
5142
|
-
point: VecLike,
|
|
5143
|
-
opts = {} as {
|
|
5144
|
-
renderingOnly?: boolean
|
|
5145
|
-
margin?: number
|
|
5146
|
-
hitInside?: boolean
|
|
5147
|
-
hitLocked?: boolean
|
|
5148
|
-
// TODO: we probably need to rename this, we don't quite _always_
|
|
5149
|
-
// respect this esp. in the part below that does "Check labels first"
|
|
5150
|
-
hitLabels?: boolean
|
|
5151
|
-
hitFrameInside?: boolean
|
|
5152
|
-
filter?(shape: TLShape): boolean
|
|
5153
|
-
}
|
|
5154
|
-
): TLShape | undefined {
|
|
5158
|
+
getShapeAtPoint(point: VecLike, opts: TLGetShapeAtPointOptions = {}): TLShape | undefined {
|
|
5155
5159
|
const zoomLevel = this.getZoomLevel()
|
|
5156
5160
|
const viewportPageBounds = this.getViewportPageBounds()
|
|
5157
5161
|
const {
|
|
@@ -5163,6 +5167,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5163
5167
|
hitFrameInside = false,
|
|
5164
5168
|
} = opts
|
|
5165
5169
|
|
|
5170
|
+
const [innerMargin, outerMargin] = Array.isArray(margin) ? margin : [margin, margin]
|
|
5171
|
+
|
|
5166
5172
|
let inHollowSmallestArea = Infinity
|
|
5167
5173
|
let inHollowSmallestAreaHit: TLShape | null = null
|
|
5168
5174
|
|
|
@@ -5182,7 +5188,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5182
5188
|
return false
|
|
5183
5189
|
const pageMask = this.getShapeMask(shape)
|
|
5184
5190
|
if (pageMask && !pointInPolygon(point, pageMask)) return false
|
|
5185
|
-
if (filter
|
|
5191
|
+
if (filter && !filter(shape)) return false
|
|
5186
5192
|
return true
|
|
5187
5193
|
})
|
|
5188
5194
|
|
|
@@ -5196,8 +5202,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5196
5202
|
// Check labels first
|
|
5197
5203
|
if (
|
|
5198
5204
|
this.isShapeOfType<TLFrameShape>(shape, 'frame') ||
|
|
5199
|
-
(this.isShapeOfType<TLArrowShape>(shape, 'arrow') && shape.props.text.trim()) ||
|
|
5200
5205
|
((this.isShapeOfType<TLNoteShape>(shape, 'note') ||
|
|
5206
|
+
this.isShapeOfType<TLArrowShape>(shape, 'arrow') ||
|
|
5201
5207
|
(this.isShapeOfType<TLGeoShape>(shape, 'geo') && shape.props.fill === 'none')) &&
|
|
5202
5208
|
this.getShapeUtil(shape).getText(shape)?.trim())
|
|
5203
5209
|
) {
|
|
@@ -5208,13 +5214,18 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5208
5214
|
}
|
|
5209
5215
|
}
|
|
5210
5216
|
|
|
5211
|
-
if (this.isShapeOfType(shape, 'frame')) {
|
|
5217
|
+
if (this.isShapeOfType<TLFrameShape>(shape, 'frame')) {
|
|
5212
5218
|
// On the rare case that we've hit a frame (not its label), test again hitInside to be forced true;
|
|
5213
5219
|
// this prevents clicks from passing through the body of a frame to shapes behind it.
|
|
5214
5220
|
|
|
5215
5221
|
// If the hit is within the frame's outer margin, then select the frame
|
|
5216
|
-
const distance = geometry.distanceToPoint(pointInShapeSpace,
|
|
5217
|
-
if (
|
|
5222
|
+
const distance = geometry.distanceToPoint(pointInShapeSpace, hitFrameInside)
|
|
5223
|
+
if (
|
|
5224
|
+
hitFrameInside
|
|
5225
|
+
? (distance > 0 && distance <= outerMargin) ||
|
|
5226
|
+
(distance <= 0 && distance > -innerMargin)
|
|
5227
|
+
: distance > 0 && distance <= outerMargin
|
|
5228
|
+
) {
|
|
5218
5229
|
return inMarginClosestToEdgeHit || shape
|
|
5219
5230
|
}
|
|
5220
5231
|
|
|
@@ -5253,11 +5264,11 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5253
5264
|
// If the margin is zero and the geometry has a very small width or height,
|
|
5254
5265
|
// then check the actual distance. This is to prevent a bug where straight
|
|
5255
5266
|
// lines would never pass the broad phase (point-in-bounds) check.
|
|
5256
|
-
if (
|
|
5267
|
+
if (outerMargin === 0 && (geometry.bounds.w < 1 || geometry.bounds.h < 1)) {
|
|
5257
5268
|
distance = geometry.distanceToPoint(pointInShapeSpace, hitInside)
|
|
5258
5269
|
} else {
|
|
5259
5270
|
// Broad phase
|
|
5260
|
-
if (geometry.bounds.containsPoint(pointInShapeSpace,
|
|
5271
|
+
if (geometry.bounds.containsPoint(pointInShapeSpace, outerMargin)) {
|
|
5261
5272
|
// Narrow phase (actual distance)
|
|
5262
5273
|
distance = geometry.distanceToPoint(pointInShapeSpace, hitInside)
|
|
5263
5274
|
} else {
|
|
@@ -5272,7 +5283,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5272
5283
|
// the shape or negative if inside of the shape. If the distance
|
|
5273
5284
|
// is greater than the margin, then it's a miss. Otherwise...
|
|
5274
5285
|
|
|
5275
|
-
|
|
5286
|
+
// Are we close to the shape's edge?
|
|
5287
|
+
if (distance <= outerMargin || (hitInside && distance <= 0 && distance > -innerMargin)) {
|
|
5276
5288
|
if (geometry.isFilled || (isGroup && geometry.children[0].isFilled)) {
|
|
5277
5289
|
// If the shape is filled, then it's a hit. Remember, we're
|
|
5278
5290
|
// starting from the TOP-MOST shape in z-index order, so any
|
|
@@ -5282,11 +5294,21 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5282
5294
|
// If the shape is bigger than the viewport, then skip it.
|
|
5283
5295
|
if (this.getShapePageBounds(shape)!.contains(viewportPageBounds)) continue
|
|
5284
5296
|
|
|
5285
|
-
//
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5297
|
+
// If we're close to the edge of the shape, and if it's the closest edge among
|
|
5298
|
+
// all the edges that we've gotten close to so far, then we will want to hit the
|
|
5299
|
+
// shape unless we hit something else or closer in later iterations.
|
|
5300
|
+
if (
|
|
5301
|
+
hitInside
|
|
5302
|
+
? // On hitInside, the distance will be negative for hits inside
|
|
5303
|
+
// If the distance is positive, check against the outer margin
|
|
5304
|
+
(distance > 0 && distance <= outerMargin) ||
|
|
5305
|
+
// If the distance is negative, check against the inner margin
|
|
5306
|
+
(distance <= 0 && distance > -innerMargin)
|
|
5307
|
+
: // If hitInside is false, then sadly _we do not know_ whether the
|
|
5308
|
+
// point is inside or outside of the shape, so we check against
|
|
5309
|
+
// the max of the two margins
|
|
5310
|
+
Math.abs(distance) <= Math.max(innerMargin, outerMargin)
|
|
5311
|
+
) {
|
|
5290
5312
|
if (Math.abs(distance) < inMarginClosestToEdgeDistance) {
|
|
5291
5313
|
inMarginClosestToEdgeDistance = Math.abs(distance)
|
|
5292
5314
|
inMarginClosestToEdgeHit = shape
|
|
@@ -5308,6 +5330,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5308
5330
|
} else {
|
|
5309
5331
|
// For open shapes (e.g. lines or draw shapes) always use the margin.
|
|
5310
5332
|
// If the distance is less than the margin, return the shape as the hit.
|
|
5333
|
+
// Use the editor's configurable hit test margin.
|
|
5311
5334
|
if (distance < this.options.hitTestMargin / zoomLevel) {
|
|
5312
5335
|
return shape
|
|
5313
5336
|
}
|
|
@@ -6310,7 +6333,17 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
6310
6333
|
|
|
6311
6334
|
this.createShapes(shapesToCreate)
|
|
6312
6335
|
this.createBindings(bindingsToCreate)
|
|
6313
|
-
|
|
6336
|
+
|
|
6337
|
+
this.setSelectedShapes(
|
|
6338
|
+
compact(
|
|
6339
|
+
ids.map((oldId) => {
|
|
6340
|
+
const newId = shapeIds.get(oldId)
|
|
6341
|
+
if (!newId) return null
|
|
6342
|
+
if (!this.getShape(newId)) return null
|
|
6343
|
+
return newId
|
|
6344
|
+
})
|
|
6345
|
+
)
|
|
6346
|
+
)
|
|
6314
6347
|
|
|
6315
6348
|
if (offset !== undefined) {
|
|
6316
6349
|
// If we've offset the duplicated shapes, check to see whether their new bounds is entirely
|
|
@@ -7364,7 +7397,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
7364
7397
|
if (
|
|
7365
7398
|
!this.getShapeUtil(shape).canBeLaidOut?.(shape, {
|
|
7366
7399
|
type: 'stretch',
|
|
7367
|
-
shapes: shapesToStretchFirstPass,
|
|
7368
7400
|
})
|
|
7369
7401
|
) {
|
|
7370
7402
|
continue
|
|
@@ -7835,31 +7867,38 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
7835
7867
|
) {
|
|
7836
7868
|
let parentId: TLParentId = this.getFocusedGroupId()
|
|
7837
7869
|
|
|
7838
|
-
|
|
7839
|
-
|
|
7840
|
-
|
|
7841
|
-
|
|
7842
|
-
|
|
7843
|
-
|
|
7844
|
-
|
|
7845
|
-
|
|
7846
|
-
|
|
7847
|
-
|
|
7848
|
-
|
|
7849
|
-
|
|
7850
|
-
|
|
7851
|
-
|
|
7852
|
-
|
|
7853
|
-
|
|
7854
|
-
|
|
7855
|
-
|
|
7856
|
-
|
|
7870
|
+
const isPositioned = partial.x !== undefined && partial.y !== undefined
|
|
7871
|
+
|
|
7872
|
+
// If the shape has been explicitly positioned, we'll try to find a parent at
|
|
7873
|
+
// that position. If not, we'll assume the user isn't deliberately placing the
|
|
7874
|
+
// shape and the positioning will be handled later by another system.
|
|
7875
|
+
if (isPositioned) {
|
|
7876
|
+
for (let i = currentPageShapesSorted.length - 1; i >= 0; i--) {
|
|
7877
|
+
const parent = currentPageShapesSorted[i]
|
|
7878
|
+
const util = this.getShapeUtil(parent)
|
|
7879
|
+
if (
|
|
7880
|
+
util.canReceiveNewChildrenOfType(parent, partial.type) &&
|
|
7881
|
+
!this.isShapeHidden(parent) &&
|
|
7882
|
+
this.isPointInShape(
|
|
7883
|
+
parent,
|
|
7884
|
+
// If no parent is provided, then we can treat the
|
|
7885
|
+
// shape's provided x/y as being in the page's space.
|
|
7886
|
+
{ x: partial.x ?? 0, y: partial.y ?? 0 },
|
|
7887
|
+
{
|
|
7888
|
+
margin: 0,
|
|
7889
|
+
hitInside: true,
|
|
7890
|
+
}
|
|
7891
|
+
)
|
|
7892
|
+
) {
|
|
7893
|
+
parentId = parent.id
|
|
7894
|
+
break
|
|
7895
|
+
}
|
|
7857
7896
|
}
|
|
7858
7897
|
}
|
|
7859
7898
|
|
|
7860
7899
|
const prevParentId = partial.parentId
|
|
7861
7900
|
|
|
7862
|
-
// a shape cannot be
|
|
7901
|
+
// a shape cannot be its own parent. This was a rare issue with frames/groups in the syncFuzz tests.
|
|
7863
7902
|
if (parentId === partial.id) {
|
|
7864
7903
|
parentId = focusedGroupId
|
|
7865
7904
|
}
|
|
@@ -9480,6 +9519,24 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9480
9519
|
}
|
|
9481
9520
|
}
|
|
9482
9521
|
|
|
9522
|
+
/**
|
|
9523
|
+
* Get an exported image of the given shapes as a data URL.
|
|
9524
|
+
*
|
|
9525
|
+
* @param shapes - The shapes (or shape ids) to export.
|
|
9526
|
+
* @param opts - Options for the export.
|
|
9527
|
+
*
|
|
9528
|
+
* @returns A data URL of the image.
|
|
9529
|
+
* @public
|
|
9530
|
+
*/
|
|
9531
|
+
async toImageDataUrl(shapes: TLShapeId[] | TLShape[], opts: TLImageExportOptions = {}) {
|
|
9532
|
+
const { blob, width, height } = await this.toImage(shapes, opts)
|
|
9533
|
+
return {
|
|
9534
|
+
url: await FileHelpers.blobToDataUrl(blob),
|
|
9535
|
+
width,
|
|
9536
|
+
height,
|
|
9537
|
+
}
|
|
9538
|
+
}
|
|
9539
|
+
|
|
9483
9540
|
/* --------------------- Events --------------------- */
|
|
9484
9541
|
|
|
9485
9542
|
/**
|
|
@@ -9647,6 +9704,52 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9647
9704
|
return this
|
|
9648
9705
|
}
|
|
9649
9706
|
|
|
9707
|
+
/**
|
|
9708
|
+
* Dispatch a pointer move event in the current position of the pointer. This is useful when
|
|
9709
|
+
* external circumstances have changed (e.g. the camera moved or a shape was moved) and you want
|
|
9710
|
+
* the current interaction to respond to that change.
|
|
9711
|
+
*
|
|
9712
|
+
* @example
|
|
9713
|
+
* ```ts
|
|
9714
|
+
* editor.updatePointer()
|
|
9715
|
+
* ```
|
|
9716
|
+
*
|
|
9717
|
+
* @param options - The options for updating the pointer.
|
|
9718
|
+
* @returns The editor instance.
|
|
9719
|
+
* @public
|
|
9720
|
+
*/
|
|
9721
|
+
updatePointer(options?: TLUpdatePointerOptions): this {
|
|
9722
|
+
const event: TLPointerEventInfo = {
|
|
9723
|
+
type: 'pointer',
|
|
9724
|
+
target: 'canvas',
|
|
9725
|
+
name: 'pointer_move',
|
|
9726
|
+
point:
|
|
9727
|
+
options?.point ??
|
|
9728
|
+
// weird but true: what `inputs` calls screen-space is actually viewport space. so
|
|
9729
|
+
// we need to convert back into true screen space first. we should fix this...
|
|
9730
|
+
Vec.Add(
|
|
9731
|
+
this.inputs.currentScreenPoint,
|
|
9732
|
+
this.store.unsafeGetWithoutCapture(TLINSTANCE_ID)!.screenBounds
|
|
9733
|
+
),
|
|
9734
|
+
pointerId: options?.pointerId ?? 0,
|
|
9735
|
+
button: options?.button ?? 0,
|
|
9736
|
+
isPen: options?.isPen ?? this.inputs.isPen,
|
|
9737
|
+
shiftKey: options?.shiftKey ?? this.inputs.shiftKey,
|
|
9738
|
+
altKey: options?.altKey ?? this.inputs.altKey,
|
|
9739
|
+
ctrlKey: options?.ctrlKey ?? this.inputs.ctrlKey,
|
|
9740
|
+
metaKey: options?.metaKey ?? this.inputs.metaKey,
|
|
9741
|
+
accelKey: options?.accelKey ?? isAccelKey(this.inputs),
|
|
9742
|
+
}
|
|
9743
|
+
|
|
9744
|
+
if (options?.immediate) {
|
|
9745
|
+
this._flushEventForTick(event)
|
|
9746
|
+
} else {
|
|
9747
|
+
this.dispatch(event)
|
|
9748
|
+
}
|
|
9749
|
+
|
|
9750
|
+
return this
|
|
9751
|
+
}
|
|
9752
|
+
|
|
9650
9753
|
/**
|
|
9651
9754
|
* Puts the editor into focused mode.
|
|
9652
9755
|
*
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
import { Mocked, vi } from 'vitest'
|
|
1
2
|
import { Editor } from '../../Editor'
|
|
2
3
|
import { TLClickEventInfo, TLPointerEventInfo } from '../../types/event-types'
|
|
3
4
|
import { ClickManager } from './ClickManager'
|
|
4
5
|
|
|
5
6
|
// Mock the Editor class
|
|
6
|
-
|
|
7
|
+
vi.mock('../../Editor')
|
|
7
8
|
|
|
8
9
|
describe('ClickManager', () => {
|
|
9
|
-
let editor:
|
|
10
|
+
let editor: Mocked<Editor>
|
|
10
11
|
let clickManager: ClickManager
|
|
11
12
|
let mockTimers: any
|
|
12
13
|
|
|
@@ -29,14 +30,14 @@ describe('ClickManager', () => {
|
|
|
29
30
|
})
|
|
30
31
|
|
|
31
32
|
beforeEach(() => {
|
|
32
|
-
|
|
33
|
+
vi.useFakeTimers()
|
|
33
34
|
mockTimers = {
|
|
34
|
-
setTimeout:
|
|
35
|
+
setTimeout: vi.fn((fn, delay) => setTimeout(fn, delay)),
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
editor = {
|
|
38
39
|
timers: mockTimers,
|
|
39
|
-
dispatch:
|
|
40
|
+
dispatch: vi.fn(),
|
|
40
41
|
options: {
|
|
41
42
|
doubleClickDurationMs: 300,
|
|
42
43
|
multiClickDurationMs: 300,
|
|
@@ -46,7 +47,7 @@ describe('ClickManager', () => {
|
|
|
46
47
|
inputs: {
|
|
47
48
|
currentScreenPoint: { x: 0, y: 0 },
|
|
48
49
|
},
|
|
49
|
-
getInstanceState:
|
|
50
|
+
getInstanceState: vi.fn(() => ({
|
|
50
51
|
isCoarsePointer: false,
|
|
51
52
|
})),
|
|
52
53
|
} as any
|
|
@@ -55,8 +56,8 @@ describe('ClickManager', () => {
|
|
|
55
56
|
})
|
|
56
57
|
|
|
57
58
|
afterEach(() => {
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
vi.useRealTimers()
|
|
60
|
+
vi.clearAllMocks()
|
|
60
61
|
})
|
|
61
62
|
|
|
62
63
|
describe('constructor and initial state', () => {
|
|
@@ -100,7 +101,7 @@ describe('ClickManager', () => {
|
|
|
100
101
|
clickManager.handlePointerEvent(pointerEvent)
|
|
101
102
|
expect(clickManager.clickState).toBe('pendingDouble')
|
|
102
103
|
|
|
103
|
-
|
|
104
|
+
vi.advanceTimersByTime(350)
|
|
104
105
|
|
|
105
106
|
expect(clickManager.clickState).toBe('idle')
|
|
106
107
|
})
|
|
@@ -141,7 +142,7 @@ describe('ClickManager', () => {
|
|
|
141
142
|
clickManager.handlePointerEvent(firstDown)
|
|
142
143
|
clickManager.handlePointerEvent(secondDown)
|
|
143
144
|
|
|
144
|
-
|
|
145
|
+
vi.advanceTimersByTime(350)
|
|
145
146
|
|
|
146
147
|
expect(editor.dispatch).toHaveBeenCalledWith(
|
|
147
148
|
expect.objectContaining({
|
|
@@ -235,7 +236,7 @@ describe('ClickManager', () => {
|
|
|
235
236
|
clickManager.handlePointerEvent(pointerDown) // second
|
|
236
237
|
clickManager.handlePointerEvent(pointerDown) // third
|
|
237
238
|
|
|
238
|
-
|
|
239
|
+
vi.advanceTimersByTime(350)
|
|
239
240
|
|
|
240
241
|
expect(editor.dispatch).toHaveBeenCalledWith(
|
|
241
242
|
expect.objectContaining({
|
|
@@ -255,7 +256,7 @@ describe('ClickManager', () => {
|
|
|
255
256
|
clickManager.handlePointerEvent(pointerDown) // third
|
|
256
257
|
clickManager.handlePointerEvent(pointerDown) // fourth
|
|
257
258
|
|
|
258
|
-
|
|
259
|
+
vi.advanceTimersByTime(350)
|
|
259
260
|
|
|
260
261
|
expect(editor.dispatch).toHaveBeenCalledWith(
|
|
261
262
|
expect.objectContaining({
|
|
@@ -277,7 +278,7 @@ describe('ClickManager', () => {
|
|
|
277
278
|
editor.options.doubleClickDurationMs
|
|
278
279
|
)
|
|
279
280
|
|
|
280
|
-
|
|
281
|
+
vi.clearAllMocks()
|
|
281
282
|
|
|
282
283
|
// Second click - should use multiClickDurationMs
|
|
283
284
|
clickManager.handlePointerEvent(pointerDown)
|
|
@@ -392,7 +393,7 @@ describe('ClickManager', () => {
|
|
|
392
393
|
clickManager.cancelDoubleClickTimeout()
|
|
393
394
|
|
|
394
395
|
// Advance time - should not dispatch settle event
|
|
395
|
-
|
|
396
|
+
vi.advanceTimersByTime(350)
|
|
396
397
|
|
|
397
398
|
expect(editor.dispatch).not.toHaveBeenCalled()
|
|
398
399
|
expect(clickManager.clickState).toBe('idle')
|
|
@@ -1,19 +1,20 @@
|
|
|
1
|
+
import { Mock, Mocked, vi } from 'vitest'
|
|
1
2
|
import { Box } from '../../../primitives/Box'
|
|
2
3
|
import { Vec } from '../../../primitives/Vec'
|
|
3
4
|
import { Editor } from '../../Editor'
|
|
4
5
|
import { EdgeScrollManager } from './EdgeScrollManager'
|
|
5
6
|
|
|
6
7
|
// Mock the Editor class
|
|
7
|
-
|
|
8
|
+
vi.mock('../../Editor')
|
|
8
9
|
|
|
9
10
|
describe('EdgeScrollManager', () => {
|
|
10
|
-
let editor:
|
|
11
|
+
let editor: Mocked<
|
|
11
12
|
Editor & {
|
|
12
|
-
user: { getEdgeScrollSpeed:
|
|
13
|
-
getCamera:
|
|
14
|
-
getCameraOptions:
|
|
15
|
-
getZoomLevel:
|
|
16
|
-
getViewportScreenBounds:
|
|
13
|
+
user: { getEdgeScrollSpeed: Mock }
|
|
14
|
+
getCamera: Mock
|
|
15
|
+
getCameraOptions: Mock
|
|
16
|
+
getZoomLevel: Mock
|
|
17
|
+
getViewportScreenBounds: Mock
|
|
17
18
|
}
|
|
18
19
|
>
|
|
19
20
|
let edgeScrollManager: EdgeScrollManager
|
|
@@ -33,33 +34,33 @@ describe('EdgeScrollManager', () => {
|
|
|
33
34
|
isPanning: false,
|
|
34
35
|
},
|
|
35
36
|
user: {
|
|
36
|
-
getEdgeScrollSpeed:
|
|
37
|
+
getEdgeScrollSpeed: vi.fn(() => 1),
|
|
37
38
|
},
|
|
38
|
-
getViewportScreenBounds:
|
|
39
|
-
getInstanceState:
|
|
39
|
+
getViewportScreenBounds: vi.fn(() => new Box(0, 0, 1000, 600)),
|
|
40
|
+
getInstanceState: vi.fn(
|
|
40
41
|
() =>
|
|
41
42
|
({
|
|
42
43
|
isCoarsePointer: false,
|
|
43
44
|
insets: [false, false, false, false], // [top, right, bottom, left]
|
|
44
45
|
}) as any
|
|
45
46
|
),
|
|
46
|
-
getCameraOptions:
|
|
47
|
+
getCameraOptions: vi.fn(() => ({
|
|
47
48
|
isLocked: false,
|
|
48
49
|
panSpeed: 1,
|
|
49
50
|
zoomSpeed: 1,
|
|
50
51
|
zoomSteps: [1],
|
|
51
52
|
wheelBehavior: 'pan' as const,
|
|
52
53
|
})),
|
|
53
|
-
getZoomLevel:
|
|
54
|
-
getCamera:
|
|
55
|
-
setCamera:
|
|
54
|
+
getZoomLevel: vi.fn(() => 1),
|
|
55
|
+
getCamera: vi.fn(() => new Vec(0, 0, 1)),
|
|
56
|
+
setCamera: vi.fn(),
|
|
56
57
|
} as any
|
|
57
58
|
|
|
58
59
|
edgeScrollManager = new EdgeScrollManager(editor as any)
|
|
59
60
|
})
|
|
60
61
|
|
|
61
62
|
afterEach(() => {
|
|
62
|
-
|
|
63
|
+
vi.clearAllMocks()
|
|
63
64
|
})
|
|
64
65
|
|
|
65
66
|
describe('constructor and initialization', () => {
|