@tldraw/editor 4.3.0-canary.8fb8d4ef1572 → 4.3.0-canary.9433fac41058
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-cjs/index.d.ts +443 -120
- package/dist-cjs/index.js +6 -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 +3 -3
- 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 +292 -274
- package/dist-cjs/lib/editor/Editor.js.map +2 -2
- package/dist-cjs/lib/editor/derivations/notVisibleShapes.js +18 -17
- 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/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/editor/types/emit-types.js.map +1 -1
- 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/globals/menus.js +1 -1
- package/dist-cjs/lib/globals/menus.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 +443 -120
- package/dist-esm/index.mjs +7 -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 +3 -3
- 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 +293 -277
- package/dist-esm/lib/editor/Editor.mjs.map +2 -2
- package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs +18 -17
- 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/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/globals/menus.mjs +1 -1
- package/dist-esm/lib/globals/menus.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 +18 -16
- package/src/index.ts +4 -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 -3
- 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 +378 -320
- package/src/lib/editor/derivations/notVisibleShapes.ts +37 -23
- 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/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/editor/types/emit-types.ts +3 -1
- 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/globals/menus.ts +1 -1
- 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,18 @@ 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'
|
|
154
152
|
import { TextManager } from './managers/TextManager/TextManager'
|
|
155
153
|
import { TickManager } from './managers/TickManager/TickManager'
|
|
156
154
|
import { UserPreferencesManager } from './managers/UserPreferencesManager/UserPreferencesManager'
|
|
157
|
-
import { ShapeUtil, TLGeometryOpts, TLResizeMode } from './shapes/ShapeUtil'
|
|
155
|
+
import { ShapeUtil, TLEditStartInfo, TLGeometryOpts, TLResizeMode } from './shapes/ShapeUtil'
|
|
158
156
|
import { RootState } from './tools/RootState'
|
|
159
157
|
import { StateNode, TLStateNodeConstructor } from './tools/StateNode'
|
|
160
158
|
import { TLContent } from './types/clipboard-types'
|
|
161
159
|
import { TLEventMap } from './types/emit-types'
|
|
162
|
-
import {
|
|
163
|
-
TLEventInfo,
|
|
164
|
-
TLPinchEventInfo,
|
|
165
|
-
TLPointerEventInfo,
|
|
166
|
-
TLWheelEventInfo,
|
|
167
|
-
} from './types/event-types'
|
|
160
|
+
import { TLEventInfo, TLPointerEventInfo } from './types/event-types'
|
|
168
161
|
import { TLExternalAsset, TLExternalContent } from './types/external-content'
|
|
169
162
|
import { TLHistoryBatchOptions } from './types/history-types'
|
|
170
163
|
import {
|
|
@@ -195,7 +188,7 @@ export type TLResizeShapeOptions = Partial<{
|
|
|
195
188
|
/** @public */
|
|
196
189
|
export interface TLEditorOptions {
|
|
197
190
|
/**
|
|
198
|
-
* The Store instance to use for keeping the
|
|
191
|
+
* The Store instance to use for keeping the editor's data. This may be prepopulated, e.g. by loading
|
|
199
192
|
* from a server or database.
|
|
200
193
|
*/
|
|
201
194
|
store: TLStore
|
|
@@ -333,6 +326,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
333
326
|
|
|
334
327
|
this._tickManager = new TickManager(this)
|
|
335
328
|
|
|
329
|
+
this.inputs = new InputsManager(this)
|
|
330
|
+
|
|
336
331
|
class NewRoot extends RootState {
|
|
337
332
|
static override initial = initialState ?? ''
|
|
338
333
|
}
|
|
@@ -867,7 +862,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
867
862
|
}
|
|
868
863
|
|
|
869
864
|
/**
|
|
870
|
-
* A set of functions to call when the
|
|
865
|
+
* A set of functions to call when the editor is disposed.
|
|
871
866
|
*
|
|
872
867
|
* @public
|
|
873
868
|
*/
|
|
@@ -880,11 +875,21 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
880
875
|
*/
|
|
881
876
|
isDisposed = false
|
|
882
877
|
|
|
883
|
-
/**
|
|
884
|
-
|
|
878
|
+
/**
|
|
879
|
+
* A manager for the editor's tick events.
|
|
880
|
+
*
|
|
881
|
+
* @internal */
|
|
882
|
+
private readonly _tickManager: TickManager
|
|
885
883
|
|
|
886
884
|
/**
|
|
887
|
-
* A manager for the
|
|
885
|
+
* A manager for the editor's input state.
|
|
886
|
+
*
|
|
887
|
+
* @public
|
|
888
|
+
*/
|
|
889
|
+
readonly inputs: InputsManager
|
|
890
|
+
|
|
891
|
+
/**
|
|
892
|
+
* A manager for the editor's snapping feature.
|
|
888
893
|
*
|
|
889
894
|
* @public
|
|
890
895
|
*/
|
|
@@ -969,6 +974,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
969
974
|
this.disposables.clear()
|
|
970
975
|
this.store.dispose()
|
|
971
976
|
this.isDisposed = true
|
|
977
|
+
this.emit('dispose')
|
|
972
978
|
}
|
|
973
979
|
|
|
974
980
|
/* ------------------- Shape Utils ------------------ */
|
|
@@ -1060,7 +1066,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1060
1066
|
/* --------------------- History -------------------- */
|
|
1061
1067
|
|
|
1062
1068
|
/**
|
|
1063
|
-
* A manager for the
|
|
1069
|
+
* A manager for the editor's history.
|
|
1064
1070
|
*
|
|
1065
1071
|
* @readonly
|
|
1066
1072
|
*/
|
|
@@ -1084,14 +1090,18 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1084
1090
|
}
|
|
1085
1091
|
|
|
1086
1092
|
/**
|
|
1087
|
-
* Whether the
|
|
1093
|
+
* Whether the editor can undo.
|
|
1088
1094
|
*
|
|
1089
1095
|
* @public
|
|
1090
1096
|
*/
|
|
1091
|
-
@computed
|
|
1097
|
+
@computed canUndo(): boolean {
|
|
1092
1098
|
return this.history.getNumUndos() > 0
|
|
1093
1099
|
}
|
|
1094
1100
|
|
|
1101
|
+
getCanUndo() {
|
|
1102
|
+
return this.canUndo()
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1095
1105
|
/**
|
|
1096
1106
|
* Redo to the next mark.
|
|
1097
1107
|
*
|
|
@@ -1109,20 +1119,24 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1109
1119
|
return this
|
|
1110
1120
|
}
|
|
1111
1121
|
|
|
1112
|
-
clearHistory() {
|
|
1113
|
-
this.history.clear()
|
|
1114
|
-
return this
|
|
1115
|
-
}
|
|
1116
|
-
|
|
1117
1122
|
/**
|
|
1118
|
-
* Whether the
|
|
1123
|
+
* Whether the editor can redo.
|
|
1119
1124
|
*
|
|
1120
1125
|
* @public
|
|
1121
1126
|
*/
|
|
1122
|
-
@computed
|
|
1127
|
+
@computed canRedo(): boolean {
|
|
1123
1128
|
return this.history.getNumRedos() > 0
|
|
1124
1129
|
}
|
|
1125
1130
|
|
|
1131
|
+
getCanRedo() {
|
|
1132
|
+
return this.canRedo()
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
clearHistory() {
|
|
1136
|
+
this.history.clear()
|
|
1137
|
+
return this
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1126
1140
|
/**
|
|
1127
1141
|
* Create a new "mark", or stopping point, in the undo redo history. Creating a mark will clear
|
|
1128
1142
|
* any redos. You typically want to do this just before a user interaction begins or is handled.
|
|
@@ -1296,7 +1310,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1296
1310
|
}),
|
|
1297
1311
|
selectionCount: this.getSelectedShapes().length,
|
|
1298
1312
|
editingShape: editingShapeId ? this.getShape(editingShapeId) : undefined,
|
|
1299
|
-
inputs: this.inputs,
|
|
1313
|
+
inputs: this.inputs.toJson(),
|
|
1300
1314
|
pageState: this.getCurrentPageState(),
|
|
1301
1315
|
instanceState: this.getInstanceState(),
|
|
1302
1316
|
collaboratorCount: this.getCollaboratorsOnCurrentPage().length,
|
|
@@ -1321,7 +1335,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1321
1335
|
* we're in a transaction that's about to be rolled back due to the same error we're currently
|
|
1322
1336
|
* reporting.
|
|
1323
1337
|
*
|
|
1324
|
-
* Instead, to listen to changes to this value, you need to listen to
|
|
1338
|
+
* Instead, to listen to changes to this value, you need to listen to editor's `crash` event.
|
|
1325
1339
|
*
|
|
1326
1340
|
* @internal
|
|
1327
1341
|
*/
|
|
@@ -2024,7 +2038,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2024
2038
|
}
|
|
2025
2039
|
|
|
2026
2040
|
/**
|
|
2027
|
-
* The id of the
|
|
2041
|
+
* The id of the editor's only selected shape.
|
|
2028
2042
|
*
|
|
2029
2043
|
* @returns Null if there is no shape or more than one selected shape, otherwise the selected shape's id.
|
|
2030
2044
|
*
|
|
@@ -2036,7 +2050,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2036
2050
|
}
|
|
2037
2051
|
|
|
2038
2052
|
/**
|
|
2039
|
-
* The
|
|
2053
|
+
* The editor's only selected shape.
|
|
2040
2054
|
*
|
|
2041
2055
|
* @returns Null if there is no shape or more than one selected shape, otherwise the selected shape.
|
|
2042
2056
|
*
|
|
@@ -2277,6 +2291,29 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2277
2291
|
return editingShapeId ? this.getShape(editingShapeId) : undefined
|
|
2278
2292
|
}
|
|
2279
2293
|
|
|
2294
|
+
/**
|
|
2295
|
+
* Whether the shape can be edited.
|
|
2296
|
+
*
|
|
2297
|
+
* @param shape - The shape (or shape id) to check if it can be edited.
|
|
2298
|
+
* @param info - The info about the edit start.
|
|
2299
|
+
*
|
|
2300
|
+
* @public
|
|
2301
|
+
* @returns true if the shape can be edited, false otherwise.
|
|
2302
|
+
*/
|
|
2303
|
+
canEditShape<T extends TLShape | TLShapeId>(shape: T | null, info?: TLEditStartInfo): shape is T {
|
|
2304
|
+
const id = typeof shape === 'string' ? shape : (shape?.id ?? null)
|
|
2305
|
+
if (!id) return false // no shape
|
|
2306
|
+
if (id === this.getEditingShapeId()) return false // already editing this shape
|
|
2307
|
+
const _shape = this.getShape(id)
|
|
2308
|
+
if (!_shape) return false // no shape
|
|
2309
|
+
const util = this.getShapeUtil(_shape)
|
|
2310
|
+
const _info: TLEditStartInfo = info ?? { type: 'unknown' }
|
|
2311
|
+
if (!util.canEdit(_shape, _info)) return false // shape is not editable
|
|
2312
|
+
if (this.getIsReadonly() && !util.canEditInReadonly(_shape)) return false // readonly and no exception
|
|
2313
|
+
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.
|
|
2314
|
+
return true // shape is editable
|
|
2315
|
+
}
|
|
2316
|
+
|
|
2280
2317
|
/**
|
|
2281
2318
|
* Set the current editing shape.
|
|
2282
2319
|
*
|
|
@@ -2292,44 +2329,59 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2292
2329
|
*/
|
|
2293
2330
|
setEditingShape(shape: TLShapeId | TLShape | null): this {
|
|
2294
2331
|
const id = typeof shape === 'string' ? shape : (shape?.id ?? null)
|
|
2295
|
-
this.setRichTextEditor(null)
|
|
2296
|
-
const prevEditingShapeId = this.getEditingShapeId()
|
|
2297
|
-
if (id !== prevEditingShapeId) {
|
|
2298
|
-
if (id) {
|
|
2299
|
-
const shape = this.getShape(id)
|
|
2300
|
-
if (shape && this.getShapeUtil(shape).canEdit(shape)) {
|
|
2301
|
-
this.run(
|
|
2302
|
-
() => {
|
|
2303
|
-
this._updateCurrentPageState({ editingShapeId: id })
|
|
2304
|
-
if (prevEditingShapeId) {
|
|
2305
|
-
const prevEditingShape = this.getShape(prevEditingShapeId)
|
|
2306
|
-
if (prevEditingShape) {
|
|
2307
|
-
this.getShapeUtil(prevEditingShape).onEditEnd?.(prevEditingShape)
|
|
2308
|
-
}
|
|
2309
|
-
}
|
|
2310
|
-
this.getShapeUtil(shape).onEditStart?.(shape)
|
|
2311
|
-
},
|
|
2312
|
-
{ history: 'ignore' }
|
|
2313
|
-
)
|
|
2314
|
-
return this
|
|
2315
|
-
}
|
|
2316
|
-
}
|
|
2317
2332
|
|
|
2318
|
-
|
|
2333
|
+
if (!id) {
|
|
2334
|
+
// setting the editing shape to null
|
|
2319
2335
|
this.run(
|
|
2320
2336
|
() => {
|
|
2321
|
-
|
|
2322
|
-
this.
|
|
2337
|
+
// Clean up the previous editing shape
|
|
2338
|
+
const prevEditingShapeId = this.getEditingShapeId()
|
|
2323
2339
|
if (prevEditingShapeId) {
|
|
2324
2340
|
const prevEditingShape = this.getShape(prevEditingShapeId)
|
|
2325
2341
|
if (prevEditingShape) {
|
|
2326
2342
|
this.getShapeUtil(prevEditingShape).onEditEnd?.(prevEditingShape)
|
|
2327
2343
|
}
|
|
2328
2344
|
}
|
|
2345
|
+
|
|
2346
|
+
// Clean up the editing shape state and rich text editor
|
|
2347
|
+
this._updateCurrentPageState({ editingShapeId: null })
|
|
2348
|
+
this._currentRichTextEditor.set(null)
|
|
2329
2349
|
},
|
|
2330
2350
|
{ history: 'ignore' }
|
|
2331
2351
|
)
|
|
2352
|
+
|
|
2353
|
+
return this
|
|
2332
2354
|
}
|
|
2355
|
+
|
|
2356
|
+
// id was provided but the next editing shape was not editable or didn't exist, so do nothing
|
|
2357
|
+
if (!this.canEditShape(id)) return this
|
|
2358
|
+
|
|
2359
|
+
// id was provided and the next editing shape is editable, so set the rich text editor to null
|
|
2360
|
+
this.run(
|
|
2361
|
+
() => {
|
|
2362
|
+
// Clean up the previous editing shape
|
|
2363
|
+
const prevEditingShapeId = this.getEditingShapeId()
|
|
2364
|
+
if (prevEditingShapeId) {
|
|
2365
|
+
const prevEditingShape = this.getShape(prevEditingShapeId)
|
|
2366
|
+
if (prevEditingShape) {
|
|
2367
|
+
this.getShapeUtil(prevEditingShape).onEditEnd?.(prevEditingShape)
|
|
2368
|
+
}
|
|
2369
|
+
}
|
|
2370
|
+
|
|
2371
|
+
// Clean up the editing shape state and rich text editor
|
|
2372
|
+
this._updateCurrentPageState({ editingShapeId: null })
|
|
2373
|
+
this._currentRichTextEditor.set(null)
|
|
2374
|
+
|
|
2375
|
+
// Set the new editing shape
|
|
2376
|
+
this.select(id)
|
|
2377
|
+
this._updateCurrentPageState({ editingShapeId: id })
|
|
2378
|
+
|
|
2379
|
+
const nextEditingShape = this.getShape(id)! // shape should be there because canEditShape checked it. Possible small chance that onEditEnd deleted it?
|
|
2380
|
+
this.getShapeUtil(nextEditingShape).onEditStart?.(nextEditingShape)
|
|
2381
|
+
},
|
|
2382
|
+
{ history: 'ignore' }
|
|
2383
|
+
)
|
|
2384
|
+
|
|
2333
2385
|
return this
|
|
2334
2386
|
}
|
|
2335
2387
|
|
|
@@ -2533,6 +2585,26 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2533
2585
|
return this.getCurrentPageState().croppingShapeId
|
|
2534
2586
|
}
|
|
2535
2587
|
|
|
2588
|
+
/**
|
|
2589
|
+
* Whether the shape can be cropped.
|
|
2590
|
+
*
|
|
2591
|
+
* @param shape - The shape (or shape id) to check if it can be cropped.
|
|
2592
|
+
*
|
|
2593
|
+
* @public
|
|
2594
|
+
* @returns true if the shape can be cropped, false otherwise.
|
|
2595
|
+
*/
|
|
2596
|
+
canCropShape<T extends TLShape | TLShapeId>(shape: T | null): shape is T {
|
|
2597
|
+
if (!shape) return false
|
|
2598
|
+
const id = typeof shape === 'string' ? shape : (shape?.id ?? null)
|
|
2599
|
+
if (!id) return false
|
|
2600
|
+
const _shape = this.getShape(id)
|
|
2601
|
+
if (!_shape) return false
|
|
2602
|
+
const util = this.getShapeUtil(_shape)
|
|
2603
|
+
if (!util.canCrop(_shape)) return false
|
|
2604
|
+
if (this.isShapeOrAncestorLocked(_shape)) return false
|
|
2605
|
+
return true
|
|
2606
|
+
}
|
|
2607
|
+
|
|
2536
2608
|
/**
|
|
2537
2609
|
* Set the current cropping shape.
|
|
2538
2610
|
*
|
|
@@ -2554,12 +2626,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2554
2626
|
() => {
|
|
2555
2627
|
if (!id) {
|
|
2556
2628
|
this.updateCurrentPageState({ croppingShapeId: null })
|
|
2557
|
-
} else {
|
|
2558
|
-
|
|
2559
|
-
const util = this.getShapeUtil(shape)
|
|
2560
|
-
if (shape && util.canCrop(shape)) {
|
|
2561
|
-
this.updateCurrentPageState({ croppingShapeId: id })
|
|
2562
|
-
}
|
|
2629
|
+
} else if (this.canCropShape(id)) {
|
|
2630
|
+
this.updateCurrentPageState({ croppingShapeId: id })
|
|
2563
2631
|
}
|
|
2564
2632
|
},
|
|
2565
2633
|
{ history: 'ignore' }
|
|
@@ -2669,6 +2737,52 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2669
2737
|
return this.getCamera().z
|
|
2670
2738
|
}
|
|
2671
2739
|
|
|
2740
|
+
private _debouncedZoomLevel = atom('debounced zoom level', 1)
|
|
2741
|
+
|
|
2742
|
+
/**
|
|
2743
|
+
* Get the debounced zoom level. When the camera is moving, this returns the zoom level
|
|
2744
|
+
* from when the camera started moving rather than the current zoom level. This can be
|
|
2745
|
+
* used to avoid expensive re-renders during camera movements.
|
|
2746
|
+
*
|
|
2747
|
+
* This behavior is controlled by the `useDebouncedZoom` option. When `useDebouncedZoom`
|
|
2748
|
+
* is `false`, this method always returns the current zoom level.
|
|
2749
|
+
*
|
|
2750
|
+
* @public
|
|
2751
|
+
*/
|
|
2752
|
+
@computed getDebouncedZoomLevel() {
|
|
2753
|
+
if (this.options.debouncedZoom) {
|
|
2754
|
+
if (this.getCameraState() === 'idle') {
|
|
2755
|
+
return this.getZoomLevel()
|
|
2756
|
+
} else {
|
|
2757
|
+
return this._debouncedZoomLevel.get()
|
|
2758
|
+
}
|
|
2759
|
+
}
|
|
2760
|
+
|
|
2761
|
+
return this.getZoomLevel()
|
|
2762
|
+
}
|
|
2763
|
+
|
|
2764
|
+
@computed private _getAboveDebouncedZoomThreshold() {
|
|
2765
|
+
return this.getCurrentPageShapeIds().size > this.options.debouncedZoomThreshold
|
|
2766
|
+
}
|
|
2767
|
+
|
|
2768
|
+
/**
|
|
2769
|
+
* Get the efficient zoom level. This returns the current zoom level if there are less than 300 shapes on the page,
|
|
2770
|
+
* otherwise it returns the debounced zoom level. This can be used to avoid expensive re-renders during camera movements.
|
|
2771
|
+
*
|
|
2772
|
+
* @public
|
|
2773
|
+
* @example
|
|
2774
|
+
* ```ts
|
|
2775
|
+
* editor.getEfficientZoomLevel()
|
|
2776
|
+
* ```
|
|
2777
|
+
*
|
|
2778
|
+
* @public
|
|
2779
|
+
*/
|
|
2780
|
+
@computed getEfficientZoomLevel() {
|
|
2781
|
+
return this._getAboveDebouncedZoomThreshold()
|
|
2782
|
+
? this.getDebouncedZoomLevel()
|
|
2783
|
+
: this.getZoomLevel()
|
|
2784
|
+
}
|
|
2785
|
+
|
|
2672
2786
|
/**
|
|
2673
2787
|
* Get the camera's initial or reset zoom level.
|
|
2674
2788
|
*
|
|
@@ -2995,7 +3109,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2995
3109
|
|
|
2996
3110
|
// Dispatch a new pointer move because the pointer's page will have changed
|
|
2997
3111
|
// (its screen position will compute to a new page position given the new camera position)
|
|
2998
|
-
const
|
|
3112
|
+
const currentScreenPoint = this.inputs.getCurrentScreenPoint()
|
|
3113
|
+
const currentPagePoint = this.inputs.getCurrentPagePoint()
|
|
2999
3114
|
|
|
3000
3115
|
// compare the next page point (derived from the current camera) to the current page point
|
|
3001
3116
|
if (
|
|
@@ -3159,7 +3274,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3159
3274
|
* ```ts
|
|
3160
3275
|
* editor.zoomIn()
|
|
3161
3276
|
* editor.zoomIn(editor.getViewportScreenCenter(), { animation: { duration: 200 } })
|
|
3162
|
-
* editor.zoomIn(editor.inputs.
|
|
3277
|
+
* editor.zoomIn(editor.inputs.getCurrentScreenPoint(), { animation: { duration: 200 } })
|
|
3163
3278
|
* ```
|
|
3164
3279
|
*
|
|
3165
3280
|
* @param point - The screen point to zoom in on. Defaults to the screen center
|
|
@@ -3204,7 +3319,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3204
3319
|
* ```ts
|
|
3205
3320
|
* editor.zoomOut()
|
|
3206
3321
|
* editor.zoomOut(editor.getViewportScreenCenter(), { animation: { duration: 120 } })
|
|
3207
|
-
* editor.zoomOut(editor.inputs.
|
|
3322
|
+
* editor.zoomOut(editor.inputs.getCurrentScreenPoint(), { animation: { duration: 120 } })
|
|
3208
3323
|
* ```
|
|
3209
3324
|
*
|
|
3210
3325
|
* @param point - The point to zoom out on. Defaults to the viewport screen center.
|
|
@@ -3261,10 +3376,17 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3261
3376
|
|
|
3262
3377
|
const selectionPageBounds = this.getSelectionPageBounds()
|
|
3263
3378
|
if (selectionPageBounds) {
|
|
3264
|
-
this.
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3379
|
+
const currentZoom = this.getZoomLevel()
|
|
3380
|
+
// If already at 100%, zoom to fit the selection in the viewport
|
|
3381
|
+
// Otherwise, zoom to 100% centered on the selection
|
|
3382
|
+
if (Math.abs(currentZoom - 1) < 0.01) {
|
|
3383
|
+
this.zoomToBounds(selectionPageBounds, opts)
|
|
3384
|
+
} else {
|
|
3385
|
+
this.zoomToBounds(selectionPageBounds, {
|
|
3386
|
+
targetZoom: 1,
|
|
3387
|
+
...opts,
|
|
3388
|
+
})
|
|
3389
|
+
}
|
|
3268
3390
|
}
|
|
3269
3391
|
return this
|
|
3270
3392
|
}
|
|
@@ -3321,7 +3443,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3321
3443
|
|
|
3322
3444
|
const viewportScreenBounds = this.getViewportScreenBounds()
|
|
3323
3445
|
|
|
3324
|
-
const inset =
|
|
3446
|
+
const inset =
|
|
3447
|
+
opts?.inset ?? Math.min(this.options.zoomToFitPadding, viewportScreenBounds.width * 0.28)
|
|
3325
3448
|
|
|
3326
3449
|
const baseZoom = this.getBaseZoom()
|
|
3327
3450
|
const zoomMin = cameraOptions.zoomSteps[0]
|
|
@@ -3631,22 +3754,23 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3631
3754
|
if (_willSetInitialBounds) {
|
|
3632
3755
|
// If we have just received the initial bounds, don't center the camera.
|
|
3633
3756
|
this.updateInstanceState({ screenBounds: screenBounds.toJson(), insets })
|
|
3757
|
+
this.emit('resize', screenBounds.toJson())
|
|
3634
3758
|
this.setCamera(this.getCamera())
|
|
3635
3759
|
} else {
|
|
3636
3760
|
if (center && !this.getInstanceState().followingUserId) {
|
|
3637
3761
|
// Get the page center before the change, make the change, and restore it
|
|
3638
3762
|
const before = this.getViewportPageBounds().center
|
|
3639
3763
|
this.updateInstanceState({ screenBounds: screenBounds.toJson(), insets })
|
|
3764
|
+
this.emit('resize', screenBounds.toJson())
|
|
3640
3765
|
this.centerOnPoint(before)
|
|
3641
3766
|
} else {
|
|
3642
3767
|
// Otherwise,
|
|
3643
3768
|
this.updateInstanceState({ screenBounds: screenBounds.toJson(), insets })
|
|
3769
|
+
this.emit('resize', screenBounds.toJson())
|
|
3644
3770
|
this._setCamera(Vec.From({ ...this.getCamera() }))
|
|
3645
3771
|
}
|
|
3646
3772
|
}
|
|
3647
3773
|
|
|
3648
|
-
this._tickCameraState()
|
|
3649
|
-
|
|
3650
3774
|
return this
|
|
3651
3775
|
}
|
|
3652
3776
|
|
|
@@ -4052,18 +4176,19 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
4052
4176
|
// box just for rendering, and we only update after the camera stops moving.
|
|
4053
4177
|
private _cameraState = atom('camera state', 'idle' as 'idle' | 'moving')
|
|
4054
4178
|
private _cameraStateTimeoutRemaining = 0
|
|
4055
|
-
_decayCameraStateTimeout(elapsed: number) {
|
|
4179
|
+
private _decayCameraStateTimeout(elapsed: number) {
|
|
4056
4180
|
this._cameraStateTimeoutRemaining -= elapsed
|
|
4057
4181
|
if (this._cameraStateTimeoutRemaining > 0) return
|
|
4058
4182
|
this.off('tick', this._decayCameraStateTimeout)
|
|
4059
4183
|
this._cameraState.set('idle')
|
|
4060
4184
|
}
|
|
4061
|
-
_tickCameraState() {
|
|
4185
|
+
private _tickCameraState() {
|
|
4062
4186
|
// always reset the timeout
|
|
4063
4187
|
this._cameraStateTimeoutRemaining = this.options.cameraMovingTimeoutMs
|
|
4064
4188
|
// If the state is idle, then start the tick
|
|
4065
4189
|
if (this._cameraState.__unsafe__getWithoutCapture() !== 'idle') return
|
|
4066
4190
|
this._cameraState.set('moving')
|
|
4191
|
+
this._debouncedZoomLevel.set(unsafe__withoutCapture(() => this.getCamera().z))
|
|
4067
4192
|
this.on('tick', this._decayCameraStateTimeout)
|
|
4068
4193
|
}
|
|
4069
4194
|
|
|
@@ -7649,8 +7774,14 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
7649
7774
|
// then if the shape is flipped in one axis only, we need to apply an extra rotation
|
|
7650
7775
|
// to make sure the shape is mirrored correctly
|
|
7651
7776
|
if (Math.sign(scale.x) * Math.sign(scale.y) < 0) {
|
|
7652
|
-
|
|
7653
|
-
rotation
|
|
7777
|
+
// We need to compute the new local rotation that will result in the negated page rotation.
|
|
7778
|
+
// For a shape with local rotation `localRot` and parent page rotation `parentRot`:
|
|
7779
|
+
// - pageRot = parentRot + localRot
|
|
7780
|
+
// - newPageRot = -pageRot (we want to negate the page rotation)
|
|
7781
|
+
// - newPageRot = parentRot + newLocalRot (parent hasn't changed)
|
|
7782
|
+
// - Therefore: newLocalRot = -pageRot - parentRot = -(parentRot + localRot) - parentRot = -localRot - 2*parentRot
|
|
7783
|
+
const parentRotation = this.getShapeParentTransform(id).rotation()
|
|
7784
|
+
const rotation = -options.initialShape.rotation - 2 * parentRotation
|
|
7654
7785
|
this.updateShapes([{ id, type, rotation }])
|
|
7655
7786
|
}
|
|
7656
7787
|
|
|
@@ -7670,9 +7801,13 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
7670
7801
|
)
|
|
7671
7802
|
|
|
7672
7803
|
// now calculate how far away the shape is from where it needs to be
|
|
7673
|
-
const pageBounds = this.getShapePageBounds(id)!
|
|
7674
7804
|
const pageTransform = this.getShapePageTransform(id)!
|
|
7675
|
-
|
|
7805
|
+
// We need to use the local bounds center transformed to page space, not the axis-aligned
|
|
7806
|
+
// page bounds center. This is because the page bounds are axis-aligned and their center
|
|
7807
|
+
// changes when the rotation changes, but we want to use the same reference point as
|
|
7808
|
+
// preScaleShapePageCenter (which used initialBounds.center transformed by the page transform).
|
|
7809
|
+
const currentLocalBounds = this.getShapeGeometry(id).bounds
|
|
7810
|
+
const currentPageCenter = Mat.applyToPoint(pageTransform, currentLocalBounds.center)
|
|
7676
7811
|
const shapePageTransformOrigin = pageTransform.point()
|
|
7677
7812
|
if (!currentPageCenter || !shapePageTransformOrigin) return this
|
|
7678
7813
|
const pageDelta = Vec.Sub(postScaleShapePageCenter, currentPageCenter)
|
|
@@ -8120,7 +8255,12 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8120
8255
|
)
|
|
8121
8256
|
)
|
|
8122
8257
|
const sortedShapeIds = shapesToGroup.sort(sortByIndex).map((s) => s.id)
|
|
8123
|
-
const
|
|
8258
|
+
const childBounds = compact(shapesToGroup.map((shape) => this.getShapePageBounds(shape)))
|
|
8259
|
+
const pageBounds = Box.Common(childBounds)
|
|
8260
|
+
|
|
8261
|
+
if (!pageBounds.isValid()) {
|
|
8262
|
+
throw Error(`Editor.groupShapes: group bounds are invalid (NaN).`)
|
|
8263
|
+
}
|
|
8124
8264
|
|
|
8125
8265
|
const { x, y } = pageBounds.point
|
|
8126
8266
|
|
|
@@ -9145,6 +9285,30 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9145
9285
|
}
|
|
9146
9286
|
}
|
|
9147
9287
|
|
|
9288
|
+
if (point) {
|
|
9289
|
+
const shapesById = new Map<TLShapeId, TLShape>(shapes.map((shape) => [shape.id, shape]))
|
|
9290
|
+
const rootShapesFromContent = compact(rootShapeIds.map((id) => shapesById.get(id)))
|
|
9291
|
+
if (rootShapesFromContent.length > 0) {
|
|
9292
|
+
const targetParent = this.getShapeAtPoint(point, {
|
|
9293
|
+
hitInside: true,
|
|
9294
|
+
hitFrameInside: true,
|
|
9295
|
+
hitLocked: true,
|
|
9296
|
+
filter: (shape) => {
|
|
9297
|
+
const util = this.getShapeUtil(shape)
|
|
9298
|
+
if (!util.canReceiveNewChildrenOfType) return false
|
|
9299
|
+
return rootShapesFromContent.every((rootShape) =>
|
|
9300
|
+
util.canReceiveNewChildrenOfType!(shape, rootShape.type)
|
|
9301
|
+
)
|
|
9302
|
+
},
|
|
9303
|
+
})
|
|
9304
|
+
|
|
9305
|
+
// When pasting at a specific point (e.g. paste-at-cursor) prefer the
|
|
9306
|
+
// parent under the pointer so that we don't keep using the original
|
|
9307
|
+
// selection's parent (which can keep shapes clipped inside frames).
|
|
9308
|
+
pasteParentId = targetParent ? targetParent.id : currentPageId
|
|
9309
|
+
}
|
|
9310
|
+
}
|
|
9311
|
+
|
|
9148
9312
|
let isDuplicating = false
|
|
9149
9313
|
|
|
9150
9314
|
if (!isPageId(pasteParentId)) {
|
|
@@ -9482,126 +9646,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9482
9646
|
|
|
9483
9647
|
/* --------------------- Events --------------------- */
|
|
9484
9648
|
|
|
9485
|
-
/**
|
|
9486
|
-
* The app's current input state.
|
|
9487
|
-
*
|
|
9488
|
-
* @public
|
|
9489
|
-
*/
|
|
9490
|
-
inputs = {
|
|
9491
|
-
/** The most recent pointer down's position in the current page space. */
|
|
9492
|
-
originPagePoint: new Vec(),
|
|
9493
|
-
/** The most recent pointer down's position in screen space. */
|
|
9494
|
-
originScreenPoint: new Vec(),
|
|
9495
|
-
/** The previous pointer position in the current page space. */
|
|
9496
|
-
previousPagePoint: new Vec(),
|
|
9497
|
-
/** The previous pointer position in screen space. */
|
|
9498
|
-
previousScreenPoint: new Vec(),
|
|
9499
|
-
/** The most recent pointer position in the current page space. */
|
|
9500
|
-
currentPagePoint: new Vec(),
|
|
9501
|
-
/** The most recent pointer position in screen space. */
|
|
9502
|
-
currentScreenPoint: new Vec(),
|
|
9503
|
-
/** A set containing the currently pressed keys. */
|
|
9504
|
-
keys: new Set<string>(),
|
|
9505
|
-
/** A set containing the currently pressed buttons. */
|
|
9506
|
-
buttons: new Set<number>(),
|
|
9507
|
-
/** Whether the input is from a pe. */
|
|
9508
|
-
isPen: false,
|
|
9509
|
-
/** Whether the shift key is currently pressed. */
|
|
9510
|
-
shiftKey: false,
|
|
9511
|
-
/** Whether the meta key is currently pressed. */
|
|
9512
|
-
metaKey: false,
|
|
9513
|
-
/** Whether the control or command key is currently pressed. */
|
|
9514
|
-
ctrlKey: false,
|
|
9515
|
-
/** Whether the alt or option key is currently pressed. */
|
|
9516
|
-
altKey: false,
|
|
9517
|
-
/** Whether the user is dragging. */
|
|
9518
|
-
isDragging: false,
|
|
9519
|
-
/** Whether the user is pointing. */
|
|
9520
|
-
isPointing: false,
|
|
9521
|
-
/** Whether the user is pinching. */
|
|
9522
|
-
isPinching: false,
|
|
9523
|
-
/** Whether the user is editing. */
|
|
9524
|
-
isEditing: false,
|
|
9525
|
-
/** Whether the user is panning. */
|
|
9526
|
-
isPanning: false,
|
|
9527
|
-
/** Whether the user is spacebar panning. */
|
|
9528
|
-
isSpacebarPanning: false,
|
|
9529
|
-
/** Velocity of mouse pointer, in pixels per millisecond */
|
|
9530
|
-
pointerVelocity: new Vec(),
|
|
9531
|
-
}
|
|
9532
|
-
|
|
9533
|
-
/**
|
|
9534
|
-
* Update the input points from a pointer, pinch, or wheel event.
|
|
9535
|
-
*
|
|
9536
|
-
* @param info - The event info.
|
|
9537
|
-
*/
|
|
9538
|
-
private _updateInputsFromEvent(
|
|
9539
|
-
info: TLPointerEventInfo | TLPinchEventInfo | TLWheelEventInfo
|
|
9540
|
-
): void {
|
|
9541
|
-
const {
|
|
9542
|
-
pointerVelocity,
|
|
9543
|
-
previousScreenPoint,
|
|
9544
|
-
previousPagePoint,
|
|
9545
|
-
currentScreenPoint,
|
|
9546
|
-
currentPagePoint,
|
|
9547
|
-
originScreenPoint,
|
|
9548
|
-
originPagePoint,
|
|
9549
|
-
} = this.inputs
|
|
9550
|
-
|
|
9551
|
-
const { screenBounds } = this.store.unsafeGetWithoutCapture(TLINSTANCE_ID)!
|
|
9552
|
-
const { x: cx, y: cy, z: cz } = unsafe__withoutCapture(() => this.getCamera())
|
|
9553
|
-
|
|
9554
|
-
const sx = info.point.x - screenBounds.x
|
|
9555
|
-
const sy = info.point.y - screenBounds.y
|
|
9556
|
-
const sz = info.point.z ?? 0.5
|
|
9557
|
-
|
|
9558
|
-
previousScreenPoint.setTo(currentScreenPoint)
|
|
9559
|
-
previousPagePoint.setTo(currentPagePoint)
|
|
9560
|
-
|
|
9561
|
-
// The "screen bounds" is relative to the user's actual screen.
|
|
9562
|
-
// The "screen point" is relative to the "screen bounds";
|
|
9563
|
-
// it will be 0,0 when its actual screen position is equal
|
|
9564
|
-
// to screenBounds.point. This is confusing!
|
|
9565
|
-
currentScreenPoint.set(sx, sy)
|
|
9566
|
-
const nx = sx / cz - cx
|
|
9567
|
-
const ny = sy / cz - cy
|
|
9568
|
-
if (isFinite(nx) && isFinite(ny)) {
|
|
9569
|
-
currentPagePoint.set(nx, ny, sz)
|
|
9570
|
-
}
|
|
9571
|
-
|
|
9572
|
-
this.inputs.isPen = info.type === 'pointer' && info.isPen
|
|
9573
|
-
|
|
9574
|
-
// Reset velocity on pointer down, or when a pinch starts or ends
|
|
9575
|
-
if (info.name === 'pointer_down' || this.inputs.isPinching) {
|
|
9576
|
-
pointerVelocity.set(0, 0)
|
|
9577
|
-
originScreenPoint.setTo(currentScreenPoint)
|
|
9578
|
-
originPagePoint.setTo(currentPagePoint)
|
|
9579
|
-
}
|
|
9580
|
-
|
|
9581
|
-
// todo: We only have to do this if there are multiple users in the document
|
|
9582
|
-
this.run(
|
|
9583
|
-
() => {
|
|
9584
|
-
this.store.put([
|
|
9585
|
-
{
|
|
9586
|
-
id: TLPOINTER_ID,
|
|
9587
|
-
typeName: 'pointer',
|
|
9588
|
-
x: currentPagePoint.x,
|
|
9589
|
-
y: currentPagePoint.y,
|
|
9590
|
-
lastActivityTimestamp:
|
|
9591
|
-
// If our pointer moved only because we're following some other user, then don't
|
|
9592
|
-
// update our last activity timestamp; otherwise, update it to the current timestamp.
|
|
9593
|
-
info.type === 'pointer' && info.pointerId === INTERNAL_POINTER_IDS.CAMERA_MOVE
|
|
9594
|
-
? (this.store.unsafeGetWithoutCapture(TLPOINTER_ID)?.lastActivityTimestamp ??
|
|
9595
|
-
this._tickManager.now)
|
|
9596
|
-
: this._tickManager.now,
|
|
9597
|
-
meta: {},
|
|
9598
|
-
},
|
|
9599
|
-
])
|
|
9600
|
-
},
|
|
9601
|
-
{ history: 'ignore' }
|
|
9602
|
-
)
|
|
9603
|
-
}
|
|
9604
|
-
|
|
9605
9649
|
/**
|
|
9606
9650
|
* Dispatch a cancel event.
|
|
9607
9651
|
*
|
|
@@ -9671,19 +9715,22 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9671
9715
|
// weird but true: what `inputs` calls screen-space is actually viewport space. so
|
|
9672
9716
|
// we need to convert back into true screen space first. we should fix this...
|
|
9673
9717
|
Vec.Add(
|
|
9674
|
-
this.inputs.
|
|
9718
|
+
this.inputs.getCurrentScreenPoint(),
|
|
9675
9719
|
this.store.unsafeGetWithoutCapture(TLINSTANCE_ID)!.screenBounds
|
|
9676
9720
|
),
|
|
9677
9721
|
pointerId: options?.pointerId ?? 0,
|
|
9678
9722
|
button: options?.button ?? 0,
|
|
9679
|
-
isPen: options?.isPen ?? this.inputs.
|
|
9680
|
-
shiftKey: options?.shiftKey ?? this.inputs.
|
|
9681
|
-
altKey: options?.altKey ?? this.inputs.
|
|
9682
|
-
ctrlKey: options?.ctrlKey ?? this.inputs.
|
|
9683
|
-
metaKey: options?.metaKey ?? this.inputs.
|
|
9684
|
-
accelKey:
|
|
9723
|
+
isPen: options?.isPen ?? this.inputs.getIsPen(),
|
|
9724
|
+
shiftKey: options?.shiftKey ?? this.inputs.getShiftKey(),
|
|
9725
|
+
altKey: options?.altKey ?? this.inputs.getAltKey(),
|
|
9726
|
+
ctrlKey: options?.ctrlKey ?? this.inputs.getCtrlKey(),
|
|
9727
|
+
metaKey: options?.metaKey ?? this.inputs.getMetaKey(),
|
|
9728
|
+
accelKey: false,
|
|
9685
9729
|
}
|
|
9686
9730
|
|
|
9731
|
+
// needs to be calculated second
|
|
9732
|
+
event.accelKey = options?.accelKey ?? this.inputs.getAccelKey()
|
|
9733
|
+
|
|
9687
9734
|
if (options?.immediate) {
|
|
9688
9735
|
this._flushEventForTick(event)
|
|
9689
9736
|
} else {
|
|
@@ -10056,16 +10103,16 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10056
10103
|
/** @internal */
|
|
10057
10104
|
@bind
|
|
10058
10105
|
_setShiftKeyTimeout() {
|
|
10059
|
-
this.inputs.
|
|
10106
|
+
this.inputs.setShiftKey(false)
|
|
10060
10107
|
this.dispatch({
|
|
10061
10108
|
type: 'keyboard',
|
|
10062
10109
|
name: 'key_up',
|
|
10063
10110
|
key: 'Shift',
|
|
10064
|
-
shiftKey: this.inputs.
|
|
10065
|
-
ctrlKey: this.inputs.
|
|
10066
|
-
altKey: this.inputs.
|
|
10067
|
-
metaKey: this.inputs.
|
|
10068
|
-
accelKey:
|
|
10111
|
+
shiftKey: this.inputs.getShiftKey(),
|
|
10112
|
+
ctrlKey: this.inputs.getCtrlKey(),
|
|
10113
|
+
altKey: this.inputs.getAltKey(),
|
|
10114
|
+
metaKey: this.inputs.getMetaKey(),
|
|
10115
|
+
accelKey: this.inputs.getAccelKey(),
|
|
10069
10116
|
code: 'ShiftLeft',
|
|
10070
10117
|
})
|
|
10071
10118
|
}
|
|
@@ -10076,16 +10123,16 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10076
10123
|
/** @internal */
|
|
10077
10124
|
@bind
|
|
10078
10125
|
_setAltKeyTimeout() {
|
|
10079
|
-
this.inputs.
|
|
10126
|
+
this.inputs.setAltKey(false)
|
|
10080
10127
|
this.dispatch({
|
|
10081
10128
|
type: 'keyboard',
|
|
10082
10129
|
name: 'key_up',
|
|
10083
10130
|
key: 'Alt',
|
|
10084
|
-
shiftKey: this.inputs.
|
|
10085
|
-
ctrlKey: this.inputs.
|
|
10086
|
-
altKey: this.inputs.
|
|
10087
|
-
metaKey: this.inputs.
|
|
10088
|
-
accelKey:
|
|
10131
|
+
shiftKey: this.inputs.getShiftKey(),
|
|
10132
|
+
ctrlKey: this.inputs.getCtrlKey(),
|
|
10133
|
+
altKey: this.inputs.getAltKey(),
|
|
10134
|
+
metaKey: this.inputs.getMetaKey(),
|
|
10135
|
+
accelKey: this.inputs.getAccelKey(),
|
|
10089
10136
|
code: 'AltLeft',
|
|
10090
10137
|
})
|
|
10091
10138
|
}
|
|
@@ -10096,16 +10143,16 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10096
10143
|
/** @internal */
|
|
10097
10144
|
@bind
|
|
10098
10145
|
_setCtrlKeyTimeout() {
|
|
10099
|
-
this.inputs.
|
|
10146
|
+
this.inputs.setCtrlKey(false)
|
|
10100
10147
|
this.dispatch({
|
|
10101
10148
|
type: 'keyboard',
|
|
10102
10149
|
name: 'key_up',
|
|
10103
10150
|
key: 'Ctrl',
|
|
10104
|
-
shiftKey: this.inputs.
|
|
10105
|
-
ctrlKey: this.inputs.
|
|
10106
|
-
altKey: this.inputs.
|
|
10107
|
-
metaKey: this.inputs.
|
|
10108
|
-
accelKey:
|
|
10151
|
+
shiftKey: this.inputs.getShiftKey(),
|
|
10152
|
+
ctrlKey: this.inputs.getCtrlKey(),
|
|
10153
|
+
altKey: this.inputs.getAltKey(),
|
|
10154
|
+
metaKey: this.inputs.getMetaKey(),
|
|
10155
|
+
accelKey: this.inputs.getAccelKey(),
|
|
10109
10156
|
code: 'ControlLeft',
|
|
10110
10157
|
})
|
|
10111
10158
|
}
|
|
@@ -10116,16 +10163,16 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10116
10163
|
/** @internal */
|
|
10117
10164
|
@bind
|
|
10118
10165
|
_setMetaKeyTimeout() {
|
|
10119
|
-
this.inputs.
|
|
10166
|
+
this.inputs.setMetaKey(false)
|
|
10120
10167
|
this.dispatch({
|
|
10121
10168
|
type: 'keyboard',
|
|
10122
10169
|
name: 'key_up',
|
|
10123
10170
|
key: 'Meta',
|
|
10124
|
-
shiftKey: this.inputs.
|
|
10125
|
-
ctrlKey: this.inputs.
|
|
10126
|
-
altKey: this.inputs.
|
|
10127
|
-
metaKey: this.inputs.
|
|
10128
|
-
accelKey:
|
|
10171
|
+
shiftKey: this.inputs.getShiftKey(),
|
|
10172
|
+
ctrlKey: this.inputs.getCtrlKey(),
|
|
10173
|
+
altKey: this.inputs.getAltKey(),
|
|
10174
|
+
metaKey: this.inputs.getMetaKey(),
|
|
10175
|
+
accelKey: this.inputs.getAccelKey(),
|
|
10129
10176
|
code: 'MetaLeft',
|
|
10130
10177
|
})
|
|
10131
10178
|
}
|
|
@@ -10133,9 +10180,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10133
10180
|
/** @internal */
|
|
10134
10181
|
private _restoreToolId = 'select'
|
|
10135
10182
|
|
|
10136
|
-
/** @internal */
|
|
10137
|
-
private _pinchStart = 1
|
|
10138
|
-
|
|
10139
10183
|
/** @internal */
|
|
10140
10184
|
private _didPinch = false
|
|
10141
10185
|
|
|
@@ -10242,55 +10286,54 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10242
10286
|
if (info.type === 'misc') {
|
|
10243
10287
|
// stop panning if the interaction is cancelled or completed
|
|
10244
10288
|
if (info.name === 'cancel' || info.name === 'complete') {
|
|
10245
|
-
this.inputs.
|
|
10289
|
+
this.inputs.setIsDragging(false)
|
|
10246
10290
|
|
|
10247
|
-
if (this.inputs.
|
|
10248
|
-
this.inputs.
|
|
10249
|
-
this.inputs.
|
|
10291
|
+
if (this.inputs.getIsPanning()) {
|
|
10292
|
+
this.inputs.setIsPanning(false)
|
|
10293
|
+
this.inputs.setIsSpacebarPanning(false)
|
|
10250
10294
|
this.setCursor({ type: this._prevCursor, rotation: 0 })
|
|
10251
10295
|
}
|
|
10252
10296
|
}
|
|
10253
10297
|
|
|
10254
10298
|
this.root.handleEvent(info)
|
|
10299
|
+
this.emit('event', info)
|
|
10255
10300
|
return
|
|
10256
10301
|
}
|
|
10257
10302
|
|
|
10258
10303
|
if (info.shiftKey) {
|
|
10259
10304
|
clearTimeout(this._shiftKeyTimeout)
|
|
10260
10305
|
this._shiftKeyTimeout = -1
|
|
10261
|
-
inputs.
|
|
10262
|
-
} else if (!info.shiftKey && inputs.
|
|
10306
|
+
inputs.setShiftKey(true)
|
|
10307
|
+
} else if (!info.shiftKey && inputs.getShiftKey() && this._shiftKeyTimeout === -1) {
|
|
10263
10308
|
this._shiftKeyTimeout = this.timers.setTimeout(this._setShiftKeyTimeout, 150)
|
|
10264
10309
|
}
|
|
10265
10310
|
|
|
10266
10311
|
if (info.altKey) {
|
|
10267
10312
|
clearTimeout(this._altKeyTimeout)
|
|
10268
10313
|
this._altKeyTimeout = -1
|
|
10269
|
-
inputs.
|
|
10270
|
-
} else if (!info.altKey && inputs.
|
|
10314
|
+
inputs.setAltKey(true)
|
|
10315
|
+
} else if (!info.altKey && inputs.getAltKey() && this._altKeyTimeout === -1) {
|
|
10271
10316
|
this._altKeyTimeout = this.timers.setTimeout(this._setAltKeyTimeout, 150)
|
|
10272
10317
|
}
|
|
10273
10318
|
|
|
10274
10319
|
if (info.ctrlKey) {
|
|
10275
10320
|
clearTimeout(this._ctrlKeyTimeout)
|
|
10276
10321
|
this._ctrlKeyTimeout = -1
|
|
10277
|
-
inputs.
|
|
10278
|
-
} else if (!info.ctrlKey && inputs.
|
|
10322
|
+
inputs.setCtrlKey(true)
|
|
10323
|
+
} else if (!info.ctrlKey && inputs.getCtrlKey() && this._ctrlKeyTimeout === -1) {
|
|
10279
10324
|
this._ctrlKeyTimeout = this.timers.setTimeout(this._setCtrlKeyTimeout, 150)
|
|
10280
10325
|
}
|
|
10281
10326
|
|
|
10282
10327
|
if (info.metaKey) {
|
|
10283
10328
|
clearTimeout(this._metaKeyTimeout)
|
|
10284
10329
|
this._metaKeyTimeout = -1
|
|
10285
|
-
inputs.
|
|
10286
|
-
} else if (!info.metaKey && inputs.
|
|
10330
|
+
inputs.setMetaKey(true)
|
|
10331
|
+
} else if (!info.metaKey && inputs.getMetaKey() && this._metaKeyTimeout === -1) {
|
|
10287
10332
|
this._metaKeyTimeout = this.timers.setTimeout(this._setMetaKeyTimeout, 150)
|
|
10288
10333
|
}
|
|
10289
10334
|
|
|
10290
|
-
|
|
10291
|
-
|
|
10292
|
-
if (!inputs.isPointing) {
|
|
10293
|
-
inputs.isDragging = false
|
|
10335
|
+
if (!inputs.getIsPointing()) {
|
|
10336
|
+
inputs.setIsDragging(false)
|
|
10294
10337
|
}
|
|
10295
10338
|
|
|
10296
10339
|
const instanceState = this.store.unsafeGetWithoutCapture(TLINSTANCE_ID)!
|
|
@@ -10301,29 +10344,29 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10301
10344
|
case 'pinch': {
|
|
10302
10345
|
if (cameraOptions.isLocked) return
|
|
10303
10346
|
clearTimeout(this._longPressTimeout)
|
|
10304
|
-
this.
|
|
10347
|
+
this.inputs.updateFromEvent(info)
|
|
10305
10348
|
|
|
10306
10349
|
switch (info.name) {
|
|
10307
10350
|
case 'pinch_start': {
|
|
10308
|
-
if (inputs.
|
|
10351
|
+
if (inputs.getIsPinching()) return
|
|
10309
10352
|
|
|
10310
|
-
if (!inputs.
|
|
10311
|
-
this._pinchStart = this.getCamera().z
|
|
10353
|
+
if (!inputs.getIsEditing()) {
|
|
10312
10354
|
if (!this._selectedShapeIdsAtPointerDown.length) {
|
|
10313
10355
|
this._selectedShapeIdsAtPointerDown = [...pageState.selectedShapeIds]
|
|
10314
10356
|
}
|
|
10315
10357
|
|
|
10316
10358
|
this._didPinch = true
|
|
10317
10359
|
|
|
10318
|
-
inputs.
|
|
10360
|
+
inputs.setIsPinching(true)
|
|
10319
10361
|
|
|
10320
10362
|
this.interrupt()
|
|
10321
10363
|
}
|
|
10322
10364
|
|
|
10365
|
+
this.emit('event', info)
|
|
10323
10366
|
return // Stop here!
|
|
10324
10367
|
}
|
|
10325
10368
|
case 'pinch': {
|
|
10326
|
-
if (!inputs.
|
|
10369
|
+
if (!inputs.getIsPinching()) return
|
|
10327
10370
|
|
|
10328
10371
|
const {
|
|
10329
10372
|
point: { z = 1 },
|
|
@@ -10354,13 +10397,14 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10354
10397
|
{ immediate: true }
|
|
10355
10398
|
)
|
|
10356
10399
|
|
|
10400
|
+
this.emit('event', info)
|
|
10357
10401
|
return // Stop here!
|
|
10358
10402
|
}
|
|
10359
10403
|
case 'pinch_end': {
|
|
10360
|
-
if (!inputs.
|
|
10404
|
+
if (!inputs.getIsPinching()) return this
|
|
10361
10405
|
|
|
10362
10406
|
// Stop pinching
|
|
10363
|
-
inputs.
|
|
10407
|
+
inputs.setIsPinching(false)
|
|
10364
10408
|
|
|
10365
10409
|
// Stash and clear the shapes that were selected when the pinch started
|
|
10366
10410
|
const { _selectedShapeIdsAtPointerDown: shapesToReselect } = this
|
|
@@ -10380,6 +10424,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10380
10424
|
}
|
|
10381
10425
|
}
|
|
10382
10426
|
|
|
10427
|
+
this.emit('event', info)
|
|
10383
10428
|
return // Stop here!
|
|
10384
10429
|
}
|
|
10385
10430
|
}
|
|
@@ -10387,7 +10432,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10387
10432
|
case 'wheel': {
|
|
10388
10433
|
if (cameraOptions.isLocked) return
|
|
10389
10434
|
|
|
10390
|
-
this.
|
|
10435
|
+
this.inputs.updateFromEvent(info)
|
|
10391
10436
|
|
|
10392
10437
|
const { panSpeed, zoomSpeed } = cameraOptions
|
|
10393
10438
|
let wheelBehavior = cameraOptions.wheelBehavior
|
|
@@ -10418,7 +10463,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10418
10463
|
switch (behavior) {
|
|
10419
10464
|
case 'zoom': {
|
|
10420
10465
|
// Zoom in on current screen point using the wheel delta
|
|
10421
|
-
const { x, y } = this.inputs.
|
|
10466
|
+
const { x, y } = this.inputs.getCurrentScreenPoint()
|
|
10422
10467
|
let delta = dz
|
|
10423
10468
|
|
|
10424
10469
|
// If we're forcing zoom, then we need to do the wheel normalization math here
|
|
@@ -10435,6 +10480,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10435
10480
|
immediate: true,
|
|
10436
10481
|
})
|
|
10437
10482
|
this.maybeTrackPerformance('Zooming')
|
|
10483
|
+
this.root.handleEvent(info)
|
|
10484
|
+
this.emit('event', info)
|
|
10438
10485
|
return
|
|
10439
10486
|
}
|
|
10440
10487
|
case 'pan': {
|
|
@@ -10443,6 +10490,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10443
10490
|
immediate: true,
|
|
10444
10491
|
})
|
|
10445
10492
|
this.maybeTrackPerformance('Panning')
|
|
10493
|
+
this.root.handleEvent(info)
|
|
10494
|
+
this.emit('event', info)
|
|
10446
10495
|
return
|
|
10447
10496
|
}
|
|
10448
10497
|
}
|
|
@@ -10451,9 +10500,9 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10451
10500
|
}
|
|
10452
10501
|
case 'pointer': {
|
|
10453
10502
|
// Ignore pointer events while we're pinching
|
|
10454
|
-
if (inputs.
|
|
10503
|
+
if (inputs.getIsPinching()) return
|
|
10455
10504
|
|
|
10456
|
-
this.
|
|
10505
|
+
this.inputs.updateFromEvent(info)
|
|
10457
10506
|
const { isPen } = info
|
|
10458
10507
|
const { isPenMode } = instanceState
|
|
10459
10508
|
|
|
@@ -10462,7 +10511,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10462
10511
|
// If we're in pen mode and the input is not a pen type, then stop here
|
|
10463
10512
|
if (isPenMode && !isPen) return
|
|
10464
10513
|
|
|
10465
|
-
if (!this.inputs.
|
|
10514
|
+
if (!this.inputs.getIsPanning()) {
|
|
10466
10515
|
// Start a long press timeout
|
|
10467
10516
|
this._longPressTimeout = this.timers.setTimeout(() => {
|
|
10468
10517
|
const vsb = this.getViewportScreenBounds()
|
|
@@ -10472,7 +10521,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10472
10521
|
// viewport bounds, and will be again when this event is handled...
|
|
10473
10522
|
// so we need to counter-adjust from the stored value so that the
|
|
10474
10523
|
// new value is set correctly.
|
|
10475
|
-
point: this.inputs.
|
|
10524
|
+
point: this.inputs.getOriginScreenPoint().clone().addXY(vsb.x, vsb.y),
|
|
10476
10525
|
name: 'long_press',
|
|
10477
10526
|
})
|
|
10478
10527
|
}, this.options.longPressDurationMs)
|
|
@@ -10489,8 +10538,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10489
10538
|
inputs.buttons.add(info.button)
|
|
10490
10539
|
|
|
10491
10540
|
// Start pointing and stop dragging
|
|
10492
|
-
inputs.
|
|
10493
|
-
inputs.
|
|
10541
|
+
inputs.setIsPointing(true)
|
|
10542
|
+
inputs.setIsDragging(false)
|
|
10494
10543
|
|
|
10495
10544
|
// If pen mode is off but we're not already in pen mode, turn that on
|
|
10496
10545
|
if (!isPenMode && isPen) this.updateInstanceState({ isPenMode: true })
|
|
@@ -10502,16 +10551,16 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10502
10551
|
this.setCurrentTool('eraser')
|
|
10503
10552
|
} else if (info.button === MIDDLE_MOUSE_BUTTON) {
|
|
10504
10553
|
// Middle mouse pan activates panning unless we're already panning (with spacebar)
|
|
10505
|
-
if (!this.inputs.
|
|
10554
|
+
if (!this.inputs.getIsPanning()) {
|
|
10506
10555
|
this._prevCursor = this.getInstanceState().cursor.type
|
|
10507
10556
|
}
|
|
10508
|
-
this.inputs.
|
|
10557
|
+
this.inputs.setIsPanning(true)
|
|
10509
10558
|
clearTimeout(this._longPressTimeout)
|
|
10510
10559
|
}
|
|
10511
10560
|
|
|
10512
10561
|
// We might be panning because we did a middle mouse click, or because we're holding spacebar and started a regular click
|
|
10513
10562
|
// Also stop here, we don't want the state chart to receive the event
|
|
10514
|
-
if (this.inputs.
|
|
10563
|
+
if (this.inputs.getIsPanning()) {
|
|
10515
10564
|
this.stopCameraAnimation()
|
|
10516
10565
|
this.setCursor({ type: 'grabbing', rotation: 0 })
|
|
10517
10566
|
return this
|
|
@@ -10526,9 +10575,10 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10526
10575
|
const { x: cx, y: cy, z: cz } = unsafe__withoutCapture(() => this.getCamera())
|
|
10527
10576
|
|
|
10528
10577
|
// If we've started panning, then clear any long press timeout
|
|
10529
|
-
if (this.inputs.
|
|
10578
|
+
if (this.inputs.getIsPanning() && this.inputs.getIsPointing()) {
|
|
10530
10579
|
// Handle spacebar / middle mouse button panning
|
|
10531
|
-
const
|
|
10580
|
+
const currentScreenPoint = this.inputs.getCurrentScreenPoint()
|
|
10581
|
+
const previousScreenPoint = this.inputs.getPreviousScreenPoint()
|
|
10532
10582
|
const offset = Vec.Sub(currentScreenPoint, previousScreenPoint)
|
|
10533
10583
|
this.setCamera(new Vec(cx + offset.x / cz, cy + offset.y / cz, cz), {
|
|
10534
10584
|
immediate: true,
|
|
@@ -10538,24 +10588,25 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10538
10588
|
}
|
|
10539
10589
|
|
|
10540
10590
|
if (
|
|
10541
|
-
inputs.
|
|
10542
|
-
!inputs.
|
|
10543
|
-
Vec.Dist2(
|
|
10591
|
+
inputs.getIsPointing() &&
|
|
10592
|
+
!inputs.getIsDragging() &&
|
|
10593
|
+
Vec.Dist2(inputs.getOriginPagePoint(), inputs.getCurrentPagePoint()) *
|
|
10594
|
+
this.getZoomLevel() >
|
|
10544
10595
|
(instanceState.isCoarsePointer
|
|
10545
10596
|
? this.options.coarseDragDistanceSquared
|
|
10546
10597
|
: this.options.dragDistanceSquared) /
|
|
10547
10598
|
cz
|
|
10548
10599
|
) {
|
|
10549
10600
|
// Start dragging
|
|
10550
|
-
inputs.
|
|
10601
|
+
inputs.setIsDragging(true)
|
|
10551
10602
|
clearTimeout(this._longPressTimeout)
|
|
10552
10603
|
}
|
|
10553
10604
|
break
|
|
10554
10605
|
}
|
|
10555
10606
|
case 'pointer_up': {
|
|
10556
10607
|
// Stop dragging / pointing
|
|
10557
|
-
inputs.
|
|
10558
|
-
inputs.
|
|
10608
|
+
inputs.setIsDragging(false)
|
|
10609
|
+
inputs.setIsPointing(false)
|
|
10559
10610
|
clearTimeout(this._longPressTimeout)
|
|
10560
10611
|
|
|
10561
10612
|
// Remove the button from the buttons set
|
|
@@ -10572,12 +10623,12 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10572
10623
|
info.button = 0
|
|
10573
10624
|
}
|
|
10574
10625
|
|
|
10575
|
-
if (inputs.
|
|
10626
|
+
if (inputs.getIsPanning()) {
|
|
10576
10627
|
if (!inputs.keys.has('Space')) {
|
|
10577
|
-
inputs.
|
|
10578
|
-
inputs.
|
|
10628
|
+
inputs.setIsPanning(false)
|
|
10629
|
+
inputs.setIsSpacebarPanning(false)
|
|
10579
10630
|
}
|
|
10580
|
-
const slideDirection = this.inputs.
|
|
10631
|
+
const slideDirection = this.inputs.getPointerVelocity()
|
|
10581
10632
|
const slideSpeed = Math.min(2, slideDirection.len())
|
|
10582
10633
|
|
|
10583
10634
|
switch (info.button) {
|
|
@@ -10621,43 +10672,48 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10621
10672
|
// Add the key from the keys set
|
|
10622
10673
|
inputs.keys.add(info.code)
|
|
10623
10674
|
|
|
10624
|
-
|
|
10625
|
-
|
|
10626
|
-
if (!
|
|
10627
|
-
this.
|
|
10628
|
-
|
|
10675
|
+
if (this.options.spacebarPanning) {
|
|
10676
|
+
// If the space key is pressed (but meta / control isn't!) activate panning
|
|
10677
|
+
if (info.code === 'Space' && !info.ctrlKey) {
|
|
10678
|
+
if (!this.inputs.getIsPanning()) {
|
|
10679
|
+
this._prevCursor = instanceState.cursor.type
|
|
10680
|
+
}
|
|
10629
10681
|
|
|
10630
|
-
|
|
10631
|
-
|
|
10632
|
-
|
|
10633
|
-
|
|
10634
|
-
|
|
10682
|
+
this.inputs.setIsPanning(true)
|
|
10683
|
+
this.inputs.setIsSpacebarPanning(true)
|
|
10684
|
+
clearTimeout(this._longPressTimeout)
|
|
10685
|
+
this.setCursor({
|
|
10686
|
+
type: this.inputs.getIsPointing() ? 'grabbing' : 'grab',
|
|
10687
|
+
rotation: 0,
|
|
10688
|
+
})
|
|
10689
|
+
}
|
|
10635
10690
|
|
|
10636
|
-
|
|
10637
|
-
|
|
10638
|
-
|
|
10639
|
-
|
|
10640
|
-
|
|
10641
|
-
|
|
10642
|
-
|
|
10643
|
-
|
|
10644
|
-
|
|
10645
|
-
|
|
10646
|
-
|
|
10647
|
-
|
|
10648
|
-
|
|
10649
|
-
|
|
10650
|
-
|
|
10651
|
-
|
|
10652
|
-
|
|
10653
|
-
|
|
10691
|
+
if (this.inputs.getIsSpacebarPanning()) {
|
|
10692
|
+
let offset: Vec | undefined
|
|
10693
|
+
switch (info.code) {
|
|
10694
|
+
case 'ArrowUp': {
|
|
10695
|
+
offset = new Vec(0, -1)
|
|
10696
|
+
break
|
|
10697
|
+
}
|
|
10698
|
+
case 'ArrowRight': {
|
|
10699
|
+
offset = new Vec(1, 0)
|
|
10700
|
+
break
|
|
10701
|
+
}
|
|
10702
|
+
case 'ArrowDown': {
|
|
10703
|
+
offset = new Vec(0, 1)
|
|
10704
|
+
break
|
|
10705
|
+
}
|
|
10706
|
+
case 'ArrowLeft': {
|
|
10707
|
+
offset = new Vec(-1, 0)
|
|
10708
|
+
break
|
|
10709
|
+
}
|
|
10654
10710
|
}
|
|
10655
|
-
}
|
|
10656
10711
|
|
|
10657
|
-
|
|
10658
|
-
|
|
10659
|
-
|
|
10660
|
-
|
|
10712
|
+
if (offset) {
|
|
10713
|
+
const bounds = this.getViewportPageBounds()
|
|
10714
|
+
const next = bounds.clone().translate(offset.mulV({ x: bounds.w, y: bounds.h }))
|
|
10715
|
+
this._animateToViewport(next, { animation: { duration: 320 } })
|
|
10716
|
+
}
|
|
10661
10717
|
}
|
|
10662
10718
|
}
|
|
10663
10719
|
|
|
@@ -10667,15 +10723,17 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10667
10723
|
// Remove the key from the keys set
|
|
10668
10724
|
inputs.keys.delete(info.code)
|
|
10669
10725
|
|
|
10670
|
-
|
|
10671
|
-
|
|
10672
|
-
if (
|
|
10673
|
-
|
|
10674
|
-
|
|
10675
|
-
|
|
10676
|
-
|
|
10677
|
-
|
|
10678
|
-
|
|
10726
|
+
if (this.options.spacebarPanning) {
|
|
10727
|
+
// If we've lifted the space key,
|
|
10728
|
+
if (info.code === 'Space') {
|
|
10729
|
+
if (this.inputs.buttons.has(MIDDLE_MOUSE_BUTTON)) {
|
|
10730
|
+
// If we're still middle dragging, continue panning
|
|
10731
|
+
} else {
|
|
10732
|
+
// otherwise, stop panning
|
|
10733
|
+
this.inputs.setIsPanning(false)
|
|
10734
|
+
this.inputs.setIsSpacebarPanning(false)
|
|
10735
|
+
this.setCursor({ type: this._prevCursor, rotation: 0 })
|
|
10736
|
+
}
|
|
10679
10737
|
}
|
|
10680
10738
|
}
|
|
10681
10739
|
break
|