@tldraw/editor 4.3.0-canary.c7096a59bf3b → 4.3.0-canary.cb6779b4f066
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/README.md +1 -1
- package/dist-cjs/index.d.ts +446 -120
- package/dist-cjs/index.js +8 -1
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/components/ErrorBoundary.js.map +1 -1
- package/dist-cjs/lib/components/GeometryDebuggingView.js +1 -17
- package/dist-cjs/lib/components/GeometryDebuggingView.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js +4 -5
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
- package/dist-cjs/lib/constants.js +1 -3
- package/dist-cjs/lib/constants.js.map +2 -2
- package/dist-cjs/lib/editor/Editor.js +342 -280
- package/dist-cjs/lib/editor/Editor.js.map +2 -2
- package/dist-cjs/lib/editor/derivations/notVisibleShapes.js +16 -23
- package/dist-cjs/lib/editor/derivations/notVisibleShapes.js.map +3 -3
- package/dist-cjs/lib/editor/derivations/parentsToChildren.js +12 -3
- package/dist-cjs/lib/editor/derivations/parentsToChildren.js.map +2 -2
- package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js +1 -1
- package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.js +5 -6
- package/dist-cjs/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/InputsManager/InputsManager.js +591 -0
- package/dist-cjs/lib/editor/managers/InputsManager/InputsManager.js.map +7 -0
- package/dist-cjs/lib/editor/managers/SnapManager/SnapManager.js +1 -1
- package/dist-cjs/lib/editor/managers/SnapManager/SnapManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/SpatialIndexManager/RBushIndex.js +144 -0
- package/dist-cjs/lib/editor/managers/SpatialIndexManager/RBushIndex.js.map +7 -0
- package/dist-cjs/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.js +181 -0
- package/dist-cjs/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.js.map +7 -0
- package/dist-cjs/lib/editor/managers/TickManager/TickManager.js +1 -22
- package/dist-cjs/lib/editor/managers/TickManager/TickManager.js.map +2 -2
- package/dist-cjs/lib/editor/shapes/ShapeUtil.js +31 -23
- package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
- package/dist-cjs/lib/editor/shapes/group/DashedOutlineBox.js +1 -1
- package/dist-cjs/lib/editor/shapes/group/DashedOutlineBox.js.map +2 -2
- package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js +3 -3
- package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js.map +2 -2
- package/dist-cjs/lib/exports/parseCss.js +1 -1
- package/dist-cjs/lib/exports/parseCss.js.map +2 -2
- package/dist-cjs/lib/globals/environment.js +45 -9
- package/dist-cjs/lib/globals/environment.js.map +2 -2
- package/dist-cjs/lib/hooks/useCoarsePointer.js +14 -29
- package/dist-cjs/lib/hooks/useCoarsePointer.js.map +2 -2
- package/dist-cjs/lib/hooks/useEvent.js +1 -1
- package/dist-cjs/lib/hooks/useEvent.js.map +2 -2
- package/dist-cjs/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/useGestureEvents.js +1 -1
- package/dist-cjs/lib/hooks/useGestureEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/usePassThroughMouseOverEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/usePassThroughWheelEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/useScreenBounds.js.map +2 -2
- package/dist-cjs/lib/hooks/useStateAttribute.js +4 -1
- package/dist-cjs/lib/hooks/useStateAttribute.js.map +2 -2
- package/dist-cjs/lib/hooks/useTransform.js.map +1 -1
- package/dist-cjs/lib/hooks/useZoomCss.js +4 -8
- package/dist-cjs/lib/hooks/useZoomCss.js.map +2 -2
- package/dist-cjs/lib/options.js +6 -1
- 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/Geometry2d.js +1 -0
- package/dist-cjs/lib/primitives/geometry/Geometry2d.js.map +2 -2
- package/dist-cjs/lib/utils/rotation.js +1 -1
- package/dist-cjs/lib/utils/rotation.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 +446 -120
- package/dist-esm/index.mjs +9 -2
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/components/ErrorBoundary.mjs.map +1 -1
- package/dist-esm/lib/components/GeometryDebuggingView.mjs +1 -17
- package/dist-esm/lib/components/GeometryDebuggingView.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +4 -5
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
- package/dist-esm/lib/constants.mjs +1 -3
- package/dist-esm/lib/constants.mjs.map +2 -2
- package/dist-esm/lib/editor/Editor.mjs +343 -283
- package/dist-esm/lib/editor/Editor.mjs.map +2 -2
- package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs +16 -23
- package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs.map +3 -3
- package/dist-esm/lib/editor/derivations/parentsToChildren.mjs +13 -4
- package/dist-esm/lib/editor/derivations/parentsToChildren.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs +1 -1
- package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.mjs +5 -6
- package/dist-esm/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/InputsManager/InputsManager.mjs +573 -0
- package/dist-esm/lib/editor/managers/InputsManager/InputsManager.mjs.map +7 -0
- package/dist-esm/lib/editor/managers/SnapManager/SnapManager.mjs +1 -1
- package/dist-esm/lib/editor/managers/SnapManager/SnapManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/SpatialIndexManager/RBushIndex.mjs +114 -0
- package/dist-esm/lib/editor/managers/SpatialIndexManager/RBushIndex.mjs.map +7 -0
- package/dist-esm/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.mjs +161 -0
- package/dist-esm/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.mjs.map +7 -0
- package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs +1 -22
- package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs.map +2 -2
- package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +31 -23
- package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/editor/shapes/group/DashedOutlineBox.mjs +1 -1
- package/dist-esm/lib/editor/shapes/group/DashedOutlineBox.mjs.map +2 -2
- package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs +3 -3
- package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs.map +2 -2
- package/dist-esm/lib/exports/parseCss.mjs +1 -1
- package/dist-esm/lib/exports/parseCss.mjs.map +2 -2
- package/dist-esm/lib/globals/environment.mjs +45 -9
- package/dist-esm/lib/globals/environment.mjs.map +2 -2
- package/dist-esm/lib/hooks/useCoarsePointer.mjs +15 -30
- package/dist-esm/lib/hooks/useCoarsePointer.mjs.map +2 -2
- package/dist-esm/lib/hooks/useEvent.mjs +1 -1
- package/dist-esm/lib/hooks/useEvent.mjs.map +2 -2
- package/dist-esm/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useGestureEvents.mjs +1 -1
- package/dist-esm/lib/hooks/useGestureEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/usePassThroughMouseOverEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useScreenBounds.mjs.map +2 -2
- package/dist-esm/lib/hooks/useStateAttribute.mjs +4 -1
- package/dist-esm/lib/hooks/useStateAttribute.mjs.map +2 -2
- package/dist-esm/lib/hooks/useTransform.mjs.map +1 -1
- package/dist-esm/lib/hooks/useZoomCss.mjs +4 -8
- package/dist-esm/lib/hooks/useZoomCss.mjs.map +2 -2
- package/dist-esm/lib/options.mjs +6 -1
- package/dist-esm/lib/options.mjs.map +2 -2
- package/dist-esm/lib/primitives/Box.mjs +3 -0
- package/dist-esm/lib/primitives/Box.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Geometry2d.mjs +1 -0
- package/dist-esm/lib/primitives/geometry/Geometry2d.mjs.map +2 -2
- package/dist-esm/lib/utils/rotation.mjs +1 -1
- package/dist-esm/lib/utils/rotation.mjs.map +2 -2
- package/dist-esm/version.mjs +3 -3
- package/dist-esm/version.mjs.map +1 -1
- package/editor.css +14 -12
- package/package.json +21 -17
- package/src/index.ts +5 -1
- package/src/lib/components/ErrorBoundary.tsx +1 -1
- package/src/lib/components/GeometryDebuggingView.tsx +1 -19
- package/src/lib/components/default-components/DefaultCanvas.tsx +4 -8
- package/src/lib/config/TLUserPreferences.test.ts +40 -0
- package/src/lib/constants.ts +0 -2
- package/src/lib/editor/Editor.test.ts +140 -0
- package/src/lib/editor/Editor.ts +448 -326
- package/src/lib/editor/derivations/notVisibleShapes.ts +21 -33
- package/src/lib/editor/derivations/parentsToChildren.ts +18 -7
- package/src/lib/editor/managers/ClickManager/ClickManager.test.ts +17 -31
- package/src/lib/editor/managers/ClickManager/ClickManager.ts +1 -1
- package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.test.ts +129 -79
- package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.ts +10 -6
- package/src/lib/editor/managers/InputsManager/InputsManager.ts +566 -0
- package/src/lib/editor/managers/ScribbleManager/ScribbleManager.test.ts +0 -4
- package/src/lib/editor/managers/SnapManager/SnapManager.test.ts +12 -0
- package/src/lib/editor/managers/SnapManager/SnapManager.ts +1 -1
- package/src/lib/editor/managers/SpatialIndexManager/RBushIndex.ts +144 -0
- package/src/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.ts +215 -0
- package/src/lib/editor/managers/TickManager/TickManager.test.ts +40 -107
- package/src/lib/editor/managers/TickManager/TickManager.ts +2 -32
- package/src/lib/editor/shapes/ShapeUtil.ts +67 -24
- package/src/lib/editor/shapes/group/DashedOutlineBox.tsx +1 -1
- package/src/lib/editor/tools/BaseBoxShapeTool/children/Pointing.ts +3 -3
- package/src/lib/exports/parseCss.test.ts +1 -0
- package/src/lib/exports/parseCss.ts +1 -1
- package/src/lib/globals/environment.ts +65 -10
- package/src/lib/hooks/useCoarsePointer.ts +16 -59
- package/src/lib/hooks/useEvent.tsx +1 -1
- package/src/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.ts +1 -1
- package/src/lib/hooks/useGestureEvents.ts +2 -2
- package/src/lib/hooks/usePassThroughMouseOverEvents.ts +1 -1
- package/src/lib/hooks/usePassThroughWheelEvents.ts +1 -1
- package/src/lib/hooks/useScreenBounds.ts +1 -1
- package/src/lib/hooks/useStateAttribute.ts +4 -1
- package/src/lib/hooks/useTransform.ts +1 -1
- package/src/lib/hooks/useZoomCss.ts +3 -8
- package/src/lib/options.ts +32 -0
- package/src/lib/primitives/Box.ts +9 -0
- package/src/lib/primitives/geometry/Geometry2d.ts +1 -0
- package/src/lib/utils/rotation.ts +1 -1
- package/src/version.ts +3 -3
package/src/lib/editor/Editor.ts
CHANGED
|
@@ -42,7 +42,6 @@ import {
|
|
|
42
42
|
TLInstance,
|
|
43
43
|
TLInstancePageState,
|
|
44
44
|
TLInstancePresence,
|
|
45
|
-
TLPOINTER_ID,
|
|
46
45
|
TLPage,
|
|
47
46
|
TLPageId,
|
|
48
47
|
TLParentId,
|
|
@@ -109,7 +108,6 @@ import {
|
|
|
109
108
|
MIDDLE_MOUSE_BUTTON,
|
|
110
109
|
RIGHT_MOUSE_BUTTON,
|
|
111
110
|
STYLUS_ERASER_BUTTON,
|
|
112
|
-
ZOOM_TO_FIT_PADDING,
|
|
113
111
|
} from '../constants'
|
|
114
112
|
import { exportToSvg } from '../exports/exportToSvg'
|
|
115
113
|
import { getSvgAsImage } from '../exports/getSvgAsImage'
|
|
@@ -135,7 +133,6 @@ import {
|
|
|
135
133
|
parseDeepLinkString,
|
|
136
134
|
} from '../utils/deepLinks'
|
|
137
135
|
import { getIncrementedName } from '../utils/getIncrementedName'
|
|
138
|
-
import { isAccelKey } from '../utils/keyboard'
|
|
139
136
|
import { getReorderingShapesChanges } from '../utils/reorderShapes'
|
|
140
137
|
import { TLTextOptions, TiptapEditor } from '../utils/richText'
|
|
141
138
|
import { applyRotationToSnapshotShapes, getRotationSnapshot } from '../utils/rotation'
|
|
@@ -149,22 +146,19 @@ import { EdgeScrollManager } from './managers/EdgeScrollManager/EdgeScrollManage
|
|
|
149
146
|
import { FocusManager } from './managers/FocusManager/FocusManager'
|
|
150
147
|
import { FontManager } from './managers/FontManager/FontManager'
|
|
151
148
|
import { HistoryManager } from './managers/HistoryManager/HistoryManager'
|
|
149
|
+
import { InputsManager } from './managers/InputsManager/InputsManager'
|
|
152
150
|
import { ScribbleManager } from './managers/ScribbleManager/ScribbleManager'
|
|
153
151
|
import { SnapManager } from './managers/SnapManager/SnapManager'
|
|
152
|
+
import { SpatialIndexManager } from './managers/SpatialIndexManager/SpatialIndexManager'
|
|
154
153
|
import { TextManager } from './managers/TextManager/TextManager'
|
|
155
154
|
import { TickManager } from './managers/TickManager/TickManager'
|
|
156
155
|
import { UserPreferencesManager } from './managers/UserPreferencesManager/UserPreferencesManager'
|
|
157
|
-
import { ShapeUtil, TLGeometryOpts, TLResizeMode } from './shapes/ShapeUtil'
|
|
156
|
+
import { ShapeUtil, TLEditStartInfo, TLGeometryOpts, TLResizeMode } from './shapes/ShapeUtil'
|
|
158
157
|
import { RootState } from './tools/RootState'
|
|
159
158
|
import { StateNode, TLStateNodeConstructor } from './tools/StateNode'
|
|
160
159
|
import { TLContent } from './types/clipboard-types'
|
|
161
160
|
import { TLEventMap } from './types/emit-types'
|
|
162
|
-
import {
|
|
163
|
-
TLEventInfo,
|
|
164
|
-
TLPinchEventInfo,
|
|
165
|
-
TLPointerEventInfo,
|
|
166
|
-
TLWheelEventInfo,
|
|
167
|
-
} from './types/event-types'
|
|
161
|
+
import { TLEventInfo, TLPointerEventInfo } from './types/event-types'
|
|
168
162
|
import { TLExternalAsset, TLExternalContent } from './types/external-content'
|
|
169
163
|
import { TLHistoryBatchOptions } from './types/history-types'
|
|
170
164
|
import {
|
|
@@ -195,7 +189,7 @@ export type TLResizeShapeOptions = Partial<{
|
|
|
195
189
|
/** @public */
|
|
196
190
|
export interface TLEditorOptions {
|
|
197
191
|
/**
|
|
198
|
-
* The Store instance to use for keeping the
|
|
192
|
+
* The Store instance to use for keeping the editor's data. This may be prepopulated, e.g. by loading
|
|
199
193
|
* from a server or database.
|
|
200
194
|
*/
|
|
201
195
|
store: TLStore
|
|
@@ -315,6 +309,9 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
315
309
|
|
|
316
310
|
this.snaps = new SnapManager(this)
|
|
317
311
|
|
|
312
|
+
this._spatialIndex = new SpatialIndexManager(this)
|
|
313
|
+
this.disposables.add(() => this._spatialIndex.dispose())
|
|
314
|
+
|
|
318
315
|
this.disposables.add(this.timers.dispose)
|
|
319
316
|
|
|
320
317
|
this._cameraOptions.set({ ...DEFAULT_CAMERA_OPTIONS, ...cameraOptions })
|
|
@@ -333,6 +330,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
333
330
|
|
|
334
331
|
this._tickManager = new TickManager(this)
|
|
335
332
|
|
|
333
|
+
this.inputs = new InputsManager(this)
|
|
334
|
+
|
|
336
335
|
class NewRoot extends RootState {
|
|
337
336
|
static override initial = initialState ?? ''
|
|
338
337
|
}
|
|
@@ -867,7 +866,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
867
866
|
}
|
|
868
867
|
|
|
869
868
|
/**
|
|
870
|
-
* A set of functions to call when the
|
|
869
|
+
* A set of functions to call when the editor is disposed.
|
|
871
870
|
*
|
|
872
871
|
* @public
|
|
873
872
|
*/
|
|
@@ -880,16 +879,28 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
880
879
|
*/
|
|
881
880
|
isDisposed = false
|
|
882
881
|
|
|
883
|
-
/**
|
|
884
|
-
|
|
882
|
+
/**
|
|
883
|
+
* A manager for the editor's tick events.
|
|
884
|
+
*
|
|
885
|
+
* @internal */
|
|
886
|
+
private readonly _tickManager: TickManager
|
|
885
887
|
|
|
886
888
|
/**
|
|
887
|
-
* A manager for the
|
|
889
|
+
* A manager for the editor's input state.
|
|
890
|
+
*
|
|
891
|
+
* @public
|
|
892
|
+
*/
|
|
893
|
+
readonly inputs: InputsManager
|
|
894
|
+
|
|
895
|
+
/**
|
|
896
|
+
* A manager for the editor's snapping feature.
|
|
888
897
|
*
|
|
889
898
|
* @public
|
|
890
899
|
*/
|
|
891
900
|
readonly snaps: SnapManager
|
|
892
901
|
|
|
902
|
+
private readonly _spatialIndex: SpatialIndexManager
|
|
903
|
+
|
|
893
904
|
/**
|
|
894
905
|
* A manager for the any asynchronous events and making sure they're
|
|
895
906
|
* cleaned up upon disposal.
|
|
@@ -1061,7 +1072,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1061
1072
|
/* --------------------- History -------------------- */
|
|
1062
1073
|
|
|
1063
1074
|
/**
|
|
1064
|
-
* A manager for the
|
|
1075
|
+
* A manager for the editor's history.
|
|
1065
1076
|
*
|
|
1066
1077
|
* @readonly
|
|
1067
1078
|
*/
|
|
@@ -1085,14 +1096,18 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1085
1096
|
}
|
|
1086
1097
|
|
|
1087
1098
|
/**
|
|
1088
|
-
* Whether the
|
|
1099
|
+
* Whether the editor can undo.
|
|
1089
1100
|
*
|
|
1090
1101
|
* @public
|
|
1091
1102
|
*/
|
|
1092
|
-
@computed
|
|
1103
|
+
@computed canUndo(): boolean {
|
|
1093
1104
|
return this.history.getNumUndos() > 0
|
|
1094
1105
|
}
|
|
1095
1106
|
|
|
1107
|
+
getCanUndo() {
|
|
1108
|
+
return this.canUndo()
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1096
1111
|
/**
|
|
1097
1112
|
* Redo to the next mark.
|
|
1098
1113
|
*
|
|
@@ -1110,20 +1125,24 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1110
1125
|
return this
|
|
1111
1126
|
}
|
|
1112
1127
|
|
|
1113
|
-
clearHistory() {
|
|
1114
|
-
this.history.clear()
|
|
1115
|
-
return this
|
|
1116
|
-
}
|
|
1117
|
-
|
|
1118
1128
|
/**
|
|
1119
|
-
* Whether the
|
|
1129
|
+
* Whether the editor can redo.
|
|
1120
1130
|
*
|
|
1121
1131
|
* @public
|
|
1122
1132
|
*/
|
|
1123
|
-
@computed
|
|
1133
|
+
@computed canRedo(): boolean {
|
|
1124
1134
|
return this.history.getNumRedos() > 0
|
|
1125
1135
|
}
|
|
1126
1136
|
|
|
1137
|
+
getCanRedo() {
|
|
1138
|
+
return this.canRedo()
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
clearHistory() {
|
|
1142
|
+
this.history.clear()
|
|
1143
|
+
return this
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1127
1146
|
/**
|
|
1128
1147
|
* Create a new "mark", or stopping point, in the undo redo history. Creating a mark will clear
|
|
1129
1148
|
* any redos. You typically want to do this just before a user interaction begins or is handled.
|
|
@@ -1297,7 +1316,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1297
1316
|
}),
|
|
1298
1317
|
selectionCount: this.getSelectedShapes().length,
|
|
1299
1318
|
editingShape: editingShapeId ? this.getShape(editingShapeId) : undefined,
|
|
1300
|
-
inputs: this.inputs,
|
|
1319
|
+
inputs: this.inputs.toJson(),
|
|
1301
1320
|
pageState: this.getCurrentPageState(),
|
|
1302
1321
|
instanceState: this.getInstanceState(),
|
|
1303
1322
|
collaboratorCount: this.getCollaboratorsOnCurrentPage().length,
|
|
@@ -1322,7 +1341,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1322
1341
|
* we're in a transaction that's about to be rolled back due to the same error we're currently
|
|
1323
1342
|
* reporting.
|
|
1324
1343
|
*
|
|
1325
|
-
* Instead, to listen to changes to this value, you need to listen to
|
|
1344
|
+
* Instead, to listen to changes to this value, you need to listen to editor's `crash` event.
|
|
1326
1345
|
*
|
|
1327
1346
|
* @internal
|
|
1328
1347
|
*/
|
|
@@ -2025,7 +2044,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2025
2044
|
}
|
|
2026
2045
|
|
|
2027
2046
|
/**
|
|
2028
|
-
* The id of the
|
|
2047
|
+
* The id of the editor's only selected shape.
|
|
2029
2048
|
*
|
|
2030
2049
|
* @returns Null if there is no shape or more than one selected shape, otherwise the selected shape's id.
|
|
2031
2050
|
*
|
|
@@ -2037,7 +2056,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2037
2056
|
}
|
|
2038
2057
|
|
|
2039
2058
|
/**
|
|
2040
|
-
* The
|
|
2059
|
+
* The editor's only selected shape.
|
|
2041
2060
|
*
|
|
2042
2061
|
* @returns Null if there is no shape or more than one selected shape, otherwise the selected shape.
|
|
2043
2062
|
*
|
|
@@ -2278,6 +2297,29 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2278
2297
|
return editingShapeId ? this.getShape(editingShapeId) : undefined
|
|
2279
2298
|
}
|
|
2280
2299
|
|
|
2300
|
+
/**
|
|
2301
|
+
* Whether the shape can be edited.
|
|
2302
|
+
*
|
|
2303
|
+
* @param shape - The shape (or shape id) to check if it can be edited.
|
|
2304
|
+
* @param info - The info about the edit start.
|
|
2305
|
+
*
|
|
2306
|
+
* @public
|
|
2307
|
+
* @returns true if the shape can be edited, false otherwise.
|
|
2308
|
+
*/
|
|
2309
|
+
canEditShape<T extends TLShape | TLShapeId>(shape: T | null, info?: TLEditStartInfo): shape is T {
|
|
2310
|
+
const id = typeof shape === 'string' ? shape : (shape?.id ?? null)
|
|
2311
|
+
if (!id) return false // no shape
|
|
2312
|
+
if (id === this.getEditingShapeId()) return false // already editing this shape
|
|
2313
|
+
const _shape = this.getShape(id)
|
|
2314
|
+
if (!_shape) return false // no shape
|
|
2315
|
+
const util = this.getShapeUtil(_shape)
|
|
2316
|
+
const _info: TLEditStartInfo = info ?? { type: 'unknown' }
|
|
2317
|
+
if (!util.canEdit(_shape, _info)) return false // shape is not editable
|
|
2318
|
+
if (this.getIsReadonly() && !util.canEditInReadonly(_shape)) return false // readonly and no exception
|
|
2319
|
+
if (this.isShapeOrAncestorLocked(_shape) && !util.canEditWhileLocked(_shape)) return false // locked and no exception. Note here: we're not distinguishing between a locked shape and a shape that is the descendant of a locked shape.
|
|
2320
|
+
return true // shape is editable
|
|
2321
|
+
}
|
|
2322
|
+
|
|
2281
2323
|
/**
|
|
2282
2324
|
* Set the current editing shape.
|
|
2283
2325
|
*
|
|
@@ -2293,44 +2335,59 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2293
2335
|
*/
|
|
2294
2336
|
setEditingShape(shape: TLShapeId | TLShape | null): this {
|
|
2295
2337
|
const id = typeof shape === 'string' ? shape : (shape?.id ?? null)
|
|
2296
|
-
this.setRichTextEditor(null)
|
|
2297
|
-
const prevEditingShapeId = this.getEditingShapeId()
|
|
2298
|
-
if (id !== prevEditingShapeId) {
|
|
2299
|
-
if (id) {
|
|
2300
|
-
const shape = this.getShape(id)
|
|
2301
|
-
if (shape && this.getShapeUtil(shape).canEdit(shape)) {
|
|
2302
|
-
this.run(
|
|
2303
|
-
() => {
|
|
2304
|
-
this._updateCurrentPageState({ editingShapeId: id })
|
|
2305
|
-
if (prevEditingShapeId) {
|
|
2306
|
-
const prevEditingShape = this.getShape(prevEditingShapeId)
|
|
2307
|
-
if (prevEditingShape) {
|
|
2308
|
-
this.getShapeUtil(prevEditingShape).onEditEnd?.(prevEditingShape)
|
|
2309
|
-
}
|
|
2310
|
-
}
|
|
2311
|
-
this.getShapeUtil(shape).onEditStart?.(shape)
|
|
2312
|
-
},
|
|
2313
|
-
{ history: 'ignore' }
|
|
2314
|
-
)
|
|
2315
|
-
return this
|
|
2316
|
-
}
|
|
2317
|
-
}
|
|
2318
2338
|
|
|
2319
|
-
|
|
2339
|
+
if (!id) {
|
|
2340
|
+
// setting the editing shape to null
|
|
2320
2341
|
this.run(
|
|
2321
2342
|
() => {
|
|
2322
|
-
|
|
2323
|
-
this.
|
|
2343
|
+
// Clean up the previous editing shape
|
|
2344
|
+
const prevEditingShapeId = this.getEditingShapeId()
|
|
2324
2345
|
if (prevEditingShapeId) {
|
|
2325
2346
|
const prevEditingShape = this.getShape(prevEditingShapeId)
|
|
2326
2347
|
if (prevEditingShape) {
|
|
2327
2348
|
this.getShapeUtil(prevEditingShape).onEditEnd?.(prevEditingShape)
|
|
2328
2349
|
}
|
|
2329
2350
|
}
|
|
2351
|
+
|
|
2352
|
+
// Clean up the editing shape state and rich text editor
|
|
2353
|
+
this._updateCurrentPageState({ editingShapeId: null })
|
|
2354
|
+
this._currentRichTextEditor.set(null)
|
|
2330
2355
|
},
|
|
2331
2356
|
{ history: 'ignore' }
|
|
2332
2357
|
)
|
|
2358
|
+
|
|
2359
|
+
return this
|
|
2333
2360
|
}
|
|
2361
|
+
|
|
2362
|
+
// id was provided but the next editing shape was not editable or didn't exist, so do nothing
|
|
2363
|
+
if (!this.canEditShape(id)) return this
|
|
2364
|
+
|
|
2365
|
+
// id was provided and the next editing shape is editable, so set the rich text editor to null
|
|
2366
|
+
this.run(
|
|
2367
|
+
() => {
|
|
2368
|
+
// Clean up the previous editing shape
|
|
2369
|
+
const prevEditingShapeId = this.getEditingShapeId()
|
|
2370
|
+
if (prevEditingShapeId) {
|
|
2371
|
+
const prevEditingShape = this.getShape(prevEditingShapeId)
|
|
2372
|
+
if (prevEditingShape) {
|
|
2373
|
+
this.getShapeUtil(prevEditingShape).onEditEnd?.(prevEditingShape)
|
|
2374
|
+
}
|
|
2375
|
+
}
|
|
2376
|
+
|
|
2377
|
+
// Clean up the editing shape state and rich text editor
|
|
2378
|
+
this._updateCurrentPageState({ editingShapeId: null })
|
|
2379
|
+
this._currentRichTextEditor.set(null)
|
|
2380
|
+
|
|
2381
|
+
// Set the new editing shape
|
|
2382
|
+
this.select(id)
|
|
2383
|
+
this._updateCurrentPageState({ editingShapeId: id })
|
|
2384
|
+
|
|
2385
|
+
const nextEditingShape = this.getShape(id)! // shape should be there because canEditShape checked it. Possible small chance that onEditEnd deleted it?
|
|
2386
|
+
this.getShapeUtil(nextEditingShape).onEditStart?.(nextEditingShape)
|
|
2387
|
+
},
|
|
2388
|
+
{ history: 'ignore' }
|
|
2389
|
+
)
|
|
2390
|
+
|
|
2334
2391
|
return this
|
|
2335
2392
|
}
|
|
2336
2393
|
|
|
@@ -2534,6 +2591,26 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2534
2591
|
return this.getCurrentPageState().croppingShapeId
|
|
2535
2592
|
}
|
|
2536
2593
|
|
|
2594
|
+
/**
|
|
2595
|
+
* Whether the shape can be cropped.
|
|
2596
|
+
*
|
|
2597
|
+
* @param shape - The shape (or shape id) to check if it can be cropped.
|
|
2598
|
+
*
|
|
2599
|
+
* @public
|
|
2600
|
+
* @returns true if the shape can be cropped, false otherwise.
|
|
2601
|
+
*/
|
|
2602
|
+
canCropShape<T extends TLShape | TLShapeId>(shape: T | null): shape is T {
|
|
2603
|
+
if (!shape) return false
|
|
2604
|
+
const id = typeof shape === 'string' ? shape : (shape?.id ?? null)
|
|
2605
|
+
if (!id) return false
|
|
2606
|
+
const _shape = this.getShape(id)
|
|
2607
|
+
if (!_shape) return false
|
|
2608
|
+
const util = this.getShapeUtil(_shape)
|
|
2609
|
+
if (!util.canCrop(_shape)) return false
|
|
2610
|
+
if (this.isShapeOrAncestorLocked(_shape)) return false
|
|
2611
|
+
return true
|
|
2612
|
+
}
|
|
2613
|
+
|
|
2537
2614
|
/**
|
|
2538
2615
|
* Set the current cropping shape.
|
|
2539
2616
|
*
|
|
@@ -2555,12 +2632,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2555
2632
|
() => {
|
|
2556
2633
|
if (!id) {
|
|
2557
2634
|
this.updateCurrentPageState({ croppingShapeId: null })
|
|
2558
|
-
} else {
|
|
2559
|
-
|
|
2560
|
-
const util = this.getShapeUtil(shape)
|
|
2561
|
-
if (shape && util.canCrop(shape)) {
|
|
2562
|
-
this.updateCurrentPageState({ croppingShapeId: id })
|
|
2563
|
-
}
|
|
2635
|
+
} else if (this.canCropShape(id)) {
|
|
2636
|
+
this.updateCurrentPageState({ croppingShapeId: id })
|
|
2564
2637
|
}
|
|
2565
2638
|
},
|
|
2566
2639
|
{ history: 'ignore' }
|
|
@@ -2670,6 +2743,52 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2670
2743
|
return this.getCamera().z
|
|
2671
2744
|
}
|
|
2672
2745
|
|
|
2746
|
+
private _debouncedZoomLevel = atom('debounced zoom level', 1)
|
|
2747
|
+
|
|
2748
|
+
/**
|
|
2749
|
+
* Get the debounced zoom level. When the camera is moving, this returns the zoom level
|
|
2750
|
+
* from when the camera started moving rather than the current zoom level. This can be
|
|
2751
|
+
* used to avoid expensive re-renders during camera movements.
|
|
2752
|
+
*
|
|
2753
|
+
* This behavior is controlled by the `useDebouncedZoom` option. When `useDebouncedZoom`
|
|
2754
|
+
* is `false`, this method always returns the current zoom level.
|
|
2755
|
+
*
|
|
2756
|
+
* @public
|
|
2757
|
+
*/
|
|
2758
|
+
@computed getDebouncedZoomLevel() {
|
|
2759
|
+
if (this.options.debouncedZoom) {
|
|
2760
|
+
if (this.getCameraState() === 'idle') {
|
|
2761
|
+
return this.getZoomLevel()
|
|
2762
|
+
} else {
|
|
2763
|
+
return this._debouncedZoomLevel.get()
|
|
2764
|
+
}
|
|
2765
|
+
}
|
|
2766
|
+
|
|
2767
|
+
return this.getZoomLevel()
|
|
2768
|
+
}
|
|
2769
|
+
|
|
2770
|
+
@computed private _getAboveDebouncedZoomThreshold() {
|
|
2771
|
+
return this.getCurrentPageShapeIds().size > this.options.debouncedZoomThreshold
|
|
2772
|
+
}
|
|
2773
|
+
|
|
2774
|
+
/**
|
|
2775
|
+
* Get the efficient zoom level. This returns the current zoom level if there are less than 300 shapes on the page,
|
|
2776
|
+
* otherwise it returns the debounced zoom level. This can be used to avoid expensive re-renders during camera movements.
|
|
2777
|
+
*
|
|
2778
|
+
* @public
|
|
2779
|
+
* @example
|
|
2780
|
+
* ```ts
|
|
2781
|
+
* editor.getEfficientZoomLevel()
|
|
2782
|
+
* ```
|
|
2783
|
+
*
|
|
2784
|
+
* @public
|
|
2785
|
+
*/
|
|
2786
|
+
@computed getEfficientZoomLevel() {
|
|
2787
|
+
return this._getAboveDebouncedZoomThreshold()
|
|
2788
|
+
? this.getDebouncedZoomLevel()
|
|
2789
|
+
: this.getZoomLevel()
|
|
2790
|
+
}
|
|
2791
|
+
|
|
2673
2792
|
/**
|
|
2674
2793
|
* Get the camera's initial or reset zoom level.
|
|
2675
2794
|
*
|
|
@@ -2996,7 +3115,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2996
3115
|
|
|
2997
3116
|
// Dispatch a new pointer move because the pointer's page will have changed
|
|
2998
3117
|
// (its screen position will compute to a new page position given the new camera position)
|
|
2999
|
-
const
|
|
3118
|
+
const currentScreenPoint = this.inputs.getCurrentScreenPoint()
|
|
3119
|
+
const currentPagePoint = this.inputs.getCurrentPagePoint()
|
|
3000
3120
|
|
|
3001
3121
|
// compare the next page point (derived from the current camera) to the current page point
|
|
3002
3122
|
if (
|
|
@@ -3160,7 +3280,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3160
3280
|
* ```ts
|
|
3161
3281
|
* editor.zoomIn()
|
|
3162
3282
|
* editor.zoomIn(editor.getViewportScreenCenter(), { animation: { duration: 200 } })
|
|
3163
|
-
* editor.zoomIn(editor.inputs.
|
|
3283
|
+
* editor.zoomIn(editor.inputs.getCurrentScreenPoint(), { animation: { duration: 200 } })
|
|
3164
3284
|
* ```
|
|
3165
3285
|
*
|
|
3166
3286
|
* @param point - The screen point to zoom in on. Defaults to the screen center
|
|
@@ -3205,7 +3325,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3205
3325
|
* ```ts
|
|
3206
3326
|
* editor.zoomOut()
|
|
3207
3327
|
* editor.zoomOut(editor.getViewportScreenCenter(), { animation: { duration: 120 } })
|
|
3208
|
-
* editor.zoomOut(editor.inputs.
|
|
3328
|
+
* editor.zoomOut(editor.inputs.getCurrentScreenPoint(), { animation: { duration: 120 } })
|
|
3209
3329
|
* ```
|
|
3210
3330
|
*
|
|
3211
3331
|
* @param point - The point to zoom out on. Defaults to the viewport screen center.
|
|
@@ -3262,10 +3382,17 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3262
3382
|
|
|
3263
3383
|
const selectionPageBounds = this.getSelectionPageBounds()
|
|
3264
3384
|
if (selectionPageBounds) {
|
|
3265
|
-
this.
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3385
|
+
const currentZoom = this.getZoomLevel()
|
|
3386
|
+
// If already at 100%, zoom to fit the selection in the viewport
|
|
3387
|
+
// Otherwise, zoom to 100% centered on the selection
|
|
3388
|
+
if (Math.abs(currentZoom - 1) < 0.01) {
|
|
3389
|
+
this.zoomToBounds(selectionPageBounds, opts)
|
|
3390
|
+
} else {
|
|
3391
|
+
this.zoomToBounds(selectionPageBounds, {
|
|
3392
|
+
targetZoom: 1,
|
|
3393
|
+
...opts,
|
|
3394
|
+
})
|
|
3395
|
+
}
|
|
3269
3396
|
}
|
|
3270
3397
|
return this
|
|
3271
3398
|
}
|
|
@@ -3322,7 +3449,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3322
3449
|
|
|
3323
3450
|
const viewportScreenBounds = this.getViewportScreenBounds()
|
|
3324
3451
|
|
|
3325
|
-
const inset =
|
|
3452
|
+
const inset =
|
|
3453
|
+
opts?.inset ?? Math.min(this.options.zoomToFitPadding, viewportScreenBounds.width * 0.28)
|
|
3326
3454
|
|
|
3327
3455
|
const baseZoom = this.getBaseZoom()
|
|
3328
3456
|
const zoomMin = cameraOptions.zoomSteps[0]
|
|
@@ -3649,8 +3777,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3649
3777
|
}
|
|
3650
3778
|
}
|
|
3651
3779
|
|
|
3652
|
-
this._tickCameraState()
|
|
3653
|
-
|
|
3654
3780
|
return this
|
|
3655
3781
|
}
|
|
3656
3782
|
|
|
@@ -4056,18 +4182,19 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
4056
4182
|
// box just for rendering, and we only update after the camera stops moving.
|
|
4057
4183
|
private _cameraState = atom('camera state', 'idle' as 'idle' | 'moving')
|
|
4058
4184
|
private _cameraStateTimeoutRemaining = 0
|
|
4059
|
-
_decayCameraStateTimeout(elapsed: number) {
|
|
4185
|
+
private _decayCameraStateTimeout(elapsed: number) {
|
|
4060
4186
|
this._cameraStateTimeoutRemaining -= elapsed
|
|
4061
4187
|
if (this._cameraStateTimeoutRemaining > 0) return
|
|
4062
4188
|
this.off('tick', this._decayCameraStateTimeout)
|
|
4063
4189
|
this._cameraState.set('idle')
|
|
4064
4190
|
}
|
|
4065
|
-
_tickCameraState() {
|
|
4191
|
+
private _tickCameraState() {
|
|
4066
4192
|
// always reset the timeout
|
|
4067
4193
|
this._cameraStateTimeoutRemaining = this.options.cameraMovingTimeoutMs
|
|
4068
4194
|
// If the state is idle, then start the tick
|
|
4069
4195
|
if (this._cameraState.__unsafe__getWithoutCapture() !== 'idle') return
|
|
4070
4196
|
this._cameraState.set('moving')
|
|
4197
|
+
this._debouncedZoomLevel.set(unsafe__withoutCapture(() => this.getCamera().z))
|
|
4071
4198
|
this.on('tick', this._decayCameraStateTimeout)
|
|
4072
4199
|
}
|
|
4073
4200
|
|
|
@@ -5014,6 +5141,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5014
5141
|
}
|
|
5015
5142
|
|
|
5016
5143
|
private _notVisibleShapes = notVisibleShapes(this)
|
|
5144
|
+
private _culledShapesCache: Set<TLShapeId> | null = null
|
|
5017
5145
|
|
|
5018
5146
|
/**
|
|
5019
5147
|
* Get culled shapes (those that should not render), taking into account which shapes are selected or editing.
|
|
@@ -5025,16 +5153,41 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5025
5153
|
const notVisibleShapes = this.getNotVisibleShapes()
|
|
5026
5154
|
const selectedShapeIds = this.getSelectedShapeIds()
|
|
5027
5155
|
const editingId = this.getEditingShapeId()
|
|
5028
|
-
const
|
|
5156
|
+
const nextValue = new Set<TLShapeId>(notVisibleShapes)
|
|
5029
5157
|
// we don't cull the shape we are editing
|
|
5030
5158
|
if (editingId) {
|
|
5031
|
-
|
|
5159
|
+
nextValue.delete(editingId)
|
|
5032
5160
|
}
|
|
5033
5161
|
// we also don't cull selected shapes
|
|
5034
5162
|
selectedShapeIds.forEach((id) => {
|
|
5035
|
-
|
|
5163
|
+
nextValue.delete(id)
|
|
5036
5164
|
})
|
|
5037
|
-
|
|
5165
|
+
|
|
5166
|
+
// Cache optimization: return same Set object if contents unchanged
|
|
5167
|
+
// This allows consumers to use === comparison and prevents unnecessary re-renders
|
|
5168
|
+
const prevValue = this._culledShapesCache
|
|
5169
|
+
if (prevValue) {
|
|
5170
|
+
// If sizes differ, contents must differ
|
|
5171
|
+
if (prevValue.size !== nextValue.size) {
|
|
5172
|
+
this._culledShapesCache = nextValue
|
|
5173
|
+
return nextValue
|
|
5174
|
+
}
|
|
5175
|
+
|
|
5176
|
+
// Check if all elements are the same
|
|
5177
|
+
for (const id of prevValue) {
|
|
5178
|
+
if (!nextValue.has(id)) {
|
|
5179
|
+
// Found a difference, update cache and return new set
|
|
5180
|
+
this._culledShapesCache = nextValue
|
|
5181
|
+
return nextValue
|
|
5182
|
+
}
|
|
5183
|
+
}
|
|
5184
|
+
|
|
5185
|
+
// Loop completed without finding differences - contents identical
|
|
5186
|
+
return prevValue
|
|
5187
|
+
}
|
|
5188
|
+
|
|
5189
|
+
this._culledShapesCache = nextValue
|
|
5190
|
+
return nextValue
|
|
5038
5191
|
}
|
|
5039
5192
|
|
|
5040
5193
|
/**
|
|
@@ -5101,11 +5254,18 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5101
5254
|
let inMarginClosestToEdgeDistance = Infinity
|
|
5102
5255
|
let inMarginClosestToEdgeHit: TLShape | null = null
|
|
5103
5256
|
|
|
5257
|
+
// Use larger margin for spatial search to account for edge distance checks
|
|
5258
|
+
const searchMargin = Math.max(innerMargin, outerMargin, this.options.hitTestMargin / zoomLevel)
|
|
5259
|
+
const candidateIds = this._spatialIndex.getShapeIdsAtPoint(point, searchMargin)
|
|
5260
|
+
|
|
5104
5261
|
const shapesToCheck = (
|
|
5105
5262
|
opts.renderingOnly
|
|
5106
5263
|
? this.getCurrentPageRenderingShapesSorted()
|
|
5107
5264
|
: this.getCurrentPageShapesSorted()
|
|
5108
5265
|
).filter((shape) => {
|
|
5266
|
+
// Frames have labels positioned above the shape (outside bounds), so always include them
|
|
5267
|
+
if (!candidateIds.has(shape.id) && !this.isShapeOfType(shape, 'frame')) return false
|
|
5268
|
+
|
|
5109
5269
|
if (
|
|
5110
5270
|
(shape.isLocked && !hitLocked) ||
|
|
5111
5271
|
this.isShapeHidden(shape) ||
|
|
@@ -5291,11 +5451,41 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5291
5451
|
point: VecLike,
|
|
5292
5452
|
opts = {} as { margin?: number; hitInside?: boolean }
|
|
5293
5453
|
): TLShape[] {
|
|
5454
|
+
const margin = opts.margin ?? 0
|
|
5455
|
+
const candidateIds = this._spatialIndex.getShapeIdsAtPoint(point, margin)
|
|
5456
|
+
|
|
5457
|
+
// Get all page shapes in z-index order and filter to candidates that pass isPointInShape
|
|
5458
|
+
// Frames are always checked because their labels can be outside their bounds
|
|
5294
5459
|
return this.getCurrentPageShapesSorted()
|
|
5295
|
-
.filter((shape) =>
|
|
5460
|
+
.filter((shape) => {
|
|
5461
|
+
if (this.isShapeHidden(shape)) return false
|
|
5462
|
+
if (!candidateIds.has(shape.id) && !this.isShapeOfType(shape, 'frame')) return false
|
|
5463
|
+
return this.isPointInShape(shape, point, opts)
|
|
5464
|
+
})
|
|
5296
5465
|
.reverse()
|
|
5297
5466
|
}
|
|
5298
5467
|
|
|
5468
|
+
/**
|
|
5469
|
+
* Get shape IDs within the given bounds.
|
|
5470
|
+
*
|
|
5471
|
+
* Note: Uses shape page bounds only. Frames with labels outside their bounds
|
|
5472
|
+
* may not be included even if the label is within the search bounds.
|
|
5473
|
+
*
|
|
5474
|
+
* Note: Results are unordered. If you need z-order, combine with sorted shapes:
|
|
5475
|
+
* ```ts
|
|
5476
|
+
* const candidates = editor.getShapeIdsInsideBounds(bounds)
|
|
5477
|
+
* const sorted = editor.getCurrentPageShapesSorted().filter(s => candidates.has(s.id))
|
|
5478
|
+
* ```
|
|
5479
|
+
*
|
|
5480
|
+
* @param bounds - The bounds to search within.
|
|
5481
|
+
* @returns Unordered set of shape IDs within the given bounds.
|
|
5482
|
+
*
|
|
5483
|
+
* @internal
|
|
5484
|
+
*/
|
|
5485
|
+
getShapeIdsInsideBounds(bounds: Box): Set<TLShapeId> {
|
|
5486
|
+
return this._spatialIndex.getShapeIdsInsideBounds(bounds)
|
|
5487
|
+
}
|
|
5488
|
+
|
|
5299
5489
|
/**
|
|
5300
5490
|
* Test whether a point (in the current page space) will will a shape. This method takes into account masks,
|
|
5301
5491
|
* such as when a shape is the child of a frame and is partially clipped by the frame.
|
|
@@ -7653,8 +7843,14 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
7653
7843
|
// then if the shape is flipped in one axis only, we need to apply an extra rotation
|
|
7654
7844
|
// to make sure the shape is mirrored correctly
|
|
7655
7845
|
if (Math.sign(scale.x) * Math.sign(scale.y) < 0) {
|
|
7656
|
-
|
|
7657
|
-
rotation
|
|
7846
|
+
// We need to compute the new local rotation that will result in the negated page rotation.
|
|
7847
|
+
// For a shape with local rotation `localRot` and parent page rotation `parentRot`:
|
|
7848
|
+
// - pageRot = parentRot + localRot
|
|
7849
|
+
// - newPageRot = -pageRot (we want to negate the page rotation)
|
|
7850
|
+
// - newPageRot = parentRot + newLocalRot (parent hasn't changed)
|
|
7851
|
+
// - Therefore: newLocalRot = -pageRot - parentRot = -(parentRot + localRot) - parentRot = -localRot - 2*parentRot
|
|
7852
|
+
const parentRotation = this.getShapeParentTransform(id).rotation()
|
|
7853
|
+
const rotation = -options.initialShape.rotation - 2 * parentRotation
|
|
7658
7854
|
this.updateShapes([{ id, type, rotation }])
|
|
7659
7855
|
}
|
|
7660
7856
|
|
|
@@ -7674,9 +7870,13 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
7674
7870
|
)
|
|
7675
7871
|
|
|
7676
7872
|
// now calculate how far away the shape is from where it needs to be
|
|
7677
|
-
const pageBounds = this.getShapePageBounds(id)!
|
|
7678
7873
|
const pageTransform = this.getShapePageTransform(id)!
|
|
7679
|
-
|
|
7874
|
+
// We need to use the local bounds center transformed to page space, not the axis-aligned
|
|
7875
|
+
// page bounds center. This is because the page bounds are axis-aligned and their center
|
|
7876
|
+
// changes when the rotation changes, but we want to use the same reference point as
|
|
7877
|
+
// preScaleShapePageCenter (which used initialBounds.center transformed by the page transform).
|
|
7878
|
+
const currentLocalBounds = this.getShapeGeometry(id).bounds
|
|
7879
|
+
const currentPageCenter = Mat.applyToPoint(pageTransform, currentLocalBounds.center)
|
|
7680
7880
|
const shapePageTransformOrigin = pageTransform.point()
|
|
7681
7881
|
if (!currentPageCenter || !shapePageTransformOrigin) return this
|
|
7682
7882
|
const pageDelta = Vec.Sub(postScaleShapePageCenter, currentPageCenter)
|
|
@@ -8124,7 +8324,12 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8124
8324
|
)
|
|
8125
8325
|
)
|
|
8126
8326
|
const sortedShapeIds = shapesToGroup.sort(sortByIndex).map((s) => s.id)
|
|
8127
|
-
const
|
|
8327
|
+
const childBounds = compact(shapesToGroup.map((shape) => this.getShapePageBounds(shape)))
|
|
8328
|
+
const pageBounds = Box.Common(childBounds)
|
|
8329
|
+
|
|
8330
|
+
if (!pageBounds.isValid()) {
|
|
8331
|
+
throw Error(`Editor.groupShapes: group bounds are invalid (NaN).`)
|
|
8332
|
+
}
|
|
8128
8333
|
|
|
8129
8334
|
const { x, y } = pageBounds.point
|
|
8130
8335
|
|
|
@@ -9149,6 +9354,30 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9149
9354
|
}
|
|
9150
9355
|
}
|
|
9151
9356
|
|
|
9357
|
+
if (point) {
|
|
9358
|
+
const shapesById = new Map<TLShapeId, TLShape>(shapes.map((shape) => [shape.id, shape]))
|
|
9359
|
+
const rootShapesFromContent = compact(rootShapeIds.map((id) => shapesById.get(id)))
|
|
9360
|
+
if (rootShapesFromContent.length > 0) {
|
|
9361
|
+
const targetParent = this.getShapeAtPoint(point, {
|
|
9362
|
+
hitInside: true,
|
|
9363
|
+
hitFrameInside: true,
|
|
9364
|
+
hitLocked: true,
|
|
9365
|
+
filter: (shape) => {
|
|
9366
|
+
const util = this.getShapeUtil(shape)
|
|
9367
|
+
if (!util.canReceiveNewChildrenOfType) return false
|
|
9368
|
+
return rootShapesFromContent.every((rootShape) =>
|
|
9369
|
+
util.canReceiveNewChildrenOfType!(shape, rootShape.type)
|
|
9370
|
+
)
|
|
9371
|
+
},
|
|
9372
|
+
})
|
|
9373
|
+
|
|
9374
|
+
// When pasting at a specific point (e.g. paste-at-cursor) prefer the
|
|
9375
|
+
// parent under the pointer so that we don't keep using the original
|
|
9376
|
+
// selection's parent (which can keep shapes clipped inside frames).
|
|
9377
|
+
pasteParentId = targetParent ? targetParent.id : currentPageId
|
|
9378
|
+
}
|
|
9379
|
+
}
|
|
9380
|
+
|
|
9152
9381
|
let isDuplicating = false
|
|
9153
9382
|
|
|
9154
9383
|
if (!isPageId(pasteParentId)) {
|
|
@@ -9486,126 +9715,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9486
9715
|
|
|
9487
9716
|
/* --------------------- Events --------------------- */
|
|
9488
9717
|
|
|
9489
|
-
/**
|
|
9490
|
-
* The app's current input state.
|
|
9491
|
-
*
|
|
9492
|
-
* @public
|
|
9493
|
-
*/
|
|
9494
|
-
inputs = {
|
|
9495
|
-
/** The most recent pointer down's position in the current page space. */
|
|
9496
|
-
originPagePoint: new Vec(),
|
|
9497
|
-
/** The most recent pointer down's position in screen space. */
|
|
9498
|
-
originScreenPoint: new Vec(),
|
|
9499
|
-
/** The previous pointer position in the current page space. */
|
|
9500
|
-
previousPagePoint: new Vec(),
|
|
9501
|
-
/** The previous pointer position in screen space. */
|
|
9502
|
-
previousScreenPoint: new Vec(),
|
|
9503
|
-
/** The most recent pointer position in the current page space. */
|
|
9504
|
-
currentPagePoint: new Vec(),
|
|
9505
|
-
/** The most recent pointer position in screen space. */
|
|
9506
|
-
currentScreenPoint: new Vec(),
|
|
9507
|
-
/** A set containing the currently pressed keys. */
|
|
9508
|
-
keys: new Set<string>(),
|
|
9509
|
-
/** A set containing the currently pressed buttons. */
|
|
9510
|
-
buttons: new Set<number>(),
|
|
9511
|
-
/** Whether the input is from a pe. */
|
|
9512
|
-
isPen: false,
|
|
9513
|
-
/** Whether the shift key is currently pressed. */
|
|
9514
|
-
shiftKey: false,
|
|
9515
|
-
/** Whether the meta key is currently pressed. */
|
|
9516
|
-
metaKey: false,
|
|
9517
|
-
/** Whether the control or command key is currently pressed. */
|
|
9518
|
-
ctrlKey: false,
|
|
9519
|
-
/** Whether the alt or option key is currently pressed. */
|
|
9520
|
-
altKey: false,
|
|
9521
|
-
/** Whether the user is dragging. */
|
|
9522
|
-
isDragging: false,
|
|
9523
|
-
/** Whether the user is pointing. */
|
|
9524
|
-
isPointing: false,
|
|
9525
|
-
/** Whether the user is pinching. */
|
|
9526
|
-
isPinching: false,
|
|
9527
|
-
/** Whether the user is editing. */
|
|
9528
|
-
isEditing: false,
|
|
9529
|
-
/** Whether the user is panning. */
|
|
9530
|
-
isPanning: false,
|
|
9531
|
-
/** Whether the user is spacebar panning. */
|
|
9532
|
-
isSpacebarPanning: false,
|
|
9533
|
-
/** Velocity of mouse pointer, in pixels per millisecond */
|
|
9534
|
-
pointerVelocity: new Vec(),
|
|
9535
|
-
}
|
|
9536
|
-
|
|
9537
|
-
/**
|
|
9538
|
-
* Update the input points from a pointer, pinch, or wheel event.
|
|
9539
|
-
*
|
|
9540
|
-
* @param info - The event info.
|
|
9541
|
-
*/
|
|
9542
|
-
private _updateInputsFromEvent(
|
|
9543
|
-
info: TLPointerEventInfo | TLPinchEventInfo | TLWheelEventInfo
|
|
9544
|
-
): void {
|
|
9545
|
-
const {
|
|
9546
|
-
pointerVelocity,
|
|
9547
|
-
previousScreenPoint,
|
|
9548
|
-
previousPagePoint,
|
|
9549
|
-
currentScreenPoint,
|
|
9550
|
-
currentPagePoint,
|
|
9551
|
-
originScreenPoint,
|
|
9552
|
-
originPagePoint,
|
|
9553
|
-
} = this.inputs
|
|
9554
|
-
|
|
9555
|
-
const { screenBounds } = this.store.unsafeGetWithoutCapture(TLINSTANCE_ID)!
|
|
9556
|
-
const { x: cx, y: cy, z: cz } = unsafe__withoutCapture(() => this.getCamera())
|
|
9557
|
-
|
|
9558
|
-
const sx = info.point.x - screenBounds.x
|
|
9559
|
-
const sy = info.point.y - screenBounds.y
|
|
9560
|
-
const sz = info.point.z ?? 0.5
|
|
9561
|
-
|
|
9562
|
-
previousScreenPoint.setTo(currentScreenPoint)
|
|
9563
|
-
previousPagePoint.setTo(currentPagePoint)
|
|
9564
|
-
|
|
9565
|
-
// The "screen bounds" is relative to the user's actual screen.
|
|
9566
|
-
// The "screen point" is relative to the "screen bounds";
|
|
9567
|
-
// it will be 0,0 when its actual screen position is equal
|
|
9568
|
-
// to screenBounds.point. This is confusing!
|
|
9569
|
-
currentScreenPoint.set(sx, sy)
|
|
9570
|
-
const nx = sx / cz - cx
|
|
9571
|
-
const ny = sy / cz - cy
|
|
9572
|
-
if (isFinite(nx) && isFinite(ny)) {
|
|
9573
|
-
currentPagePoint.set(nx, ny, sz)
|
|
9574
|
-
}
|
|
9575
|
-
|
|
9576
|
-
this.inputs.isPen = info.type === 'pointer' && info.isPen
|
|
9577
|
-
|
|
9578
|
-
// Reset velocity on pointer down, or when a pinch starts or ends
|
|
9579
|
-
if (info.name === 'pointer_down' || this.inputs.isPinching) {
|
|
9580
|
-
pointerVelocity.set(0, 0)
|
|
9581
|
-
originScreenPoint.setTo(currentScreenPoint)
|
|
9582
|
-
originPagePoint.setTo(currentPagePoint)
|
|
9583
|
-
}
|
|
9584
|
-
|
|
9585
|
-
// todo: We only have to do this if there are multiple users in the document
|
|
9586
|
-
this.run(
|
|
9587
|
-
() => {
|
|
9588
|
-
this.store.put([
|
|
9589
|
-
{
|
|
9590
|
-
id: TLPOINTER_ID,
|
|
9591
|
-
typeName: 'pointer',
|
|
9592
|
-
x: currentPagePoint.x,
|
|
9593
|
-
y: currentPagePoint.y,
|
|
9594
|
-
lastActivityTimestamp:
|
|
9595
|
-
// If our pointer moved only because we're following some other user, then don't
|
|
9596
|
-
// update our last activity timestamp; otherwise, update it to the current timestamp.
|
|
9597
|
-
info.type === 'pointer' && info.pointerId === INTERNAL_POINTER_IDS.CAMERA_MOVE
|
|
9598
|
-
? (this.store.unsafeGetWithoutCapture(TLPOINTER_ID)?.lastActivityTimestamp ??
|
|
9599
|
-
this._tickManager.now)
|
|
9600
|
-
: this._tickManager.now,
|
|
9601
|
-
meta: {},
|
|
9602
|
-
},
|
|
9603
|
-
])
|
|
9604
|
-
},
|
|
9605
|
-
{ history: 'ignore' }
|
|
9606
|
-
)
|
|
9607
|
-
}
|
|
9608
|
-
|
|
9609
9718
|
/**
|
|
9610
9719
|
* Dispatch a cancel event.
|
|
9611
9720
|
*
|
|
@@ -9675,19 +9784,22 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9675
9784
|
// weird but true: what `inputs` calls screen-space is actually viewport space. so
|
|
9676
9785
|
// we need to convert back into true screen space first. we should fix this...
|
|
9677
9786
|
Vec.Add(
|
|
9678
|
-
this.inputs.
|
|
9787
|
+
this.inputs.getCurrentScreenPoint(),
|
|
9679
9788
|
this.store.unsafeGetWithoutCapture(TLINSTANCE_ID)!.screenBounds
|
|
9680
9789
|
),
|
|
9681
9790
|
pointerId: options?.pointerId ?? 0,
|
|
9682
9791
|
button: options?.button ?? 0,
|
|
9683
|
-
isPen: options?.isPen ?? this.inputs.
|
|
9684
|
-
shiftKey: options?.shiftKey ?? this.inputs.
|
|
9685
|
-
altKey: options?.altKey ?? this.inputs.
|
|
9686
|
-
ctrlKey: options?.ctrlKey ?? this.inputs.
|
|
9687
|
-
metaKey: options?.metaKey ?? this.inputs.
|
|
9688
|
-
accelKey:
|
|
9792
|
+
isPen: options?.isPen ?? this.inputs.getIsPen(),
|
|
9793
|
+
shiftKey: options?.shiftKey ?? this.inputs.getShiftKey(),
|
|
9794
|
+
altKey: options?.altKey ?? this.inputs.getAltKey(),
|
|
9795
|
+
ctrlKey: options?.ctrlKey ?? this.inputs.getCtrlKey(),
|
|
9796
|
+
metaKey: options?.metaKey ?? this.inputs.getMetaKey(),
|
|
9797
|
+
accelKey: false,
|
|
9689
9798
|
}
|
|
9690
9799
|
|
|
9800
|
+
// needs to be calculated second
|
|
9801
|
+
event.accelKey = options?.accelKey ?? this.inputs.getAccelKey()
|
|
9802
|
+
|
|
9691
9803
|
if (options?.immediate) {
|
|
9692
9804
|
this._flushEventForTick(event)
|
|
9693
9805
|
} else {
|
|
@@ -10060,16 +10172,16 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10060
10172
|
/** @internal */
|
|
10061
10173
|
@bind
|
|
10062
10174
|
_setShiftKeyTimeout() {
|
|
10063
|
-
this.inputs.
|
|
10175
|
+
this.inputs.setShiftKey(false)
|
|
10064
10176
|
this.dispatch({
|
|
10065
10177
|
type: 'keyboard',
|
|
10066
10178
|
name: 'key_up',
|
|
10067
10179
|
key: 'Shift',
|
|
10068
|
-
shiftKey: this.inputs.
|
|
10069
|
-
ctrlKey: this.inputs.
|
|
10070
|
-
altKey: this.inputs.
|
|
10071
|
-
metaKey: this.inputs.
|
|
10072
|
-
accelKey:
|
|
10180
|
+
shiftKey: this.inputs.getShiftKey(),
|
|
10181
|
+
ctrlKey: this.inputs.getCtrlKey(),
|
|
10182
|
+
altKey: this.inputs.getAltKey(),
|
|
10183
|
+
metaKey: this.inputs.getMetaKey(),
|
|
10184
|
+
accelKey: this.inputs.getAccelKey(),
|
|
10073
10185
|
code: 'ShiftLeft',
|
|
10074
10186
|
})
|
|
10075
10187
|
}
|
|
@@ -10080,16 +10192,16 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10080
10192
|
/** @internal */
|
|
10081
10193
|
@bind
|
|
10082
10194
|
_setAltKeyTimeout() {
|
|
10083
|
-
this.inputs.
|
|
10195
|
+
this.inputs.setAltKey(false)
|
|
10084
10196
|
this.dispatch({
|
|
10085
10197
|
type: 'keyboard',
|
|
10086
10198
|
name: 'key_up',
|
|
10087
10199
|
key: 'Alt',
|
|
10088
|
-
shiftKey: this.inputs.
|
|
10089
|
-
ctrlKey: this.inputs.
|
|
10090
|
-
altKey: this.inputs.
|
|
10091
|
-
metaKey: this.inputs.
|
|
10092
|
-
accelKey:
|
|
10200
|
+
shiftKey: this.inputs.getShiftKey(),
|
|
10201
|
+
ctrlKey: this.inputs.getCtrlKey(),
|
|
10202
|
+
altKey: this.inputs.getAltKey(),
|
|
10203
|
+
metaKey: this.inputs.getMetaKey(),
|
|
10204
|
+
accelKey: this.inputs.getAccelKey(),
|
|
10093
10205
|
code: 'AltLeft',
|
|
10094
10206
|
})
|
|
10095
10207
|
}
|
|
@@ -10100,16 +10212,16 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10100
10212
|
/** @internal */
|
|
10101
10213
|
@bind
|
|
10102
10214
|
_setCtrlKeyTimeout() {
|
|
10103
|
-
this.inputs.
|
|
10215
|
+
this.inputs.setCtrlKey(false)
|
|
10104
10216
|
this.dispatch({
|
|
10105
10217
|
type: 'keyboard',
|
|
10106
10218
|
name: 'key_up',
|
|
10107
10219
|
key: 'Ctrl',
|
|
10108
|
-
shiftKey: this.inputs.
|
|
10109
|
-
ctrlKey: this.inputs.
|
|
10110
|
-
altKey: this.inputs.
|
|
10111
|
-
metaKey: this.inputs.
|
|
10112
|
-
accelKey:
|
|
10220
|
+
shiftKey: this.inputs.getShiftKey(),
|
|
10221
|
+
ctrlKey: this.inputs.getCtrlKey(),
|
|
10222
|
+
altKey: this.inputs.getAltKey(),
|
|
10223
|
+
metaKey: this.inputs.getMetaKey(),
|
|
10224
|
+
accelKey: this.inputs.getAccelKey(),
|
|
10113
10225
|
code: 'ControlLeft',
|
|
10114
10226
|
})
|
|
10115
10227
|
}
|
|
@@ -10120,16 +10232,16 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10120
10232
|
/** @internal */
|
|
10121
10233
|
@bind
|
|
10122
10234
|
_setMetaKeyTimeout() {
|
|
10123
|
-
this.inputs.
|
|
10235
|
+
this.inputs.setMetaKey(false)
|
|
10124
10236
|
this.dispatch({
|
|
10125
10237
|
type: 'keyboard',
|
|
10126
10238
|
name: 'key_up',
|
|
10127
10239
|
key: 'Meta',
|
|
10128
|
-
shiftKey: this.inputs.
|
|
10129
|
-
ctrlKey: this.inputs.
|
|
10130
|
-
altKey: this.inputs.
|
|
10131
|
-
metaKey: this.inputs.
|
|
10132
|
-
accelKey:
|
|
10240
|
+
shiftKey: this.inputs.getShiftKey(),
|
|
10241
|
+
ctrlKey: this.inputs.getCtrlKey(),
|
|
10242
|
+
altKey: this.inputs.getAltKey(),
|
|
10243
|
+
metaKey: this.inputs.getMetaKey(),
|
|
10244
|
+
accelKey: this.inputs.getAccelKey(),
|
|
10133
10245
|
code: 'MetaLeft',
|
|
10134
10246
|
})
|
|
10135
10247
|
}
|
|
@@ -10137,9 +10249,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10137
10249
|
/** @internal */
|
|
10138
10250
|
private _restoreToolId = 'select'
|
|
10139
10251
|
|
|
10140
|
-
/** @internal */
|
|
10141
|
-
private _pinchStart = 1
|
|
10142
|
-
|
|
10143
10252
|
/** @internal */
|
|
10144
10253
|
private _didPinch = false
|
|
10145
10254
|
|
|
@@ -10246,56 +10355,54 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10246
10355
|
if (info.type === 'misc') {
|
|
10247
10356
|
// stop panning if the interaction is cancelled or completed
|
|
10248
10357
|
if (info.name === 'cancel' || info.name === 'complete') {
|
|
10249
|
-
this.inputs.
|
|
10358
|
+
this.inputs.setIsDragging(false)
|
|
10250
10359
|
|
|
10251
|
-
if (this.inputs.
|
|
10252
|
-
this.inputs.
|
|
10253
|
-
this.inputs.
|
|
10360
|
+
if (this.inputs.getIsPanning()) {
|
|
10361
|
+
this.inputs.setIsPanning(false)
|
|
10362
|
+
this.inputs.setIsSpacebarPanning(false)
|
|
10254
10363
|
this.setCursor({ type: this._prevCursor, rotation: 0 })
|
|
10255
10364
|
}
|
|
10256
10365
|
}
|
|
10257
10366
|
|
|
10258
|
-
this.emit('event', info)
|
|
10259
10367
|
this.root.handleEvent(info)
|
|
10368
|
+
this.emit('event', info)
|
|
10260
10369
|
return
|
|
10261
10370
|
}
|
|
10262
10371
|
|
|
10263
10372
|
if (info.shiftKey) {
|
|
10264
10373
|
clearTimeout(this._shiftKeyTimeout)
|
|
10265
10374
|
this._shiftKeyTimeout = -1
|
|
10266
|
-
inputs.
|
|
10267
|
-
} else if (!info.shiftKey && inputs.
|
|
10375
|
+
inputs.setShiftKey(true)
|
|
10376
|
+
} else if (!info.shiftKey && inputs.getShiftKey() && this._shiftKeyTimeout === -1) {
|
|
10268
10377
|
this._shiftKeyTimeout = this.timers.setTimeout(this._setShiftKeyTimeout, 150)
|
|
10269
10378
|
}
|
|
10270
10379
|
|
|
10271
10380
|
if (info.altKey) {
|
|
10272
10381
|
clearTimeout(this._altKeyTimeout)
|
|
10273
10382
|
this._altKeyTimeout = -1
|
|
10274
|
-
inputs.
|
|
10275
|
-
} else if (!info.altKey && inputs.
|
|
10383
|
+
inputs.setAltKey(true)
|
|
10384
|
+
} else if (!info.altKey && inputs.getAltKey() && this._altKeyTimeout === -1) {
|
|
10276
10385
|
this._altKeyTimeout = this.timers.setTimeout(this._setAltKeyTimeout, 150)
|
|
10277
10386
|
}
|
|
10278
10387
|
|
|
10279
10388
|
if (info.ctrlKey) {
|
|
10280
10389
|
clearTimeout(this._ctrlKeyTimeout)
|
|
10281
10390
|
this._ctrlKeyTimeout = -1
|
|
10282
|
-
inputs.
|
|
10283
|
-
} else if (!info.ctrlKey && inputs.
|
|
10391
|
+
inputs.setCtrlKey(true)
|
|
10392
|
+
} else if (!info.ctrlKey && inputs.getCtrlKey() && this._ctrlKeyTimeout === -1) {
|
|
10284
10393
|
this._ctrlKeyTimeout = this.timers.setTimeout(this._setCtrlKeyTimeout, 150)
|
|
10285
10394
|
}
|
|
10286
10395
|
|
|
10287
10396
|
if (info.metaKey) {
|
|
10288
10397
|
clearTimeout(this._metaKeyTimeout)
|
|
10289
10398
|
this._metaKeyTimeout = -1
|
|
10290
|
-
inputs.
|
|
10291
|
-
} else if (!info.metaKey && inputs.
|
|
10399
|
+
inputs.setMetaKey(true)
|
|
10400
|
+
} else if (!info.metaKey && inputs.getMetaKey() && this._metaKeyTimeout === -1) {
|
|
10292
10401
|
this._metaKeyTimeout = this.timers.setTimeout(this._setMetaKeyTimeout, 150)
|
|
10293
10402
|
}
|
|
10294
10403
|
|
|
10295
|
-
|
|
10296
|
-
|
|
10297
|
-
if (!inputs.isPointing) {
|
|
10298
|
-
inputs.isDragging = false
|
|
10404
|
+
if (!inputs.getIsPointing()) {
|
|
10405
|
+
inputs.setIsDragging(false)
|
|
10299
10406
|
}
|
|
10300
10407
|
|
|
10301
10408
|
const instanceState = this.store.unsafeGetWithoutCapture(TLINSTANCE_ID)!
|
|
@@ -10306,29 +10413,29 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10306
10413
|
case 'pinch': {
|
|
10307
10414
|
if (cameraOptions.isLocked) return
|
|
10308
10415
|
clearTimeout(this._longPressTimeout)
|
|
10309
|
-
this.
|
|
10416
|
+
this.inputs.updateFromEvent(info)
|
|
10310
10417
|
|
|
10311
10418
|
switch (info.name) {
|
|
10312
10419
|
case 'pinch_start': {
|
|
10313
|
-
if (inputs.
|
|
10420
|
+
if (inputs.getIsPinching()) return
|
|
10314
10421
|
|
|
10315
|
-
if (!inputs.
|
|
10316
|
-
this._pinchStart = this.getCamera().z
|
|
10422
|
+
if (!inputs.getIsEditing()) {
|
|
10317
10423
|
if (!this._selectedShapeIdsAtPointerDown.length) {
|
|
10318
10424
|
this._selectedShapeIdsAtPointerDown = [...pageState.selectedShapeIds]
|
|
10319
10425
|
}
|
|
10320
10426
|
|
|
10321
10427
|
this._didPinch = true
|
|
10322
10428
|
|
|
10323
|
-
inputs.
|
|
10429
|
+
inputs.setIsPinching(true)
|
|
10324
10430
|
|
|
10325
10431
|
this.interrupt()
|
|
10326
10432
|
}
|
|
10327
10433
|
|
|
10434
|
+
this.emit('event', info)
|
|
10328
10435
|
return // Stop here!
|
|
10329
10436
|
}
|
|
10330
10437
|
case 'pinch': {
|
|
10331
|
-
if (!inputs.
|
|
10438
|
+
if (!inputs.getIsPinching()) return
|
|
10332
10439
|
|
|
10333
10440
|
const {
|
|
10334
10441
|
point: { z = 1 },
|
|
@@ -10359,13 +10466,14 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10359
10466
|
{ immediate: true }
|
|
10360
10467
|
)
|
|
10361
10468
|
|
|
10469
|
+
this.emit('event', info)
|
|
10362
10470
|
return // Stop here!
|
|
10363
10471
|
}
|
|
10364
10472
|
case 'pinch_end': {
|
|
10365
|
-
if (!inputs.
|
|
10473
|
+
if (!inputs.getIsPinching()) return this
|
|
10366
10474
|
|
|
10367
10475
|
// Stop pinching
|
|
10368
|
-
inputs.
|
|
10476
|
+
inputs.setIsPinching(false)
|
|
10369
10477
|
|
|
10370
10478
|
// Stash and clear the shapes that were selected when the pinch started
|
|
10371
10479
|
const { _selectedShapeIdsAtPointerDown: shapesToReselect } = this
|
|
@@ -10385,6 +10493,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10385
10493
|
}
|
|
10386
10494
|
}
|
|
10387
10495
|
|
|
10496
|
+
this.emit('event', info)
|
|
10388
10497
|
return // Stop here!
|
|
10389
10498
|
}
|
|
10390
10499
|
}
|
|
@@ -10392,7 +10501,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10392
10501
|
case 'wheel': {
|
|
10393
10502
|
if (cameraOptions.isLocked) return
|
|
10394
10503
|
|
|
10395
|
-
this.
|
|
10504
|
+
this.inputs.updateFromEvent(info)
|
|
10396
10505
|
|
|
10397
10506
|
const { panSpeed, zoomSpeed } = cameraOptions
|
|
10398
10507
|
let wheelBehavior = cameraOptions.wheelBehavior
|
|
@@ -10423,7 +10532,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10423
10532
|
switch (behavior) {
|
|
10424
10533
|
case 'zoom': {
|
|
10425
10534
|
// Zoom in on current screen point using the wheel delta
|
|
10426
|
-
const { x, y } = this.inputs.
|
|
10535
|
+
const { x, y } = this.inputs.getCurrentScreenPoint()
|
|
10427
10536
|
let delta = dz
|
|
10428
10537
|
|
|
10429
10538
|
// If we're forcing zoom, then we need to do the wheel normalization math here
|
|
@@ -10440,6 +10549,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10440
10549
|
immediate: true,
|
|
10441
10550
|
})
|
|
10442
10551
|
this.maybeTrackPerformance('Zooming')
|
|
10552
|
+
this.root.handleEvent(info)
|
|
10553
|
+
this.emit('event', info)
|
|
10443
10554
|
return
|
|
10444
10555
|
}
|
|
10445
10556
|
case 'pan': {
|
|
@@ -10448,6 +10559,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10448
10559
|
immediate: true,
|
|
10449
10560
|
})
|
|
10450
10561
|
this.maybeTrackPerformance('Panning')
|
|
10562
|
+
this.root.handleEvent(info)
|
|
10563
|
+
this.emit('event', info)
|
|
10451
10564
|
return
|
|
10452
10565
|
}
|
|
10453
10566
|
}
|
|
@@ -10456,9 +10569,9 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10456
10569
|
}
|
|
10457
10570
|
case 'pointer': {
|
|
10458
10571
|
// Ignore pointer events while we're pinching
|
|
10459
|
-
if (inputs.
|
|
10572
|
+
if (inputs.getIsPinching()) return
|
|
10460
10573
|
|
|
10461
|
-
this.
|
|
10574
|
+
this.inputs.updateFromEvent(info)
|
|
10462
10575
|
const { isPen } = info
|
|
10463
10576
|
const { isPenMode } = instanceState
|
|
10464
10577
|
|
|
@@ -10467,7 +10580,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10467
10580
|
// If we're in pen mode and the input is not a pen type, then stop here
|
|
10468
10581
|
if (isPenMode && !isPen) return
|
|
10469
10582
|
|
|
10470
|
-
if (!this.inputs.
|
|
10583
|
+
if (!this.inputs.getIsPanning()) {
|
|
10471
10584
|
// Start a long press timeout
|
|
10472
10585
|
this._longPressTimeout = this.timers.setTimeout(() => {
|
|
10473
10586
|
const vsb = this.getViewportScreenBounds()
|
|
@@ -10477,7 +10590,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10477
10590
|
// viewport bounds, and will be again when this event is handled...
|
|
10478
10591
|
// so we need to counter-adjust from the stored value so that the
|
|
10479
10592
|
// new value is set correctly.
|
|
10480
|
-
point: this.inputs.
|
|
10593
|
+
point: this.inputs.getOriginScreenPoint().clone().addXY(vsb.x, vsb.y),
|
|
10481
10594
|
name: 'long_press',
|
|
10482
10595
|
})
|
|
10483
10596
|
}, this.options.longPressDurationMs)
|
|
@@ -10494,8 +10607,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10494
10607
|
inputs.buttons.add(info.button)
|
|
10495
10608
|
|
|
10496
10609
|
// Start pointing and stop dragging
|
|
10497
|
-
inputs.
|
|
10498
|
-
inputs.
|
|
10610
|
+
inputs.setIsPointing(true)
|
|
10611
|
+
inputs.setIsDragging(false)
|
|
10499
10612
|
|
|
10500
10613
|
// If pen mode is off but we're not already in pen mode, turn that on
|
|
10501
10614
|
if (!isPenMode && isPen) this.updateInstanceState({ isPenMode: true })
|
|
@@ -10507,16 +10620,16 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10507
10620
|
this.setCurrentTool('eraser')
|
|
10508
10621
|
} else if (info.button === MIDDLE_MOUSE_BUTTON) {
|
|
10509
10622
|
// Middle mouse pan activates panning unless we're already panning (with spacebar)
|
|
10510
|
-
if (!this.inputs.
|
|
10623
|
+
if (!this.inputs.getIsPanning()) {
|
|
10511
10624
|
this._prevCursor = this.getInstanceState().cursor.type
|
|
10512
10625
|
}
|
|
10513
|
-
this.inputs.
|
|
10626
|
+
this.inputs.setIsPanning(true)
|
|
10514
10627
|
clearTimeout(this._longPressTimeout)
|
|
10515
10628
|
}
|
|
10516
10629
|
|
|
10517
10630
|
// We might be panning because we did a middle mouse click, or because we're holding spacebar and started a regular click
|
|
10518
10631
|
// Also stop here, we don't want the state chart to receive the event
|
|
10519
|
-
if (this.inputs.
|
|
10632
|
+
if (this.inputs.getIsPanning()) {
|
|
10520
10633
|
this.stopCameraAnimation()
|
|
10521
10634
|
this.setCursor({ type: 'grabbing', rotation: 0 })
|
|
10522
10635
|
return this
|
|
@@ -10531,9 +10644,10 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10531
10644
|
const { x: cx, y: cy, z: cz } = unsafe__withoutCapture(() => this.getCamera())
|
|
10532
10645
|
|
|
10533
10646
|
// If we've started panning, then clear any long press timeout
|
|
10534
|
-
if (this.inputs.
|
|
10647
|
+
if (this.inputs.getIsPanning() && this.inputs.getIsPointing()) {
|
|
10535
10648
|
// Handle spacebar / middle mouse button panning
|
|
10536
|
-
const
|
|
10649
|
+
const currentScreenPoint = this.inputs.getCurrentScreenPoint()
|
|
10650
|
+
const previousScreenPoint = this.inputs.getPreviousScreenPoint()
|
|
10537
10651
|
const offset = Vec.Sub(currentScreenPoint, previousScreenPoint)
|
|
10538
10652
|
this.setCamera(new Vec(cx + offset.x / cz, cy + offset.y / cz, cz), {
|
|
10539
10653
|
immediate: true,
|
|
@@ -10543,24 +10657,25 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10543
10657
|
}
|
|
10544
10658
|
|
|
10545
10659
|
if (
|
|
10546
|
-
inputs.
|
|
10547
|
-
!inputs.
|
|
10548
|
-
Vec.Dist2(
|
|
10660
|
+
inputs.getIsPointing() &&
|
|
10661
|
+
!inputs.getIsDragging() &&
|
|
10662
|
+
Vec.Dist2(inputs.getOriginPagePoint(), inputs.getCurrentPagePoint()) *
|
|
10663
|
+
this.getZoomLevel() >
|
|
10549
10664
|
(instanceState.isCoarsePointer
|
|
10550
10665
|
? this.options.coarseDragDistanceSquared
|
|
10551
10666
|
: this.options.dragDistanceSquared) /
|
|
10552
10667
|
cz
|
|
10553
10668
|
) {
|
|
10554
10669
|
// Start dragging
|
|
10555
|
-
inputs.
|
|
10670
|
+
inputs.setIsDragging(true)
|
|
10556
10671
|
clearTimeout(this._longPressTimeout)
|
|
10557
10672
|
}
|
|
10558
10673
|
break
|
|
10559
10674
|
}
|
|
10560
10675
|
case 'pointer_up': {
|
|
10561
10676
|
// Stop dragging / pointing
|
|
10562
|
-
inputs.
|
|
10563
|
-
inputs.
|
|
10677
|
+
inputs.setIsDragging(false)
|
|
10678
|
+
inputs.setIsPointing(false)
|
|
10564
10679
|
clearTimeout(this._longPressTimeout)
|
|
10565
10680
|
|
|
10566
10681
|
// Remove the button from the buttons set
|
|
@@ -10577,12 +10692,12 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10577
10692
|
info.button = 0
|
|
10578
10693
|
}
|
|
10579
10694
|
|
|
10580
|
-
if (inputs.
|
|
10695
|
+
if (inputs.getIsPanning()) {
|
|
10581
10696
|
if (!inputs.keys.has('Space')) {
|
|
10582
|
-
inputs.
|
|
10583
|
-
inputs.
|
|
10697
|
+
inputs.setIsPanning(false)
|
|
10698
|
+
inputs.setIsSpacebarPanning(false)
|
|
10584
10699
|
}
|
|
10585
|
-
const slideDirection = this.inputs.
|
|
10700
|
+
const slideDirection = this.inputs.getPointerVelocity()
|
|
10586
10701
|
const slideSpeed = Math.min(2, slideDirection.len())
|
|
10587
10702
|
|
|
10588
10703
|
switch (info.button) {
|
|
@@ -10626,43 +10741,48 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10626
10741
|
// Add the key from the keys set
|
|
10627
10742
|
inputs.keys.add(info.code)
|
|
10628
10743
|
|
|
10629
|
-
|
|
10630
|
-
|
|
10631
|
-
if (!
|
|
10632
|
-
this.
|
|
10633
|
-
|
|
10744
|
+
if (this.options.spacebarPanning) {
|
|
10745
|
+
// If the space key is pressed (but meta / control isn't!) activate panning
|
|
10746
|
+
if (info.code === 'Space' && !info.ctrlKey) {
|
|
10747
|
+
if (!this.inputs.getIsPanning()) {
|
|
10748
|
+
this._prevCursor = instanceState.cursor.type
|
|
10749
|
+
}
|
|
10634
10750
|
|
|
10635
|
-
|
|
10636
|
-
|
|
10637
|
-
|
|
10638
|
-
|
|
10639
|
-
|
|
10751
|
+
this.inputs.setIsPanning(true)
|
|
10752
|
+
this.inputs.setIsSpacebarPanning(true)
|
|
10753
|
+
clearTimeout(this._longPressTimeout)
|
|
10754
|
+
this.setCursor({
|
|
10755
|
+
type: this.inputs.getIsPointing() ? 'grabbing' : 'grab',
|
|
10756
|
+
rotation: 0,
|
|
10757
|
+
})
|
|
10758
|
+
}
|
|
10640
10759
|
|
|
10641
|
-
|
|
10642
|
-
|
|
10643
|
-
|
|
10644
|
-
|
|
10645
|
-
|
|
10646
|
-
|
|
10647
|
-
|
|
10648
|
-
|
|
10649
|
-
|
|
10650
|
-
|
|
10651
|
-
|
|
10652
|
-
|
|
10653
|
-
|
|
10654
|
-
|
|
10655
|
-
|
|
10656
|
-
|
|
10657
|
-
|
|
10658
|
-
|
|
10760
|
+
if (this.inputs.getIsSpacebarPanning()) {
|
|
10761
|
+
let offset: Vec | undefined
|
|
10762
|
+
switch (info.code) {
|
|
10763
|
+
case 'ArrowUp': {
|
|
10764
|
+
offset = new Vec(0, -1)
|
|
10765
|
+
break
|
|
10766
|
+
}
|
|
10767
|
+
case 'ArrowRight': {
|
|
10768
|
+
offset = new Vec(1, 0)
|
|
10769
|
+
break
|
|
10770
|
+
}
|
|
10771
|
+
case 'ArrowDown': {
|
|
10772
|
+
offset = new Vec(0, 1)
|
|
10773
|
+
break
|
|
10774
|
+
}
|
|
10775
|
+
case 'ArrowLeft': {
|
|
10776
|
+
offset = new Vec(-1, 0)
|
|
10777
|
+
break
|
|
10778
|
+
}
|
|
10659
10779
|
}
|
|
10660
|
-
}
|
|
10661
10780
|
|
|
10662
|
-
|
|
10663
|
-
|
|
10664
|
-
|
|
10665
|
-
|
|
10781
|
+
if (offset) {
|
|
10782
|
+
const bounds = this.getViewportPageBounds()
|
|
10783
|
+
const next = bounds.clone().translate(offset.mulV({ x: bounds.w, y: bounds.h }))
|
|
10784
|
+
this._animateToViewport(next, { animation: { duration: 320 } })
|
|
10785
|
+
}
|
|
10666
10786
|
}
|
|
10667
10787
|
}
|
|
10668
10788
|
|
|
@@ -10672,15 +10792,17 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10672
10792
|
// Remove the key from the keys set
|
|
10673
10793
|
inputs.keys.delete(info.code)
|
|
10674
10794
|
|
|
10675
|
-
|
|
10676
|
-
|
|
10677
|
-
if (
|
|
10678
|
-
|
|
10679
|
-
|
|
10680
|
-
|
|
10681
|
-
|
|
10682
|
-
|
|
10683
|
-
|
|
10795
|
+
if (this.options.spacebarPanning) {
|
|
10796
|
+
// If we've lifted the space key,
|
|
10797
|
+
if (info.code === 'Space') {
|
|
10798
|
+
if (this.inputs.buttons.has(MIDDLE_MOUSE_BUTTON)) {
|
|
10799
|
+
// If we're still middle dragging, continue panning
|
|
10800
|
+
} else {
|
|
10801
|
+
// otherwise, stop panning
|
|
10802
|
+
this.inputs.setIsPanning(false)
|
|
10803
|
+
this.inputs.setIsSpacebarPanning(false)
|
|
10804
|
+
this.setCursor({ type: this._prevCursor, rotation: 0 })
|
|
10805
|
+
}
|
|
10684
10806
|
}
|
|
10685
10807
|
}
|
|
10686
10808
|
break
|