@tldraw/editor 4.3.0-canary.bf87ebaf143a → 4.3.0-canary.c08047039e53
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 +392 -118
- package/dist-cjs/index.js +7 -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 +1 -2
- 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 +301 -278
- 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/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/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/options.js +4 -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 +392 -118
- package/dist-esm/index.mjs +7 -1
- 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 +1 -2
- 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 +302 -281
- 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/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/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/options.mjs +4 -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 +6 -8
- package/package.json +21 -17
- package/src/index.ts +4 -0
- package/src/lib/components/ErrorBoundary.tsx +1 -1
- package/src/lib/components/GeometryDebuggingView.tsx +1 -19
- package/src/lib/components/default-components/DefaultCanvas.tsx +1 -5
- 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 +374 -321
- 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/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/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/options.ts +19 -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
|
|
887
|
+
|
|
888
|
+
/**
|
|
889
|
+
* A manager for the editor's input state.
|
|
890
|
+
*
|
|
891
|
+
* @public
|
|
892
|
+
*/
|
|
893
|
+
readonly inputs: InputsManager
|
|
885
894
|
|
|
886
895
|
/**
|
|
887
|
-
* A manager for the
|
|
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' }
|
|
@@ -3042,7 +3115,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3042
3115
|
|
|
3043
3116
|
// Dispatch a new pointer move because the pointer's page will have changed
|
|
3044
3117
|
// (its screen position will compute to a new page position given the new camera position)
|
|
3045
|
-
const
|
|
3118
|
+
const currentScreenPoint = this.inputs.getCurrentScreenPoint()
|
|
3119
|
+
const currentPagePoint = this.inputs.getCurrentPagePoint()
|
|
3046
3120
|
|
|
3047
3121
|
// compare the next page point (derived from the current camera) to the current page point
|
|
3048
3122
|
if (
|
|
@@ -3206,7 +3280,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3206
3280
|
* ```ts
|
|
3207
3281
|
* editor.zoomIn()
|
|
3208
3282
|
* editor.zoomIn(editor.getViewportScreenCenter(), { animation: { duration: 200 } })
|
|
3209
|
-
* editor.zoomIn(editor.inputs.
|
|
3283
|
+
* editor.zoomIn(editor.inputs.getCurrentScreenPoint(), { animation: { duration: 200 } })
|
|
3210
3284
|
* ```
|
|
3211
3285
|
*
|
|
3212
3286
|
* @param point - The screen point to zoom in on. Defaults to the screen center
|
|
@@ -3251,7 +3325,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3251
3325
|
* ```ts
|
|
3252
3326
|
* editor.zoomOut()
|
|
3253
3327
|
* editor.zoomOut(editor.getViewportScreenCenter(), { animation: { duration: 120 } })
|
|
3254
|
-
* editor.zoomOut(editor.inputs.
|
|
3328
|
+
* editor.zoomOut(editor.inputs.getCurrentScreenPoint(), { animation: { duration: 120 } })
|
|
3255
3329
|
* ```
|
|
3256
3330
|
*
|
|
3257
3331
|
* @param point - The point to zoom out on. Defaults to the viewport screen center.
|
|
@@ -3308,10 +3382,17 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3308
3382
|
|
|
3309
3383
|
const selectionPageBounds = this.getSelectionPageBounds()
|
|
3310
3384
|
if (selectionPageBounds) {
|
|
3311
|
-
this.
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
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
|
+
}
|
|
3315
3396
|
}
|
|
3316
3397
|
return this
|
|
3317
3398
|
}
|
|
@@ -3368,7 +3449,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3368
3449
|
|
|
3369
3450
|
const viewportScreenBounds = this.getViewportScreenBounds()
|
|
3370
3451
|
|
|
3371
|
-
const inset =
|
|
3452
|
+
const inset =
|
|
3453
|
+
opts?.inset ?? Math.min(this.options.zoomToFitPadding, viewportScreenBounds.width * 0.28)
|
|
3372
3454
|
|
|
3373
3455
|
const baseZoom = this.getBaseZoom()
|
|
3374
3456
|
const zoomMin = cameraOptions.zoomSteps[0]
|
|
@@ -5059,6 +5141,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5059
5141
|
}
|
|
5060
5142
|
|
|
5061
5143
|
private _notVisibleShapes = notVisibleShapes(this)
|
|
5144
|
+
private _culledShapesCache: Set<TLShapeId> | null = null
|
|
5062
5145
|
|
|
5063
5146
|
/**
|
|
5064
5147
|
* Get culled shapes (those that should not render), taking into account which shapes are selected or editing.
|
|
@@ -5070,16 +5153,41 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5070
5153
|
const notVisibleShapes = this.getNotVisibleShapes()
|
|
5071
5154
|
const selectedShapeIds = this.getSelectedShapeIds()
|
|
5072
5155
|
const editingId = this.getEditingShapeId()
|
|
5073
|
-
const
|
|
5156
|
+
const nextValue = new Set<TLShapeId>(notVisibleShapes)
|
|
5074
5157
|
// we don't cull the shape we are editing
|
|
5075
5158
|
if (editingId) {
|
|
5076
|
-
|
|
5159
|
+
nextValue.delete(editingId)
|
|
5077
5160
|
}
|
|
5078
5161
|
// we also don't cull selected shapes
|
|
5079
5162
|
selectedShapeIds.forEach((id) => {
|
|
5080
|
-
|
|
5163
|
+
nextValue.delete(id)
|
|
5081
5164
|
})
|
|
5082
|
-
|
|
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
|
|
5083
5191
|
}
|
|
5084
5192
|
|
|
5085
5193
|
/**
|
|
@@ -5146,11 +5254,18 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5146
5254
|
let inMarginClosestToEdgeDistance = Infinity
|
|
5147
5255
|
let inMarginClosestToEdgeHit: TLShape | null = null
|
|
5148
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
|
+
|
|
5149
5261
|
const shapesToCheck = (
|
|
5150
5262
|
opts.renderingOnly
|
|
5151
5263
|
? this.getCurrentPageRenderingShapesSorted()
|
|
5152
5264
|
: this.getCurrentPageShapesSorted()
|
|
5153
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
|
+
|
|
5154
5269
|
if (
|
|
5155
5270
|
(shape.isLocked && !hitLocked) ||
|
|
5156
5271
|
this.isShapeHidden(shape) ||
|
|
@@ -5336,11 +5451,41 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5336
5451
|
point: VecLike,
|
|
5337
5452
|
opts = {} as { margin?: number; hitInside?: boolean }
|
|
5338
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
|
|
5339
5459
|
return this.getCurrentPageShapesSorted()
|
|
5340
|
-
.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
|
+
})
|
|
5341
5465
|
.reverse()
|
|
5342
5466
|
}
|
|
5343
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
|
+
|
|
5344
5489
|
/**
|
|
5345
5490
|
* Test whether a point (in the current page space) will will a shape. This method takes into account masks,
|
|
5346
5491
|
* such as when a shape is the child of a frame and is partially clipped by the frame.
|
|
@@ -7698,8 +7843,14 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
7698
7843
|
// then if the shape is flipped in one axis only, we need to apply an extra rotation
|
|
7699
7844
|
// to make sure the shape is mirrored correctly
|
|
7700
7845
|
if (Math.sign(scale.x) * Math.sign(scale.y) < 0) {
|
|
7701
|
-
|
|
7702
|
-
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
|
|
7703
7854
|
this.updateShapes([{ id, type, rotation }])
|
|
7704
7855
|
}
|
|
7705
7856
|
|
|
@@ -7719,9 +7870,13 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
7719
7870
|
)
|
|
7720
7871
|
|
|
7721
7872
|
// now calculate how far away the shape is from where it needs to be
|
|
7722
|
-
const pageBounds = this.getShapePageBounds(id)!
|
|
7723
7873
|
const pageTransform = this.getShapePageTransform(id)!
|
|
7724
|
-
|
|
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)
|
|
7725
7880
|
const shapePageTransformOrigin = pageTransform.point()
|
|
7726
7881
|
if (!currentPageCenter || !shapePageTransformOrigin) return this
|
|
7727
7882
|
const pageDelta = Vec.Sub(postScaleShapePageCenter, currentPageCenter)
|
|
@@ -8169,7 +8324,12 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8169
8324
|
)
|
|
8170
8325
|
)
|
|
8171
8326
|
const sortedShapeIds = shapesToGroup.sort(sortByIndex).map((s) => s.id)
|
|
8172
|
-
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
|
+
}
|
|
8173
8333
|
|
|
8174
8334
|
const { x, y } = pageBounds.point
|
|
8175
8335
|
|
|
@@ -9555,126 +9715,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9555
9715
|
|
|
9556
9716
|
/* --------------------- Events --------------------- */
|
|
9557
9717
|
|
|
9558
|
-
/**
|
|
9559
|
-
* The app's current input state.
|
|
9560
|
-
*
|
|
9561
|
-
* @public
|
|
9562
|
-
*/
|
|
9563
|
-
inputs = {
|
|
9564
|
-
/** The most recent pointer down's position in the current page space. */
|
|
9565
|
-
originPagePoint: new Vec(),
|
|
9566
|
-
/** The most recent pointer down's position in screen space. */
|
|
9567
|
-
originScreenPoint: new Vec(),
|
|
9568
|
-
/** The previous pointer position in the current page space. */
|
|
9569
|
-
previousPagePoint: new Vec(),
|
|
9570
|
-
/** The previous pointer position in screen space. */
|
|
9571
|
-
previousScreenPoint: new Vec(),
|
|
9572
|
-
/** The most recent pointer position in the current page space. */
|
|
9573
|
-
currentPagePoint: new Vec(),
|
|
9574
|
-
/** The most recent pointer position in screen space. */
|
|
9575
|
-
currentScreenPoint: new Vec(),
|
|
9576
|
-
/** A set containing the currently pressed keys. */
|
|
9577
|
-
keys: new Set<string>(),
|
|
9578
|
-
/** A set containing the currently pressed buttons. */
|
|
9579
|
-
buttons: new Set<number>(),
|
|
9580
|
-
/** Whether the input is from a pe. */
|
|
9581
|
-
isPen: false,
|
|
9582
|
-
/** Whether the shift key is currently pressed. */
|
|
9583
|
-
shiftKey: false,
|
|
9584
|
-
/** Whether the meta key is currently pressed. */
|
|
9585
|
-
metaKey: false,
|
|
9586
|
-
/** Whether the control or command key is currently pressed. */
|
|
9587
|
-
ctrlKey: false,
|
|
9588
|
-
/** Whether the alt or option key is currently pressed. */
|
|
9589
|
-
altKey: false,
|
|
9590
|
-
/** Whether the user is dragging. */
|
|
9591
|
-
isDragging: false,
|
|
9592
|
-
/** Whether the user is pointing. */
|
|
9593
|
-
isPointing: false,
|
|
9594
|
-
/** Whether the user is pinching. */
|
|
9595
|
-
isPinching: false,
|
|
9596
|
-
/** Whether the user is editing. */
|
|
9597
|
-
isEditing: false,
|
|
9598
|
-
/** Whether the user is panning. */
|
|
9599
|
-
isPanning: false,
|
|
9600
|
-
/** Whether the user is spacebar panning. */
|
|
9601
|
-
isSpacebarPanning: false,
|
|
9602
|
-
/** Velocity of mouse pointer, in pixels per millisecond */
|
|
9603
|
-
pointerVelocity: new Vec(),
|
|
9604
|
-
}
|
|
9605
|
-
|
|
9606
|
-
/**
|
|
9607
|
-
* Update the input points from a pointer, pinch, or wheel event.
|
|
9608
|
-
*
|
|
9609
|
-
* @param info - The event info.
|
|
9610
|
-
*/
|
|
9611
|
-
private _updateInputsFromEvent(
|
|
9612
|
-
info: TLPointerEventInfo | TLPinchEventInfo | TLWheelEventInfo
|
|
9613
|
-
): void {
|
|
9614
|
-
const {
|
|
9615
|
-
pointerVelocity,
|
|
9616
|
-
previousScreenPoint,
|
|
9617
|
-
previousPagePoint,
|
|
9618
|
-
currentScreenPoint,
|
|
9619
|
-
currentPagePoint,
|
|
9620
|
-
originScreenPoint,
|
|
9621
|
-
originPagePoint,
|
|
9622
|
-
} = this.inputs
|
|
9623
|
-
|
|
9624
|
-
const { screenBounds } = this.store.unsafeGetWithoutCapture(TLINSTANCE_ID)!
|
|
9625
|
-
const { x: cx, y: cy, z: cz } = unsafe__withoutCapture(() => this.getCamera())
|
|
9626
|
-
|
|
9627
|
-
const sx = info.point.x - screenBounds.x
|
|
9628
|
-
const sy = info.point.y - screenBounds.y
|
|
9629
|
-
const sz = info.point.z ?? 0.5
|
|
9630
|
-
|
|
9631
|
-
previousScreenPoint.setTo(currentScreenPoint)
|
|
9632
|
-
previousPagePoint.setTo(currentPagePoint)
|
|
9633
|
-
|
|
9634
|
-
// The "screen bounds" is relative to the user's actual screen.
|
|
9635
|
-
// The "screen point" is relative to the "screen bounds";
|
|
9636
|
-
// it will be 0,0 when its actual screen position is equal
|
|
9637
|
-
// to screenBounds.point. This is confusing!
|
|
9638
|
-
currentScreenPoint.set(sx, sy)
|
|
9639
|
-
const nx = sx / cz - cx
|
|
9640
|
-
const ny = sy / cz - cy
|
|
9641
|
-
if (isFinite(nx) && isFinite(ny)) {
|
|
9642
|
-
currentPagePoint.set(nx, ny, sz)
|
|
9643
|
-
}
|
|
9644
|
-
|
|
9645
|
-
this.inputs.isPen = info.type === 'pointer' && info.isPen
|
|
9646
|
-
|
|
9647
|
-
// Reset velocity on pointer down, or when a pinch starts or ends
|
|
9648
|
-
if (info.name === 'pointer_down' || this.inputs.isPinching) {
|
|
9649
|
-
pointerVelocity.set(0, 0)
|
|
9650
|
-
originScreenPoint.setTo(currentScreenPoint)
|
|
9651
|
-
originPagePoint.setTo(currentPagePoint)
|
|
9652
|
-
}
|
|
9653
|
-
|
|
9654
|
-
// todo: We only have to do this if there are multiple users in the document
|
|
9655
|
-
this.run(
|
|
9656
|
-
() => {
|
|
9657
|
-
this.store.put([
|
|
9658
|
-
{
|
|
9659
|
-
id: TLPOINTER_ID,
|
|
9660
|
-
typeName: 'pointer',
|
|
9661
|
-
x: currentPagePoint.x,
|
|
9662
|
-
y: currentPagePoint.y,
|
|
9663
|
-
lastActivityTimestamp:
|
|
9664
|
-
// If our pointer moved only because we're following some other user, then don't
|
|
9665
|
-
// update our last activity timestamp; otherwise, update it to the current timestamp.
|
|
9666
|
-
info.type === 'pointer' && info.pointerId === INTERNAL_POINTER_IDS.CAMERA_MOVE
|
|
9667
|
-
? (this.store.unsafeGetWithoutCapture(TLPOINTER_ID)?.lastActivityTimestamp ??
|
|
9668
|
-
this._tickManager.now)
|
|
9669
|
-
: this._tickManager.now,
|
|
9670
|
-
meta: {},
|
|
9671
|
-
},
|
|
9672
|
-
])
|
|
9673
|
-
},
|
|
9674
|
-
{ history: 'ignore' }
|
|
9675
|
-
)
|
|
9676
|
-
}
|
|
9677
|
-
|
|
9678
9718
|
/**
|
|
9679
9719
|
* Dispatch a cancel event.
|
|
9680
9720
|
*
|
|
@@ -9744,19 +9784,22 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9744
9784
|
// weird but true: what `inputs` calls screen-space is actually viewport space. so
|
|
9745
9785
|
// we need to convert back into true screen space first. we should fix this...
|
|
9746
9786
|
Vec.Add(
|
|
9747
|
-
this.inputs.
|
|
9787
|
+
this.inputs.getCurrentScreenPoint(),
|
|
9748
9788
|
this.store.unsafeGetWithoutCapture(TLINSTANCE_ID)!.screenBounds
|
|
9749
9789
|
),
|
|
9750
9790
|
pointerId: options?.pointerId ?? 0,
|
|
9751
9791
|
button: options?.button ?? 0,
|
|
9752
|
-
isPen: options?.isPen ?? this.inputs.
|
|
9753
|
-
shiftKey: options?.shiftKey ?? this.inputs.
|
|
9754
|
-
altKey: options?.altKey ?? this.inputs.
|
|
9755
|
-
ctrlKey: options?.ctrlKey ?? this.inputs.
|
|
9756
|
-
metaKey: options?.metaKey ?? this.inputs.
|
|
9757
|
-
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,
|
|
9758
9798
|
}
|
|
9759
9799
|
|
|
9800
|
+
// needs to be calculated second
|
|
9801
|
+
event.accelKey = options?.accelKey ?? this.inputs.getAccelKey()
|
|
9802
|
+
|
|
9760
9803
|
if (options?.immediate) {
|
|
9761
9804
|
this._flushEventForTick(event)
|
|
9762
9805
|
} else {
|
|
@@ -10129,16 +10172,16 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10129
10172
|
/** @internal */
|
|
10130
10173
|
@bind
|
|
10131
10174
|
_setShiftKeyTimeout() {
|
|
10132
|
-
this.inputs.
|
|
10175
|
+
this.inputs.setShiftKey(false)
|
|
10133
10176
|
this.dispatch({
|
|
10134
10177
|
type: 'keyboard',
|
|
10135
10178
|
name: 'key_up',
|
|
10136
10179
|
key: 'Shift',
|
|
10137
|
-
shiftKey: this.inputs.
|
|
10138
|
-
ctrlKey: this.inputs.
|
|
10139
|
-
altKey: this.inputs.
|
|
10140
|
-
metaKey: this.inputs.
|
|
10141
|
-
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(),
|
|
10142
10185
|
code: 'ShiftLeft',
|
|
10143
10186
|
})
|
|
10144
10187
|
}
|
|
@@ -10149,16 +10192,16 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10149
10192
|
/** @internal */
|
|
10150
10193
|
@bind
|
|
10151
10194
|
_setAltKeyTimeout() {
|
|
10152
|
-
this.inputs.
|
|
10195
|
+
this.inputs.setAltKey(false)
|
|
10153
10196
|
this.dispatch({
|
|
10154
10197
|
type: 'keyboard',
|
|
10155
10198
|
name: 'key_up',
|
|
10156
10199
|
key: 'Alt',
|
|
10157
|
-
shiftKey: this.inputs.
|
|
10158
|
-
ctrlKey: this.inputs.
|
|
10159
|
-
altKey: this.inputs.
|
|
10160
|
-
metaKey: this.inputs.
|
|
10161
|
-
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(),
|
|
10162
10205
|
code: 'AltLeft',
|
|
10163
10206
|
})
|
|
10164
10207
|
}
|
|
@@ -10169,16 +10212,16 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10169
10212
|
/** @internal */
|
|
10170
10213
|
@bind
|
|
10171
10214
|
_setCtrlKeyTimeout() {
|
|
10172
|
-
this.inputs.
|
|
10215
|
+
this.inputs.setCtrlKey(false)
|
|
10173
10216
|
this.dispatch({
|
|
10174
10217
|
type: 'keyboard',
|
|
10175
10218
|
name: 'key_up',
|
|
10176
10219
|
key: 'Ctrl',
|
|
10177
|
-
shiftKey: this.inputs.
|
|
10178
|
-
ctrlKey: this.inputs.
|
|
10179
|
-
altKey: this.inputs.
|
|
10180
|
-
metaKey: this.inputs.
|
|
10181
|
-
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(),
|
|
10182
10225
|
code: 'ControlLeft',
|
|
10183
10226
|
})
|
|
10184
10227
|
}
|
|
@@ -10189,16 +10232,16 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10189
10232
|
/** @internal */
|
|
10190
10233
|
@bind
|
|
10191
10234
|
_setMetaKeyTimeout() {
|
|
10192
|
-
this.inputs.
|
|
10235
|
+
this.inputs.setMetaKey(false)
|
|
10193
10236
|
this.dispatch({
|
|
10194
10237
|
type: 'keyboard',
|
|
10195
10238
|
name: 'key_up',
|
|
10196
10239
|
key: 'Meta',
|
|
10197
|
-
shiftKey: this.inputs.
|
|
10198
|
-
ctrlKey: this.inputs.
|
|
10199
|
-
altKey: this.inputs.
|
|
10200
|
-
metaKey: this.inputs.
|
|
10201
|
-
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(),
|
|
10202
10245
|
code: 'MetaLeft',
|
|
10203
10246
|
})
|
|
10204
10247
|
}
|
|
@@ -10206,9 +10249,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10206
10249
|
/** @internal */
|
|
10207
10250
|
private _restoreToolId = 'select'
|
|
10208
10251
|
|
|
10209
|
-
/** @internal */
|
|
10210
|
-
private _pinchStart = 1
|
|
10211
|
-
|
|
10212
10252
|
/** @internal */
|
|
10213
10253
|
private _didPinch = false
|
|
10214
10254
|
|
|
@@ -10315,11 +10355,11 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10315
10355
|
if (info.type === 'misc') {
|
|
10316
10356
|
// stop panning if the interaction is cancelled or completed
|
|
10317
10357
|
if (info.name === 'cancel' || info.name === 'complete') {
|
|
10318
|
-
this.inputs.
|
|
10358
|
+
this.inputs.setIsDragging(false)
|
|
10319
10359
|
|
|
10320
|
-
if (this.inputs.
|
|
10321
|
-
this.inputs.
|
|
10322
|
-
this.inputs.
|
|
10360
|
+
if (this.inputs.getIsPanning()) {
|
|
10361
|
+
this.inputs.setIsPanning(false)
|
|
10362
|
+
this.inputs.setIsSpacebarPanning(false)
|
|
10323
10363
|
this.setCursor({ type: this._prevCursor, rotation: 0 })
|
|
10324
10364
|
}
|
|
10325
10365
|
}
|
|
@@ -10332,39 +10372,37 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10332
10372
|
if (info.shiftKey) {
|
|
10333
10373
|
clearTimeout(this._shiftKeyTimeout)
|
|
10334
10374
|
this._shiftKeyTimeout = -1
|
|
10335
|
-
inputs.
|
|
10336
|
-
} else if (!info.shiftKey && inputs.
|
|
10375
|
+
inputs.setShiftKey(true)
|
|
10376
|
+
} else if (!info.shiftKey && inputs.getShiftKey() && this._shiftKeyTimeout === -1) {
|
|
10337
10377
|
this._shiftKeyTimeout = this.timers.setTimeout(this._setShiftKeyTimeout, 150)
|
|
10338
10378
|
}
|
|
10339
10379
|
|
|
10340
10380
|
if (info.altKey) {
|
|
10341
10381
|
clearTimeout(this._altKeyTimeout)
|
|
10342
10382
|
this._altKeyTimeout = -1
|
|
10343
|
-
inputs.
|
|
10344
|
-
} else if (!info.altKey && inputs.
|
|
10383
|
+
inputs.setAltKey(true)
|
|
10384
|
+
} else if (!info.altKey && inputs.getAltKey() && this._altKeyTimeout === -1) {
|
|
10345
10385
|
this._altKeyTimeout = this.timers.setTimeout(this._setAltKeyTimeout, 150)
|
|
10346
10386
|
}
|
|
10347
10387
|
|
|
10348
10388
|
if (info.ctrlKey) {
|
|
10349
10389
|
clearTimeout(this._ctrlKeyTimeout)
|
|
10350
10390
|
this._ctrlKeyTimeout = -1
|
|
10351
|
-
inputs.
|
|
10352
|
-
} else if (!info.ctrlKey && inputs.
|
|
10391
|
+
inputs.setCtrlKey(true)
|
|
10392
|
+
} else if (!info.ctrlKey && inputs.getCtrlKey() && this._ctrlKeyTimeout === -1) {
|
|
10353
10393
|
this._ctrlKeyTimeout = this.timers.setTimeout(this._setCtrlKeyTimeout, 150)
|
|
10354
10394
|
}
|
|
10355
10395
|
|
|
10356
10396
|
if (info.metaKey) {
|
|
10357
10397
|
clearTimeout(this._metaKeyTimeout)
|
|
10358
10398
|
this._metaKeyTimeout = -1
|
|
10359
|
-
inputs.
|
|
10360
|
-
} else if (!info.metaKey && inputs.
|
|
10399
|
+
inputs.setMetaKey(true)
|
|
10400
|
+
} else if (!info.metaKey && inputs.getMetaKey() && this._metaKeyTimeout === -1) {
|
|
10361
10401
|
this._metaKeyTimeout = this.timers.setTimeout(this._setMetaKeyTimeout, 150)
|
|
10362
10402
|
}
|
|
10363
10403
|
|
|
10364
|
-
|
|
10365
|
-
|
|
10366
|
-
if (!inputs.isPointing) {
|
|
10367
|
-
inputs.isDragging = false
|
|
10404
|
+
if (!inputs.getIsPointing()) {
|
|
10405
|
+
inputs.setIsDragging(false)
|
|
10368
10406
|
}
|
|
10369
10407
|
|
|
10370
10408
|
const instanceState = this.store.unsafeGetWithoutCapture(TLINSTANCE_ID)!
|
|
@@ -10375,29 +10413,29 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10375
10413
|
case 'pinch': {
|
|
10376
10414
|
if (cameraOptions.isLocked) return
|
|
10377
10415
|
clearTimeout(this._longPressTimeout)
|
|
10378
|
-
this.
|
|
10416
|
+
this.inputs.updateFromEvent(info)
|
|
10379
10417
|
|
|
10380
10418
|
switch (info.name) {
|
|
10381
10419
|
case 'pinch_start': {
|
|
10382
|
-
if (inputs.
|
|
10420
|
+
if (inputs.getIsPinching()) return
|
|
10383
10421
|
|
|
10384
|
-
if (!inputs.
|
|
10385
|
-
this._pinchStart = this.getCamera().z
|
|
10422
|
+
if (!inputs.getIsEditing()) {
|
|
10386
10423
|
if (!this._selectedShapeIdsAtPointerDown.length) {
|
|
10387
10424
|
this._selectedShapeIdsAtPointerDown = [...pageState.selectedShapeIds]
|
|
10388
10425
|
}
|
|
10389
10426
|
|
|
10390
10427
|
this._didPinch = true
|
|
10391
10428
|
|
|
10392
|
-
inputs.
|
|
10429
|
+
inputs.setIsPinching(true)
|
|
10393
10430
|
|
|
10394
10431
|
this.interrupt()
|
|
10395
10432
|
}
|
|
10396
10433
|
|
|
10434
|
+
this.emit('event', info)
|
|
10397
10435
|
return // Stop here!
|
|
10398
10436
|
}
|
|
10399
10437
|
case 'pinch': {
|
|
10400
|
-
if (!inputs.
|
|
10438
|
+
if (!inputs.getIsPinching()) return
|
|
10401
10439
|
|
|
10402
10440
|
const {
|
|
10403
10441
|
point: { z = 1 },
|
|
@@ -10428,13 +10466,14 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10428
10466
|
{ immediate: true }
|
|
10429
10467
|
)
|
|
10430
10468
|
|
|
10469
|
+
this.emit('event', info)
|
|
10431
10470
|
return // Stop here!
|
|
10432
10471
|
}
|
|
10433
10472
|
case 'pinch_end': {
|
|
10434
|
-
if (!inputs.
|
|
10473
|
+
if (!inputs.getIsPinching()) return this
|
|
10435
10474
|
|
|
10436
10475
|
// Stop pinching
|
|
10437
|
-
inputs.
|
|
10476
|
+
inputs.setIsPinching(false)
|
|
10438
10477
|
|
|
10439
10478
|
// Stash and clear the shapes that were selected when the pinch started
|
|
10440
10479
|
const { _selectedShapeIdsAtPointerDown: shapesToReselect } = this
|
|
@@ -10454,6 +10493,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10454
10493
|
}
|
|
10455
10494
|
}
|
|
10456
10495
|
|
|
10496
|
+
this.emit('event', info)
|
|
10457
10497
|
return // Stop here!
|
|
10458
10498
|
}
|
|
10459
10499
|
}
|
|
@@ -10461,7 +10501,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10461
10501
|
case 'wheel': {
|
|
10462
10502
|
if (cameraOptions.isLocked) return
|
|
10463
10503
|
|
|
10464
|
-
this.
|
|
10504
|
+
this.inputs.updateFromEvent(info)
|
|
10465
10505
|
|
|
10466
10506
|
const { panSpeed, zoomSpeed } = cameraOptions
|
|
10467
10507
|
let wheelBehavior = cameraOptions.wheelBehavior
|
|
@@ -10492,7 +10532,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10492
10532
|
switch (behavior) {
|
|
10493
10533
|
case 'zoom': {
|
|
10494
10534
|
// Zoom in on current screen point using the wheel delta
|
|
10495
|
-
const { x, y } = this.inputs.
|
|
10535
|
+
const { x, y } = this.inputs.getCurrentScreenPoint()
|
|
10496
10536
|
let delta = dz
|
|
10497
10537
|
|
|
10498
10538
|
// If we're forcing zoom, then we need to do the wheel normalization math here
|
|
@@ -10509,6 +10549,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10509
10549
|
immediate: true,
|
|
10510
10550
|
})
|
|
10511
10551
|
this.maybeTrackPerformance('Zooming')
|
|
10552
|
+
this.root.handleEvent(info)
|
|
10553
|
+
this.emit('event', info)
|
|
10512
10554
|
return
|
|
10513
10555
|
}
|
|
10514
10556
|
case 'pan': {
|
|
@@ -10517,6 +10559,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10517
10559
|
immediate: true,
|
|
10518
10560
|
})
|
|
10519
10561
|
this.maybeTrackPerformance('Panning')
|
|
10562
|
+
this.root.handleEvent(info)
|
|
10563
|
+
this.emit('event', info)
|
|
10520
10564
|
return
|
|
10521
10565
|
}
|
|
10522
10566
|
}
|
|
@@ -10525,9 +10569,9 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10525
10569
|
}
|
|
10526
10570
|
case 'pointer': {
|
|
10527
10571
|
// Ignore pointer events while we're pinching
|
|
10528
|
-
if (inputs.
|
|
10572
|
+
if (inputs.getIsPinching()) return
|
|
10529
10573
|
|
|
10530
|
-
this.
|
|
10574
|
+
this.inputs.updateFromEvent(info)
|
|
10531
10575
|
const { isPen } = info
|
|
10532
10576
|
const { isPenMode } = instanceState
|
|
10533
10577
|
|
|
@@ -10536,7 +10580,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10536
10580
|
// If we're in pen mode and the input is not a pen type, then stop here
|
|
10537
10581
|
if (isPenMode && !isPen) return
|
|
10538
10582
|
|
|
10539
|
-
if (!this.inputs.
|
|
10583
|
+
if (!this.inputs.getIsPanning()) {
|
|
10540
10584
|
// Start a long press timeout
|
|
10541
10585
|
this._longPressTimeout = this.timers.setTimeout(() => {
|
|
10542
10586
|
const vsb = this.getViewportScreenBounds()
|
|
@@ -10546,7 +10590,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10546
10590
|
// viewport bounds, and will be again when this event is handled...
|
|
10547
10591
|
// so we need to counter-adjust from the stored value so that the
|
|
10548
10592
|
// new value is set correctly.
|
|
10549
|
-
point: this.inputs.
|
|
10593
|
+
point: this.inputs.getOriginScreenPoint().clone().addXY(vsb.x, vsb.y),
|
|
10550
10594
|
name: 'long_press',
|
|
10551
10595
|
})
|
|
10552
10596
|
}, this.options.longPressDurationMs)
|
|
@@ -10563,8 +10607,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10563
10607
|
inputs.buttons.add(info.button)
|
|
10564
10608
|
|
|
10565
10609
|
// Start pointing and stop dragging
|
|
10566
|
-
inputs.
|
|
10567
|
-
inputs.
|
|
10610
|
+
inputs.setIsPointing(true)
|
|
10611
|
+
inputs.setIsDragging(false)
|
|
10568
10612
|
|
|
10569
10613
|
// If pen mode is off but we're not already in pen mode, turn that on
|
|
10570
10614
|
if (!isPenMode && isPen) this.updateInstanceState({ isPenMode: true })
|
|
@@ -10576,16 +10620,16 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10576
10620
|
this.setCurrentTool('eraser')
|
|
10577
10621
|
} else if (info.button === MIDDLE_MOUSE_BUTTON) {
|
|
10578
10622
|
// Middle mouse pan activates panning unless we're already panning (with spacebar)
|
|
10579
|
-
if (!this.inputs.
|
|
10623
|
+
if (!this.inputs.getIsPanning()) {
|
|
10580
10624
|
this._prevCursor = this.getInstanceState().cursor.type
|
|
10581
10625
|
}
|
|
10582
|
-
this.inputs.
|
|
10626
|
+
this.inputs.setIsPanning(true)
|
|
10583
10627
|
clearTimeout(this._longPressTimeout)
|
|
10584
10628
|
}
|
|
10585
10629
|
|
|
10586
10630
|
// We might be panning because we did a middle mouse click, or because we're holding spacebar and started a regular click
|
|
10587
10631
|
// Also stop here, we don't want the state chart to receive the event
|
|
10588
|
-
if (this.inputs.
|
|
10632
|
+
if (this.inputs.getIsPanning()) {
|
|
10589
10633
|
this.stopCameraAnimation()
|
|
10590
10634
|
this.setCursor({ type: 'grabbing', rotation: 0 })
|
|
10591
10635
|
return this
|
|
@@ -10600,9 +10644,10 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10600
10644
|
const { x: cx, y: cy, z: cz } = unsafe__withoutCapture(() => this.getCamera())
|
|
10601
10645
|
|
|
10602
10646
|
// If we've started panning, then clear any long press timeout
|
|
10603
|
-
if (this.inputs.
|
|
10647
|
+
if (this.inputs.getIsPanning() && this.inputs.getIsPointing()) {
|
|
10604
10648
|
// Handle spacebar / middle mouse button panning
|
|
10605
|
-
const
|
|
10649
|
+
const currentScreenPoint = this.inputs.getCurrentScreenPoint()
|
|
10650
|
+
const previousScreenPoint = this.inputs.getPreviousScreenPoint()
|
|
10606
10651
|
const offset = Vec.Sub(currentScreenPoint, previousScreenPoint)
|
|
10607
10652
|
this.setCamera(new Vec(cx + offset.x / cz, cy + offset.y / cz, cz), {
|
|
10608
10653
|
immediate: true,
|
|
@@ -10612,24 +10657,25 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10612
10657
|
}
|
|
10613
10658
|
|
|
10614
10659
|
if (
|
|
10615
|
-
inputs.
|
|
10616
|
-
!inputs.
|
|
10617
|
-
Vec.Dist2(
|
|
10660
|
+
inputs.getIsPointing() &&
|
|
10661
|
+
!inputs.getIsDragging() &&
|
|
10662
|
+
Vec.Dist2(inputs.getOriginPagePoint(), inputs.getCurrentPagePoint()) *
|
|
10663
|
+
this.getZoomLevel() >
|
|
10618
10664
|
(instanceState.isCoarsePointer
|
|
10619
10665
|
? this.options.coarseDragDistanceSquared
|
|
10620
10666
|
: this.options.dragDistanceSquared) /
|
|
10621
10667
|
cz
|
|
10622
10668
|
) {
|
|
10623
10669
|
// Start dragging
|
|
10624
|
-
inputs.
|
|
10670
|
+
inputs.setIsDragging(true)
|
|
10625
10671
|
clearTimeout(this._longPressTimeout)
|
|
10626
10672
|
}
|
|
10627
10673
|
break
|
|
10628
10674
|
}
|
|
10629
10675
|
case 'pointer_up': {
|
|
10630
10676
|
// Stop dragging / pointing
|
|
10631
|
-
inputs.
|
|
10632
|
-
inputs.
|
|
10677
|
+
inputs.setIsDragging(false)
|
|
10678
|
+
inputs.setIsPointing(false)
|
|
10633
10679
|
clearTimeout(this._longPressTimeout)
|
|
10634
10680
|
|
|
10635
10681
|
// Remove the button from the buttons set
|
|
@@ -10646,12 +10692,12 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10646
10692
|
info.button = 0
|
|
10647
10693
|
}
|
|
10648
10694
|
|
|
10649
|
-
if (inputs.
|
|
10695
|
+
if (inputs.getIsPanning()) {
|
|
10650
10696
|
if (!inputs.keys.has('Space')) {
|
|
10651
|
-
inputs.
|
|
10652
|
-
inputs.
|
|
10697
|
+
inputs.setIsPanning(false)
|
|
10698
|
+
inputs.setIsSpacebarPanning(false)
|
|
10653
10699
|
}
|
|
10654
|
-
const slideDirection = this.inputs.
|
|
10700
|
+
const slideDirection = this.inputs.getPointerVelocity()
|
|
10655
10701
|
const slideSpeed = Math.min(2, slideDirection.len())
|
|
10656
10702
|
|
|
10657
10703
|
switch (info.button) {
|
|
@@ -10695,43 +10741,48 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10695
10741
|
// Add the key from the keys set
|
|
10696
10742
|
inputs.keys.add(info.code)
|
|
10697
10743
|
|
|
10698
|
-
|
|
10699
|
-
|
|
10700
|
-
if (!
|
|
10701
|
-
this.
|
|
10702
|
-
|
|
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
|
+
}
|
|
10703
10750
|
|
|
10704
|
-
|
|
10705
|
-
|
|
10706
|
-
|
|
10707
|
-
|
|
10708
|
-
|
|
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
|
+
}
|
|
10709
10759
|
|
|
10710
|
-
|
|
10711
|
-
|
|
10712
|
-
|
|
10713
|
-
|
|
10714
|
-
|
|
10715
|
-
|
|
10716
|
-
|
|
10717
|
-
|
|
10718
|
-
|
|
10719
|
-
|
|
10720
|
-
|
|
10721
|
-
|
|
10722
|
-
|
|
10723
|
-
|
|
10724
|
-
|
|
10725
|
-
|
|
10726
|
-
|
|
10727
|
-
|
|
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
|
+
}
|
|
10728
10779
|
}
|
|
10729
|
-
}
|
|
10730
10780
|
|
|
10731
|
-
|
|
10732
|
-
|
|
10733
|
-
|
|
10734
|
-
|
|
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
|
+
}
|
|
10735
10786
|
}
|
|
10736
10787
|
}
|
|
10737
10788
|
|
|
@@ -10741,15 +10792,17 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10741
10792
|
// Remove the key from the keys set
|
|
10742
10793
|
inputs.keys.delete(info.code)
|
|
10743
10794
|
|
|
10744
|
-
|
|
10745
|
-
|
|
10746
|
-
if (
|
|
10747
|
-
|
|
10748
|
-
|
|
10749
|
-
|
|
10750
|
-
|
|
10751
|
-
|
|
10752
|
-
|
|
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
|
+
}
|
|
10753
10806
|
}
|
|
10754
10807
|
}
|
|
10755
10808
|
break
|