@tldraw/editor 4.3.0-next.f4772c19540d → 4.4.0-canary.1e3b436e33e4
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 +503 -155
- package/dist-cjs/index.js +8 -1
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/components/ErrorBoundary.js.map +1 -1
- package/dist-cjs/lib/components/GeometryDebuggingView.js +1 -17
- package/dist-cjs/lib/components/GeometryDebuggingView.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js +4 -5
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
- package/dist-cjs/lib/constants.js +1 -3
- package/dist-cjs/lib/constants.js.map +2 -2
- package/dist-cjs/lib/editor/Editor.js +346 -291
- package/dist-cjs/lib/editor/Editor.js.map +2 -2
- package/dist-cjs/lib/editor/bindings/BindingUtil.js.map +2 -2
- package/dist-cjs/lib/editor/derivations/bindingsIndex.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/BaseBoxShapeUtil.js.map +1 -1
- 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/shapes/group/GroupShapeUtil.js.map +2 -2
- package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/BaseBoxShapeTool.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/getSvgJsx.js.map +2 -2
- package/dist-cjs/lib/exports/parseCss.js +1 -1
- package/dist-cjs/lib/exports/parseCss.js.map +2 -2
- package/dist-cjs/lib/globals/environment.js +45 -9
- package/dist-cjs/lib/globals/environment.js.map +2 -2
- package/dist-cjs/lib/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/reparenting.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 +503 -155
- package/dist-esm/index.mjs +9 -2
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/components/ErrorBoundary.mjs.map +1 -1
- package/dist-esm/lib/components/GeometryDebuggingView.mjs +1 -17
- package/dist-esm/lib/components/GeometryDebuggingView.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +4 -5
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
- package/dist-esm/lib/constants.mjs +1 -3
- package/dist-esm/lib/constants.mjs.map +2 -2
- package/dist-esm/lib/editor/Editor.mjs +347 -294
- package/dist-esm/lib/editor/Editor.mjs.map +2 -2
- package/dist-esm/lib/editor/bindings/BindingUtil.mjs.map +2 -2
- package/dist-esm/lib/editor/derivations/bindingsIndex.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/BaseBoxShapeUtil.mjs.map +1 -1
- 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/shapes/group/GroupShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/editor/tools/BaseBoxShapeTool/BaseBoxShapeTool.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/getSvgJsx.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/reparenting.mjs.map +2 -2
- package/dist-esm/lib/utils/rotation.mjs +1 -1
- package/dist-esm/lib/utils/rotation.mjs.map +2 -2
- package/dist-esm/version.mjs +3 -3
- package/dist-esm/version.mjs.map +1 -1
- package/editor.css +14 -12
- package/package.json +21 -17
- package/src/index.ts +5 -1
- package/src/lib/components/ErrorBoundary.tsx +1 -1
- package/src/lib/components/GeometryDebuggingView.tsx +1 -19
- package/src/lib/components/default-components/DefaultCanvas.tsx +5 -8
- package/src/lib/config/TLUserPreferences.test.ts +40 -0
- package/src/lib/constants.ts +0 -2
- package/src/lib/editor/Editor.test.ts +150 -10
- package/src/lib/editor/Editor.ts +533 -384
- package/src/lib/editor/bindings/BindingUtil.ts +15 -9
- package/src/lib/editor/derivations/bindingsIndex.ts +2 -2
- 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/FontManager/FontManager.test.ts +14 -4
- 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 +4 -4
- 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/BaseBoxShapeUtil.tsx +2 -2
- package/src/lib/editor/shapes/ShapeUtil.ts +72 -32
- package/src/lib/editor/shapes/group/DashedOutlineBox.tsx +1 -1
- package/src/lib/editor/shapes/group/GroupShapeUtil.tsx +1 -3
- package/src/lib/editor/tools/BaseBoxShapeTool/BaseBoxShapeTool.ts +2 -1
- package/src/lib/editor/tools/BaseBoxShapeTool/children/Pointing.ts +6 -6
- package/src/lib/editor/types/emit-types.ts +3 -1
- package/src/lib/exports/getSvgJsx.test.ts +10 -19
- package/src/lib/exports/getSvgJsx.tsx +2 -5
- 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/reparenting.ts +5 -5
- package/src/lib/utils/rotation.ts +1 -1
- package/src/version.ts +3 -3
package/src/lib/editor/Editor.ts
CHANGED
|
@@ -21,7 +21,6 @@ import {
|
|
|
21
21
|
PageRecordType,
|
|
22
22
|
StyleProp,
|
|
23
23
|
StylePropValue,
|
|
24
|
-
TLArrowShape,
|
|
25
24
|
TLAsset,
|
|
26
25
|
TLAssetId,
|
|
27
26
|
TLAssetPartial,
|
|
@@ -30,12 +29,12 @@ import {
|
|
|
30
29
|
TLBindingId,
|
|
31
30
|
TLBindingUpdate,
|
|
32
31
|
TLCamera,
|
|
32
|
+
TLCreateShapePartial,
|
|
33
33
|
TLCursor,
|
|
34
34
|
TLCursorType,
|
|
35
35
|
TLDOCUMENT_ID,
|
|
36
36
|
TLDocument,
|
|
37
37
|
TLFrameShape,
|
|
38
|
-
TLGeoShape,
|
|
39
38
|
TLGroupShape,
|
|
40
39
|
TLHandle,
|
|
41
40
|
TLINSTANCE_ID,
|
|
@@ -43,8 +42,6 @@ import {
|
|
|
43
42
|
TLInstance,
|
|
44
43
|
TLInstancePageState,
|
|
45
44
|
TLInstancePresence,
|
|
46
|
-
TLNoteShape,
|
|
47
|
-
TLPOINTER_ID,
|
|
48
45
|
TLPage,
|
|
49
46
|
TLPageId,
|
|
50
47
|
TLParentId,
|
|
@@ -54,8 +51,6 @@ import {
|
|
|
54
51
|
TLShapePartial,
|
|
55
52
|
TLStore,
|
|
56
53
|
TLStoreSnapshot,
|
|
57
|
-
TLUnknownBinding,
|
|
58
|
-
TLUnknownShape,
|
|
59
54
|
TLVideoAsset,
|
|
60
55
|
createBindingId,
|
|
61
56
|
createShapeId,
|
|
@@ -113,7 +108,6 @@ import {
|
|
|
113
108
|
MIDDLE_MOUSE_BUTTON,
|
|
114
109
|
RIGHT_MOUSE_BUTTON,
|
|
115
110
|
STYLUS_ERASER_BUTTON,
|
|
116
|
-
ZOOM_TO_FIT_PADDING,
|
|
117
111
|
} from '../constants'
|
|
118
112
|
import { exportToSvg } from '../exports/exportToSvg'
|
|
119
113
|
import { getSvgAsImage } from '../exports/getSvgAsImage'
|
|
@@ -139,7 +133,6 @@ import {
|
|
|
139
133
|
parseDeepLinkString,
|
|
140
134
|
} from '../utils/deepLinks'
|
|
141
135
|
import { getIncrementedName } from '../utils/getIncrementedName'
|
|
142
|
-
import { isAccelKey } from '../utils/keyboard'
|
|
143
136
|
import { getReorderingShapesChanges } from '../utils/reorderShapes'
|
|
144
137
|
import { TLTextOptions, TiptapEditor } from '../utils/richText'
|
|
145
138
|
import { applyRotationToSnapshotShapes, getRotationSnapshot } from '../utils/rotation'
|
|
@@ -153,22 +146,19 @@ import { EdgeScrollManager } from './managers/EdgeScrollManager/EdgeScrollManage
|
|
|
153
146
|
import { FocusManager } from './managers/FocusManager/FocusManager'
|
|
154
147
|
import { FontManager } from './managers/FontManager/FontManager'
|
|
155
148
|
import { HistoryManager } from './managers/HistoryManager/HistoryManager'
|
|
149
|
+
import { InputsManager } from './managers/InputsManager/InputsManager'
|
|
156
150
|
import { ScribbleManager } from './managers/ScribbleManager/ScribbleManager'
|
|
157
151
|
import { SnapManager } from './managers/SnapManager/SnapManager'
|
|
152
|
+
import { SpatialIndexManager } from './managers/SpatialIndexManager/SpatialIndexManager'
|
|
158
153
|
import { TextManager } from './managers/TextManager/TextManager'
|
|
159
154
|
import { TickManager } from './managers/TickManager/TickManager'
|
|
160
155
|
import { UserPreferencesManager } from './managers/UserPreferencesManager/UserPreferencesManager'
|
|
161
|
-
import { ShapeUtil, TLGeometryOpts, TLResizeMode } from './shapes/ShapeUtil'
|
|
156
|
+
import { ShapeUtil, TLEditStartInfo, TLGeometryOpts, TLResizeMode } from './shapes/ShapeUtil'
|
|
162
157
|
import { RootState } from './tools/RootState'
|
|
163
158
|
import { StateNode, TLStateNodeConstructor } from './tools/StateNode'
|
|
164
159
|
import { TLContent } from './types/clipboard-types'
|
|
165
160
|
import { TLEventMap } from './types/emit-types'
|
|
166
|
-
import {
|
|
167
|
-
TLEventInfo,
|
|
168
|
-
TLPinchEventInfo,
|
|
169
|
-
TLPointerEventInfo,
|
|
170
|
-
TLWheelEventInfo,
|
|
171
|
-
} from './types/event-types'
|
|
161
|
+
import { TLEventInfo, TLPointerEventInfo } from './types/event-types'
|
|
172
162
|
import { TLExternalAsset, TLExternalContent } from './types/external-content'
|
|
173
163
|
import { TLHistoryBatchOptions } from './types/history-types'
|
|
174
164
|
import {
|
|
@@ -199,7 +189,7 @@ export type TLResizeShapeOptions = Partial<{
|
|
|
199
189
|
/** @public */
|
|
200
190
|
export interface TLEditorOptions {
|
|
201
191
|
/**
|
|
202
|
-
* 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
|
|
203
193
|
* from a server or database.
|
|
204
194
|
*/
|
|
205
195
|
store: TLStore
|
|
@@ -319,6 +309,9 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
319
309
|
|
|
320
310
|
this.snaps = new SnapManager(this)
|
|
321
311
|
|
|
312
|
+
this._spatialIndex = new SpatialIndexManager(this)
|
|
313
|
+
this.disposables.add(() => this._spatialIndex.dispose())
|
|
314
|
+
|
|
322
315
|
this.disposables.add(this.timers.dispose)
|
|
323
316
|
|
|
324
317
|
this._cameraOptions.set({ ...DEFAULT_CAMERA_OPTIONS, ...cameraOptions })
|
|
@@ -337,6 +330,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
337
330
|
|
|
338
331
|
this._tickManager = new TickManager(this)
|
|
339
332
|
|
|
333
|
+
this.inputs = new InputsManager(this)
|
|
334
|
+
|
|
340
335
|
class NewRoot extends RootState {
|
|
341
336
|
static override initial = initialState ?? ''
|
|
342
337
|
}
|
|
@@ -447,7 +442,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
447
442
|
let deletedBindings = new Map<TLBindingId, BindingOnDeleteOptions<any>>()
|
|
448
443
|
const deletedShapeIds = new Set<TLShapeId>()
|
|
449
444
|
const invalidParents = new Set<TLShapeId>()
|
|
450
|
-
let invalidBindingTypes = new Set<
|
|
445
|
+
let invalidBindingTypes = new Set<TLBinding['type']>()
|
|
451
446
|
this.disposables.add(
|
|
452
447
|
this.sideEffects.registerOperationCompleteHandler(() => {
|
|
453
448
|
// this needs to be cleared here because further effects may delete more shapes
|
|
@@ -710,7 +705,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
710
705
|
if (filtered.length > 0) {
|
|
711
706
|
const commonGroupAncestor = this.findCommonAncestor(
|
|
712
707
|
compact(filtered.map((id) => this.getShape(id))),
|
|
713
|
-
(shape) => this.isShapeOfType
|
|
708
|
+
(shape) => this.isShapeOfType(shape, 'group')
|
|
714
709
|
)
|
|
715
710
|
|
|
716
711
|
if (commonGroupAncestor) {
|
|
@@ -871,7 +866,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
871
866
|
}
|
|
872
867
|
|
|
873
868
|
/**
|
|
874
|
-
* A set of functions to call when the
|
|
869
|
+
* A set of functions to call when the editor is disposed.
|
|
875
870
|
*
|
|
876
871
|
* @public
|
|
877
872
|
*/
|
|
@@ -884,16 +879,28 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
884
879
|
*/
|
|
885
880
|
isDisposed = false
|
|
886
881
|
|
|
887
|
-
/**
|
|
888
|
-
|
|
882
|
+
/**
|
|
883
|
+
* A manager for the editor's tick events.
|
|
884
|
+
*
|
|
885
|
+
* @internal */
|
|
886
|
+
private readonly _tickManager: TickManager
|
|
889
887
|
|
|
890
888
|
/**
|
|
891
|
-
* A manager for the
|
|
889
|
+
* A manager for the editor's input state.
|
|
890
|
+
*
|
|
891
|
+
* @public
|
|
892
|
+
*/
|
|
893
|
+
readonly inputs: InputsManager
|
|
894
|
+
|
|
895
|
+
/**
|
|
896
|
+
* A manager for the editor's snapping feature.
|
|
892
897
|
*
|
|
893
898
|
* @public
|
|
894
899
|
*/
|
|
895
900
|
readonly snaps: SnapManager
|
|
896
901
|
|
|
902
|
+
private readonly _spatialIndex: SpatialIndexManager
|
|
903
|
+
|
|
897
904
|
/**
|
|
898
905
|
* A manager for the any asynchronous events and making sure they're
|
|
899
906
|
* cleaned up upon disposal.
|
|
@@ -973,6 +980,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
973
980
|
this.disposables.clear()
|
|
974
981
|
this.store.dispose()
|
|
975
982
|
this.isDisposed = true
|
|
983
|
+
this.emit('dispose')
|
|
976
984
|
}
|
|
977
985
|
|
|
978
986
|
/* ------------------- Shape Utils ------------------ */
|
|
@@ -982,7 +990,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
982
990
|
*
|
|
983
991
|
* @public
|
|
984
992
|
*/
|
|
985
|
-
shapeUtils: { readonly [K in string]?: ShapeUtil<
|
|
993
|
+
shapeUtils: { readonly [K in string]?: ShapeUtil<TLShape> }
|
|
986
994
|
|
|
987
995
|
styleProps: { [key: string]: Map<StyleProp<any>, string> }
|
|
988
996
|
|
|
@@ -1001,8 +1009,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1001
1009
|
*
|
|
1002
1010
|
* @public
|
|
1003
1011
|
*/
|
|
1004
|
-
getShapeUtil<
|
|
1005
|
-
getShapeUtil<S extends
|
|
1012
|
+
getShapeUtil<K extends TLShape['type']>(type: K): ShapeUtil<Extract<TLShape, { type: K }>>
|
|
1013
|
+
getShapeUtil<S extends TLShape>(shape: S | TLShapePartial<S> | S['type']): ShapeUtil<S>
|
|
1006
1014
|
getShapeUtil<T extends ShapeUtil>(type: T extends ShapeUtil<infer R> ? R['type'] : string): T
|
|
1007
1015
|
getShapeUtil(arg: string | { type: string }) {
|
|
1008
1016
|
const type = typeof arg === 'string' ? arg : arg.type
|
|
@@ -1016,8 +1024,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1016
1024
|
*
|
|
1017
1025
|
* @param shape - A shape, shape partial, or shape type.
|
|
1018
1026
|
*/
|
|
1019
|
-
hasShapeUtil
|
|
1020
|
-
hasShapeUtil
|
|
1027
|
+
hasShapeUtil(shape: TLShape | TLShapePartial<TLShape>): boolean
|
|
1028
|
+
hasShapeUtil(type: TLShape['type']): boolean
|
|
1021
1029
|
hasShapeUtil<T extends ShapeUtil>(
|
|
1022
1030
|
type: T extends ShapeUtil<infer R> ? R['type'] : string
|
|
1023
1031
|
): boolean
|
|
@@ -1032,7 +1040,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1032
1040
|
*
|
|
1033
1041
|
* @public
|
|
1034
1042
|
*/
|
|
1035
|
-
bindingUtils: { readonly [K in string]?: BindingUtil<
|
|
1043
|
+
bindingUtils: { readonly [K in string]?: BindingUtil<TLBinding> }
|
|
1036
1044
|
|
|
1037
1045
|
/**
|
|
1038
1046
|
* Get a binding util from a binding itself.
|
|
@@ -1049,8 +1057,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1049
1057
|
*
|
|
1050
1058
|
* @public
|
|
1051
1059
|
*/
|
|
1052
|
-
getBindingUtil<
|
|
1053
|
-
getBindingUtil<S extends
|
|
1060
|
+
getBindingUtil<K extends TLBinding['type']>(type: K): BindingUtil<Extract<TLBinding, { type: K }>>
|
|
1061
|
+
getBindingUtil<S extends TLBinding>(binding: S | { type: S['type'] }): BindingUtil<S>
|
|
1054
1062
|
getBindingUtil<T extends BindingUtil>(
|
|
1055
1063
|
type: T extends BindingUtil<infer R> ? R['type'] : string
|
|
1056
1064
|
): T
|
|
@@ -1064,7 +1072,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1064
1072
|
/* --------------------- History -------------------- */
|
|
1065
1073
|
|
|
1066
1074
|
/**
|
|
1067
|
-
* A manager for the
|
|
1075
|
+
* A manager for the editor's history.
|
|
1068
1076
|
*
|
|
1069
1077
|
* @readonly
|
|
1070
1078
|
*/
|
|
@@ -1088,14 +1096,18 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1088
1096
|
}
|
|
1089
1097
|
|
|
1090
1098
|
/**
|
|
1091
|
-
* Whether the
|
|
1099
|
+
* Whether the editor can undo.
|
|
1092
1100
|
*
|
|
1093
1101
|
* @public
|
|
1094
1102
|
*/
|
|
1095
|
-
@computed
|
|
1103
|
+
@computed canUndo(): boolean {
|
|
1096
1104
|
return this.history.getNumUndos() > 0
|
|
1097
1105
|
}
|
|
1098
1106
|
|
|
1107
|
+
getCanUndo() {
|
|
1108
|
+
return this.canUndo()
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1099
1111
|
/**
|
|
1100
1112
|
* Redo to the next mark.
|
|
1101
1113
|
*
|
|
@@ -1113,20 +1125,24 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1113
1125
|
return this
|
|
1114
1126
|
}
|
|
1115
1127
|
|
|
1116
|
-
clearHistory() {
|
|
1117
|
-
this.history.clear()
|
|
1118
|
-
return this
|
|
1119
|
-
}
|
|
1120
|
-
|
|
1121
1128
|
/**
|
|
1122
|
-
* Whether the
|
|
1129
|
+
* Whether the editor can redo.
|
|
1123
1130
|
*
|
|
1124
1131
|
* @public
|
|
1125
1132
|
*/
|
|
1126
|
-
@computed
|
|
1133
|
+
@computed canRedo(): boolean {
|
|
1127
1134
|
return this.history.getNumRedos() > 0
|
|
1128
1135
|
}
|
|
1129
1136
|
|
|
1137
|
+
getCanRedo() {
|
|
1138
|
+
return this.canRedo()
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
clearHistory() {
|
|
1142
|
+
this.history.clear()
|
|
1143
|
+
return this
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1130
1146
|
/**
|
|
1131
1147
|
* Create a new "mark", or stopping point, in the undo redo history. Creating a mark will clear
|
|
1132
1148
|
* any redos. You typically want to do this just before a user interaction begins or is handled.
|
|
@@ -1300,7 +1316,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1300
1316
|
}),
|
|
1301
1317
|
selectionCount: this.getSelectedShapes().length,
|
|
1302
1318
|
editingShape: editingShapeId ? this.getShape(editingShapeId) : undefined,
|
|
1303
|
-
inputs: this.inputs,
|
|
1319
|
+
inputs: this.inputs.toJson(),
|
|
1304
1320
|
pageState: this.getCurrentPageState(),
|
|
1305
1321
|
instanceState: this.getInstanceState(),
|
|
1306
1322
|
collaboratorCount: this.getCollaboratorsOnCurrentPage().length,
|
|
@@ -1325,7 +1341,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1325
1341
|
* we're in a transaction that's about to be rolled back due to the same error we're currently
|
|
1326
1342
|
* reporting.
|
|
1327
1343
|
*
|
|
1328
|
-
* 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.
|
|
1329
1345
|
*
|
|
1330
1346
|
* @internal
|
|
1331
1347
|
*/
|
|
@@ -2028,7 +2044,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2028
2044
|
}
|
|
2029
2045
|
|
|
2030
2046
|
/**
|
|
2031
|
-
* The id of the
|
|
2047
|
+
* The id of the editor's only selected shape.
|
|
2032
2048
|
*
|
|
2033
2049
|
* @returns Null if there is no shape or more than one selected shape, otherwise the selected shape's id.
|
|
2034
2050
|
*
|
|
@@ -2040,7 +2056,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2040
2056
|
}
|
|
2041
2057
|
|
|
2042
2058
|
/**
|
|
2043
|
-
* The
|
|
2059
|
+
* The editor's only selected shape.
|
|
2044
2060
|
*
|
|
2045
2061
|
* @returns Null if there is no shape or more than one selected shape, otherwise the selected shape.
|
|
2046
2062
|
*
|
|
@@ -2220,7 +2236,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2220
2236
|
throw Error(`Editor.setFocusedGroup: Shape with id ${id} does not exist`)
|
|
2221
2237
|
}
|
|
2222
2238
|
|
|
2223
|
-
if (!this.isShapeOfType
|
|
2239
|
+
if (!this.isShapeOfType(shape, 'group')) {
|
|
2224
2240
|
throw Error(
|
|
2225
2241
|
`Editor.setFocusedGroup: Cannot set focused group to shape of type ${shape.type}`
|
|
2226
2242
|
)
|
|
@@ -2248,7 +2264,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2248
2264
|
if (focusedGroup) {
|
|
2249
2265
|
// If we have a focused layer, look for an ancestor of the focused shape that is a group
|
|
2250
2266
|
const match = this.findShapeAncestor(focusedGroup, (shape) =>
|
|
2251
|
-
this.isShapeOfType
|
|
2267
|
+
this.isShapeOfType(shape, 'group')
|
|
2252
2268
|
)
|
|
2253
2269
|
// If we have an ancestor that can become a focused layer, set it as the focused layer
|
|
2254
2270
|
this.setFocusedGroup(match?.id ?? null)
|
|
@@ -2281,6 +2297,29 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2281
2297
|
return editingShapeId ? this.getShape(editingShapeId) : undefined
|
|
2282
2298
|
}
|
|
2283
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
|
+
|
|
2284
2323
|
/**
|
|
2285
2324
|
* Set the current editing shape.
|
|
2286
2325
|
*
|
|
@@ -2296,44 +2335,59 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2296
2335
|
*/
|
|
2297
2336
|
setEditingShape(shape: TLShapeId | TLShape | null): this {
|
|
2298
2337
|
const id = typeof shape === 'string' ? shape : (shape?.id ?? null)
|
|
2299
|
-
this.setRichTextEditor(null)
|
|
2300
|
-
const prevEditingShapeId = this.getEditingShapeId()
|
|
2301
|
-
if (id !== prevEditingShapeId) {
|
|
2302
|
-
if (id) {
|
|
2303
|
-
const shape = this.getShape(id)
|
|
2304
|
-
if (shape && this.getShapeUtil(shape).canEdit(shape)) {
|
|
2305
|
-
this.run(
|
|
2306
|
-
() => {
|
|
2307
|
-
this._updateCurrentPageState({ editingShapeId: id })
|
|
2308
|
-
if (prevEditingShapeId) {
|
|
2309
|
-
const prevEditingShape = this.getShape(prevEditingShapeId)
|
|
2310
|
-
if (prevEditingShape) {
|
|
2311
|
-
this.getShapeUtil(prevEditingShape).onEditEnd?.(prevEditingShape)
|
|
2312
|
-
}
|
|
2313
|
-
}
|
|
2314
|
-
this.getShapeUtil(shape).onEditStart?.(shape)
|
|
2315
|
-
},
|
|
2316
|
-
{ history: 'ignore' }
|
|
2317
|
-
)
|
|
2318
|
-
return this
|
|
2319
|
-
}
|
|
2320
|
-
}
|
|
2321
2338
|
|
|
2322
|
-
|
|
2339
|
+
if (!id) {
|
|
2340
|
+
// setting the editing shape to null
|
|
2323
2341
|
this.run(
|
|
2324
2342
|
() => {
|
|
2325
|
-
|
|
2326
|
-
this.
|
|
2343
|
+
// Clean up the previous editing shape
|
|
2344
|
+
const prevEditingShapeId = this.getEditingShapeId()
|
|
2327
2345
|
if (prevEditingShapeId) {
|
|
2328
2346
|
const prevEditingShape = this.getShape(prevEditingShapeId)
|
|
2329
2347
|
if (prevEditingShape) {
|
|
2330
2348
|
this.getShapeUtil(prevEditingShape).onEditEnd?.(prevEditingShape)
|
|
2331
2349
|
}
|
|
2332
2350
|
}
|
|
2351
|
+
|
|
2352
|
+
// Clean up the editing shape state and rich text editor
|
|
2353
|
+
this._updateCurrentPageState({ editingShapeId: null })
|
|
2354
|
+
this._currentRichTextEditor.set(null)
|
|
2333
2355
|
},
|
|
2334
2356
|
{ history: 'ignore' }
|
|
2335
2357
|
)
|
|
2358
|
+
|
|
2359
|
+
return this
|
|
2336
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
|
+
|
|
2337
2391
|
return this
|
|
2338
2392
|
}
|
|
2339
2393
|
|
|
@@ -2537,6 +2591,26 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2537
2591
|
return this.getCurrentPageState().croppingShapeId
|
|
2538
2592
|
}
|
|
2539
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
|
+
|
|
2540
2614
|
/**
|
|
2541
2615
|
* Set the current cropping shape.
|
|
2542
2616
|
*
|
|
@@ -2558,12 +2632,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2558
2632
|
() => {
|
|
2559
2633
|
if (!id) {
|
|
2560
2634
|
this.updateCurrentPageState({ croppingShapeId: null })
|
|
2561
|
-
} else {
|
|
2562
|
-
|
|
2563
|
-
const util = this.getShapeUtil(shape)
|
|
2564
|
-
if (shape && util.canCrop(shape)) {
|
|
2565
|
-
this.updateCurrentPageState({ croppingShapeId: id })
|
|
2566
|
-
}
|
|
2635
|
+
} else if (this.canCropShape(id)) {
|
|
2636
|
+
this.updateCurrentPageState({ croppingShapeId: id })
|
|
2567
2637
|
}
|
|
2568
2638
|
},
|
|
2569
2639
|
{ history: 'ignore' }
|
|
@@ -2673,6 +2743,52 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2673
2743
|
return this.getCamera().z
|
|
2674
2744
|
}
|
|
2675
2745
|
|
|
2746
|
+
private _debouncedZoomLevel = atom('debounced zoom level', 1)
|
|
2747
|
+
|
|
2748
|
+
/**
|
|
2749
|
+
* Get the debounced zoom level. When the camera is moving, this returns the zoom level
|
|
2750
|
+
* from when the camera started moving rather than the current zoom level. This can be
|
|
2751
|
+
* used to avoid expensive re-renders during camera movements.
|
|
2752
|
+
*
|
|
2753
|
+
* This behavior is controlled by the `useDebouncedZoom` option. When `useDebouncedZoom`
|
|
2754
|
+
* is `false`, this method always returns the current zoom level.
|
|
2755
|
+
*
|
|
2756
|
+
* @public
|
|
2757
|
+
*/
|
|
2758
|
+
@computed getDebouncedZoomLevel() {
|
|
2759
|
+
if (this.options.debouncedZoom) {
|
|
2760
|
+
if (this.getCameraState() === 'idle') {
|
|
2761
|
+
return this.getZoomLevel()
|
|
2762
|
+
} else {
|
|
2763
|
+
return this._debouncedZoomLevel.get()
|
|
2764
|
+
}
|
|
2765
|
+
}
|
|
2766
|
+
|
|
2767
|
+
return this.getZoomLevel()
|
|
2768
|
+
}
|
|
2769
|
+
|
|
2770
|
+
@computed private _getAboveDebouncedZoomThreshold() {
|
|
2771
|
+
return this.getCurrentPageShapeIds().size > this.options.debouncedZoomThreshold
|
|
2772
|
+
}
|
|
2773
|
+
|
|
2774
|
+
/**
|
|
2775
|
+
* Get the efficient zoom level. This returns the current zoom level if there are less than 300 shapes on the page,
|
|
2776
|
+
* otherwise it returns the debounced zoom level. This can be used to avoid expensive re-renders during camera movements.
|
|
2777
|
+
*
|
|
2778
|
+
* @public
|
|
2779
|
+
* @example
|
|
2780
|
+
* ```ts
|
|
2781
|
+
* editor.getEfficientZoomLevel()
|
|
2782
|
+
* ```
|
|
2783
|
+
*
|
|
2784
|
+
* @public
|
|
2785
|
+
*/
|
|
2786
|
+
@computed getEfficientZoomLevel() {
|
|
2787
|
+
return this._getAboveDebouncedZoomThreshold()
|
|
2788
|
+
? this.getDebouncedZoomLevel()
|
|
2789
|
+
: this.getZoomLevel()
|
|
2790
|
+
}
|
|
2791
|
+
|
|
2676
2792
|
/**
|
|
2677
2793
|
* Get the camera's initial or reset zoom level.
|
|
2678
2794
|
*
|
|
@@ -2999,7 +3115,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2999
3115
|
|
|
3000
3116
|
// Dispatch a new pointer move because the pointer's page will have changed
|
|
3001
3117
|
// (its screen position will compute to a new page position given the new camera position)
|
|
3002
|
-
const
|
|
3118
|
+
const currentScreenPoint = this.inputs.getCurrentScreenPoint()
|
|
3119
|
+
const currentPagePoint = this.inputs.getCurrentPagePoint()
|
|
3003
3120
|
|
|
3004
3121
|
// compare the next page point (derived from the current camera) to the current page point
|
|
3005
3122
|
if (
|
|
@@ -3163,7 +3280,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3163
3280
|
* ```ts
|
|
3164
3281
|
* editor.zoomIn()
|
|
3165
3282
|
* editor.zoomIn(editor.getViewportScreenCenter(), { animation: { duration: 200 } })
|
|
3166
|
-
* editor.zoomIn(editor.inputs.
|
|
3283
|
+
* editor.zoomIn(editor.inputs.getCurrentScreenPoint(), { animation: { duration: 200 } })
|
|
3167
3284
|
* ```
|
|
3168
3285
|
*
|
|
3169
3286
|
* @param point - The screen point to zoom in on. Defaults to the screen center
|
|
@@ -3208,7 +3325,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3208
3325
|
* ```ts
|
|
3209
3326
|
* editor.zoomOut()
|
|
3210
3327
|
* editor.zoomOut(editor.getViewportScreenCenter(), { animation: { duration: 120 } })
|
|
3211
|
-
* editor.zoomOut(editor.inputs.
|
|
3328
|
+
* editor.zoomOut(editor.inputs.getCurrentScreenPoint(), { animation: { duration: 120 } })
|
|
3212
3329
|
* ```
|
|
3213
3330
|
*
|
|
3214
3331
|
* @param point - The point to zoom out on. Defaults to the viewport screen center.
|
|
@@ -3265,10 +3382,17 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3265
3382
|
|
|
3266
3383
|
const selectionPageBounds = this.getSelectionPageBounds()
|
|
3267
3384
|
if (selectionPageBounds) {
|
|
3268
|
-
this.
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
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
|
+
}
|
|
3272
3396
|
}
|
|
3273
3397
|
return this
|
|
3274
3398
|
}
|
|
@@ -3325,7 +3449,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3325
3449
|
|
|
3326
3450
|
const viewportScreenBounds = this.getViewportScreenBounds()
|
|
3327
3451
|
|
|
3328
|
-
const inset =
|
|
3452
|
+
const inset =
|
|
3453
|
+
opts?.inset ?? Math.min(this.options.zoomToFitPadding, viewportScreenBounds.width * 0.28)
|
|
3329
3454
|
|
|
3330
3455
|
const baseZoom = this.getBaseZoom()
|
|
3331
3456
|
const zoomMin = cameraOptions.zoomSteps[0]
|
|
@@ -3635,22 +3760,23 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3635
3760
|
if (_willSetInitialBounds) {
|
|
3636
3761
|
// If we have just received the initial bounds, don't center the camera.
|
|
3637
3762
|
this.updateInstanceState({ screenBounds: screenBounds.toJson(), insets })
|
|
3763
|
+
this.emit('resize', screenBounds.toJson())
|
|
3638
3764
|
this.setCamera(this.getCamera())
|
|
3639
3765
|
} else {
|
|
3640
3766
|
if (center && !this.getInstanceState().followingUserId) {
|
|
3641
3767
|
// Get the page center before the change, make the change, and restore it
|
|
3642
3768
|
const before = this.getViewportPageBounds().center
|
|
3643
3769
|
this.updateInstanceState({ screenBounds: screenBounds.toJson(), insets })
|
|
3770
|
+
this.emit('resize', screenBounds.toJson())
|
|
3644
3771
|
this.centerOnPoint(before)
|
|
3645
3772
|
} else {
|
|
3646
3773
|
// Otherwise,
|
|
3647
3774
|
this.updateInstanceState({ screenBounds: screenBounds.toJson(), insets })
|
|
3775
|
+
this.emit('resize', screenBounds.toJson())
|
|
3648
3776
|
this._setCamera(Vec.From({ ...this.getCamera() }))
|
|
3649
3777
|
}
|
|
3650
3778
|
}
|
|
3651
3779
|
|
|
3652
|
-
this._tickCameraState()
|
|
3653
|
-
|
|
3654
3780
|
return this
|
|
3655
3781
|
}
|
|
3656
3782
|
|
|
@@ -4056,18 +4182,19 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
4056
4182
|
// box just for rendering, and we only update after the camera stops moving.
|
|
4057
4183
|
private _cameraState = atom('camera state', 'idle' as 'idle' | 'moving')
|
|
4058
4184
|
private _cameraStateTimeoutRemaining = 0
|
|
4059
|
-
_decayCameraStateTimeout(elapsed: number) {
|
|
4185
|
+
private _decayCameraStateTimeout(elapsed: number) {
|
|
4060
4186
|
this._cameraStateTimeoutRemaining -= elapsed
|
|
4061
4187
|
if (this._cameraStateTimeoutRemaining > 0) return
|
|
4062
4188
|
this.off('tick', this._decayCameraStateTimeout)
|
|
4063
4189
|
this._cameraState.set('idle')
|
|
4064
4190
|
}
|
|
4065
|
-
_tickCameraState() {
|
|
4191
|
+
private _tickCameraState() {
|
|
4066
4192
|
// always reset the timeout
|
|
4067
4193
|
this._cameraStateTimeoutRemaining = this.options.cameraMovingTimeoutMs
|
|
4068
4194
|
// If the state is idle, then start the tick
|
|
4069
4195
|
if (this._cameraState.__unsafe__getWithoutCapture() !== 'idle') return
|
|
4070
4196
|
this._cameraState.set('moving')
|
|
4197
|
+
this._debouncedZoomLevel.set(unsafe__withoutCapture(() => this.getCamera().z))
|
|
4071
4198
|
this.on('tick', this._decayCameraStateTimeout)
|
|
4072
4199
|
}
|
|
4073
4200
|
|
|
@@ -5014,6 +5141,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5014
5141
|
}
|
|
5015
5142
|
|
|
5016
5143
|
private _notVisibleShapes = notVisibleShapes(this)
|
|
5144
|
+
private _culledShapesCache: Set<TLShapeId> | null = null
|
|
5017
5145
|
|
|
5018
5146
|
/**
|
|
5019
5147
|
* Get culled shapes (those that should not render), taking into account which shapes are selected or editing.
|
|
@@ -5025,16 +5153,41 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5025
5153
|
const notVisibleShapes = this.getNotVisibleShapes()
|
|
5026
5154
|
const selectedShapeIds = this.getSelectedShapeIds()
|
|
5027
5155
|
const editingId = this.getEditingShapeId()
|
|
5028
|
-
const
|
|
5156
|
+
const nextValue = new Set<TLShapeId>(notVisibleShapes)
|
|
5029
5157
|
// we don't cull the shape we are editing
|
|
5030
5158
|
if (editingId) {
|
|
5031
|
-
|
|
5159
|
+
nextValue.delete(editingId)
|
|
5032
5160
|
}
|
|
5033
5161
|
// we also don't cull selected shapes
|
|
5034
5162
|
selectedShapeIds.forEach((id) => {
|
|
5035
|
-
|
|
5163
|
+
nextValue.delete(id)
|
|
5036
5164
|
})
|
|
5037
|
-
|
|
5165
|
+
|
|
5166
|
+
// Cache optimization: return same Set object if contents unchanged
|
|
5167
|
+
// This allows consumers to use === comparison and prevents unnecessary re-renders
|
|
5168
|
+
const prevValue = this._culledShapesCache
|
|
5169
|
+
if (prevValue) {
|
|
5170
|
+
// If sizes differ, contents must differ
|
|
5171
|
+
if (prevValue.size !== nextValue.size) {
|
|
5172
|
+
this._culledShapesCache = nextValue
|
|
5173
|
+
return nextValue
|
|
5174
|
+
}
|
|
5175
|
+
|
|
5176
|
+
// Check if all elements are the same
|
|
5177
|
+
for (const id of prevValue) {
|
|
5178
|
+
if (!nextValue.has(id)) {
|
|
5179
|
+
// Found a difference, update cache and return new set
|
|
5180
|
+
this._culledShapesCache = nextValue
|
|
5181
|
+
return nextValue
|
|
5182
|
+
}
|
|
5183
|
+
}
|
|
5184
|
+
|
|
5185
|
+
// Loop completed without finding differences - contents identical
|
|
5186
|
+
return prevValue
|
|
5187
|
+
}
|
|
5188
|
+
|
|
5189
|
+
this._culledShapesCache = nextValue
|
|
5190
|
+
return nextValue
|
|
5038
5191
|
}
|
|
5039
5192
|
|
|
5040
5193
|
/**
|
|
@@ -5101,11 +5254,18 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5101
5254
|
let inMarginClosestToEdgeDistance = Infinity
|
|
5102
5255
|
let inMarginClosestToEdgeHit: TLShape | null = null
|
|
5103
5256
|
|
|
5257
|
+
// Use larger margin for spatial search to account for edge distance checks
|
|
5258
|
+
const searchMargin = Math.max(innerMargin, outerMargin, this.options.hitTestMargin / zoomLevel)
|
|
5259
|
+
const candidateIds = this._spatialIndex.getShapeIdsAtPoint(point, searchMargin)
|
|
5260
|
+
|
|
5104
5261
|
const shapesToCheck = (
|
|
5105
5262
|
opts.renderingOnly
|
|
5106
5263
|
? this.getCurrentPageRenderingShapesSorted()
|
|
5107
5264
|
: this.getCurrentPageShapesSorted()
|
|
5108
5265
|
).filter((shape) => {
|
|
5266
|
+
// Frames have labels positioned above the shape (outside bounds), so always include them
|
|
5267
|
+
if (!candidateIds.has(shape.id) && !this.isShapeOfType(shape, 'frame')) return false
|
|
5268
|
+
|
|
5109
5269
|
if (
|
|
5110
5270
|
(shape.isLocked && !hitLocked) ||
|
|
5111
5271
|
this.isShapeHidden(shape) ||
|
|
@@ -5127,10 +5287,10 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5127
5287
|
|
|
5128
5288
|
// Check labels first
|
|
5129
5289
|
if (
|
|
5130
|
-
this.isShapeOfType
|
|
5131
|
-
((this.isShapeOfType
|
|
5132
|
-
this.isShapeOfType
|
|
5133
|
-
(this.isShapeOfType
|
|
5290
|
+
this.isShapeOfType(shape, 'frame') ||
|
|
5291
|
+
((this.isShapeOfType(shape, 'note') ||
|
|
5292
|
+
this.isShapeOfType(shape, 'arrow') ||
|
|
5293
|
+
(this.isShapeOfType(shape, 'geo') && shape.props.fill === 'none')) &&
|
|
5134
5294
|
this.getShapeUtil(shape).getText(shape)?.trim())
|
|
5135
5295
|
) {
|
|
5136
5296
|
for (const childGeometry of (geometry as Group2d).children) {
|
|
@@ -5140,7 +5300,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5140
5300
|
}
|
|
5141
5301
|
}
|
|
5142
5302
|
|
|
5143
|
-
if (this.isShapeOfType
|
|
5303
|
+
if (this.isShapeOfType(shape, 'frame')) {
|
|
5144
5304
|
// On the rare case that we've hit a frame (not its label), test again hitInside to be forced true;
|
|
5145
5305
|
// this prevents clicks from passing through the body of a frame to shapes behind it.
|
|
5146
5306
|
|
|
@@ -5291,11 +5451,41 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5291
5451
|
point: VecLike,
|
|
5292
5452
|
opts = {} as { margin?: number; hitInside?: boolean }
|
|
5293
5453
|
): TLShape[] {
|
|
5454
|
+
const margin = opts.margin ?? 0
|
|
5455
|
+
const candidateIds = this._spatialIndex.getShapeIdsAtPoint(point, margin)
|
|
5456
|
+
|
|
5457
|
+
// Get all page shapes in z-index order and filter to candidates that pass isPointInShape
|
|
5458
|
+
// Frames are always checked because their labels can be outside their bounds
|
|
5294
5459
|
return this.getCurrentPageShapesSorted()
|
|
5295
|
-
.filter((shape) =>
|
|
5460
|
+
.filter((shape) => {
|
|
5461
|
+
if (this.isShapeHidden(shape)) return false
|
|
5462
|
+
if (!candidateIds.has(shape.id) && !this.isShapeOfType(shape, 'frame')) return false
|
|
5463
|
+
return this.isPointInShape(shape, point, opts)
|
|
5464
|
+
})
|
|
5296
5465
|
.reverse()
|
|
5297
5466
|
}
|
|
5298
5467
|
|
|
5468
|
+
/**
|
|
5469
|
+
* Get shape IDs within the given bounds.
|
|
5470
|
+
*
|
|
5471
|
+
* Note: Uses shape page bounds only. Frames with labels outside their bounds
|
|
5472
|
+
* may not be included even if the label is within the search bounds.
|
|
5473
|
+
*
|
|
5474
|
+
* Note: Results are unordered. If you need z-order, combine with sorted shapes:
|
|
5475
|
+
* ```ts
|
|
5476
|
+
* const candidates = editor.getShapeIdsInsideBounds(bounds)
|
|
5477
|
+
* const sorted = editor.getCurrentPageShapesSorted().filter(s => candidates.has(s.id))
|
|
5478
|
+
* ```
|
|
5479
|
+
*
|
|
5480
|
+
* @param bounds - The bounds to search within.
|
|
5481
|
+
* @returns Unordered set of shape IDs within the given bounds.
|
|
5482
|
+
*
|
|
5483
|
+
* @internal
|
|
5484
|
+
*/
|
|
5485
|
+
getShapeIdsInsideBounds(bounds: Box): Set<TLShapeId> {
|
|
5486
|
+
return this._spatialIndex.getShapeIdsInsideBounds(bounds)
|
|
5487
|
+
}
|
|
5488
|
+
|
|
5299
5489
|
/**
|
|
5300
5490
|
* Test whether a point (in the current page space) will will a shape. This method takes into account masks,
|
|
5301
5491
|
* such as when a shape is the child of a frame and is partially clipped by the frame.
|
|
@@ -5421,7 +5611,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5421
5611
|
*
|
|
5422
5612
|
* @example
|
|
5423
5613
|
* ```ts
|
|
5424
|
-
* const isArrowShape = isShapeOfType
|
|
5614
|
+
* const isArrowShape = isShapeOfType(someShape, 'arrow')
|
|
5425
5615
|
* ```
|
|
5426
5616
|
*
|
|
5427
5617
|
* @param util - the TLShapeUtil constructor to test against
|
|
@@ -5429,15 +5619,16 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5429
5619
|
*
|
|
5430
5620
|
* @public
|
|
5431
5621
|
*/
|
|
5432
|
-
isShapeOfType<
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
arg: TLUnknownShape | TLUnknownShape['id'],
|
|
5622
|
+
isShapeOfType<K extends TLShape['type']>(
|
|
5623
|
+
shape: TLShape,
|
|
5624
|
+
type: K
|
|
5625
|
+
): shape is Extract<TLShape, { type: K }>
|
|
5626
|
+
isShapeOfType<T extends TLShape>(
|
|
5627
|
+
shape: TLShape,
|
|
5439
5628
|
type: T['type']
|
|
5440
|
-
) {
|
|
5629
|
+
): shape is Extract<TLShape, { type: T['type'] }>
|
|
5630
|
+
isShapeOfType<T extends TLShape = TLShape>(shapeId: TLShapeId, type: T['type']): boolean
|
|
5631
|
+
isShapeOfType(arg: TLShape | TLShapeId, type: TLShape['type']) {
|
|
5441
5632
|
const shape = typeof arg === 'string' ? this.getShape(arg) : arg
|
|
5442
5633
|
if (!shape) return false
|
|
5443
5634
|
return shape.type === type
|
|
@@ -5833,7 +6024,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5833
6024
|
|
|
5834
6025
|
while (node) {
|
|
5835
6026
|
if (
|
|
5836
|
-
this.isShapeOfType
|
|
6027
|
+
this.isShapeOfType(node, 'group') &&
|
|
5837
6028
|
focusedGroup?.id !== node.id &&
|
|
5838
6029
|
!this.hasAncestor(focusedGroup, node.id) &&
|
|
5839
6030
|
(filter?.(node) ?? true)
|
|
@@ -5875,7 +6066,15 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5875
6066
|
* Get all bindings of a certain type _from_ a particular shape. These are the bindings whose
|
|
5876
6067
|
* `fromId` matched the shape's ID.
|
|
5877
6068
|
*/
|
|
5878
|
-
getBindingsFromShape<
|
|
6069
|
+
getBindingsFromShape<K extends TLBinding['type']>(
|
|
6070
|
+
shape: TLShape | TLShapeId,
|
|
6071
|
+
type: K
|
|
6072
|
+
): Extract<TLBinding, { type: K }>[]
|
|
6073
|
+
getBindingsFromShape<Binding extends TLBinding = TLBinding>(
|
|
6074
|
+
shape: TLShape | TLShapeId,
|
|
6075
|
+
type: Binding['type']
|
|
6076
|
+
): Binding[]
|
|
6077
|
+
getBindingsFromShape<Binding extends TLBinding = TLBinding>(
|
|
5879
6078
|
shape: TLShape | TLShapeId,
|
|
5880
6079
|
type: Binding['type']
|
|
5881
6080
|
): Binding[] {
|
|
@@ -5889,7 +6088,15 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5889
6088
|
* Get all bindings of a certain type _to_ a particular shape. These are the bindings whose
|
|
5890
6089
|
* `toId` matches the shape's ID.
|
|
5891
6090
|
*/
|
|
5892
|
-
getBindingsToShape<
|
|
6091
|
+
getBindingsToShape<K extends TLBinding['type']>(
|
|
6092
|
+
shape: TLShape | TLShapeId,
|
|
6093
|
+
type: K
|
|
6094
|
+
): Extract<TLBinding, { type: K }>[]
|
|
6095
|
+
getBindingsToShape<Binding extends TLBinding = TLBinding>(
|
|
6096
|
+
shape: TLShape | TLShapeId,
|
|
6097
|
+
type: Binding['type']
|
|
6098
|
+
): Binding[]
|
|
6099
|
+
getBindingsToShape<Binding extends TLBinding = TLBinding>(
|
|
5893
6100
|
shape: TLShape | TLShapeId,
|
|
5894
6101
|
type: Binding['type']
|
|
5895
6102
|
): Binding[] {
|
|
@@ -5903,7 +6110,15 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5903
6110
|
* Get all bindings involving a particular shape. This includes bindings where the shape is the
|
|
5904
6111
|
* `fromId` or `toId`. If a type is provided, only bindings of that type are returned.
|
|
5905
6112
|
*/
|
|
5906
|
-
getBindingsInvolvingShape<
|
|
6113
|
+
getBindingsInvolvingShape<K extends TLBinding['type']>(
|
|
6114
|
+
shape: TLShape | TLShapeId,
|
|
6115
|
+
type: K
|
|
6116
|
+
): Extract<TLBinding, { type: K }>[]
|
|
6117
|
+
getBindingsInvolvingShape<Binding extends TLBinding = TLBinding>(
|
|
6118
|
+
shape: TLShape | TLShapeId,
|
|
6119
|
+
type?: Binding['type']
|
|
6120
|
+
): Binding[]
|
|
6121
|
+
getBindingsInvolvingShape<Binding extends TLBinding = TLBinding>(
|
|
5907
6122
|
shape: TLShape | TLShapeId,
|
|
5908
6123
|
type?: Binding['type']
|
|
5909
6124
|
): Binding[] {
|
|
@@ -5925,7 +6140,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5925
6140
|
if (!fromShape || !toShape) continue
|
|
5926
6141
|
if (!this.canBindShapes({ fromShape, toShape, binding: partial })) continue
|
|
5927
6142
|
|
|
5928
|
-
const util = this.getBindingUtil
|
|
6143
|
+
const util = this.getBindingUtil(partial.type)
|
|
5929
6144
|
const defaultProps = util.getDefaultProps()
|
|
5930
6145
|
const binding = this.store.schema.types.binding.create({
|
|
5931
6146
|
...partial,
|
|
@@ -6030,7 +6245,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
6030
6245
|
const toShapeType = typeof toShape === 'string' ? toShape : toShape.type
|
|
6031
6246
|
const bindingType = typeof binding === 'string' ? binding : binding.type
|
|
6032
6247
|
|
|
6033
|
-
const canBindOpts = { fromShapeType, toShapeType, bindingType }
|
|
6248
|
+
const canBindOpts = { fromShapeType, toShapeType, bindingType } as const
|
|
6034
6249
|
|
|
6035
6250
|
if (fromShapeType === toShapeType) {
|
|
6036
6251
|
return this.getShapeUtil(fromShapeType).canBind(canBindOpts)
|
|
@@ -6571,7 +6786,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
6571
6786
|
const shapesToFlipFirstPass = compact(ids.map((id) => this.getShape(id)))
|
|
6572
6787
|
|
|
6573
6788
|
for (const shape of shapesToFlipFirstPass) {
|
|
6574
|
-
if (this.isShapeOfType
|
|
6789
|
+
if (this.isShapeOfType(shape, 'group')) {
|
|
6575
6790
|
const childrenOfGroups = compact(
|
|
6576
6791
|
this.getSortedChildIdsForParent(shape.id).map((id) => this.getShape(id))
|
|
6577
6792
|
)
|
|
@@ -7628,8 +7843,14 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
7628
7843
|
// then if the shape is flipped in one axis only, we need to apply an extra rotation
|
|
7629
7844
|
// to make sure the shape is mirrored correctly
|
|
7630
7845
|
if (Math.sign(scale.x) * Math.sign(scale.y) < 0) {
|
|
7631
|
-
|
|
7632
|
-
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
|
|
7633
7854
|
this.updateShapes([{ id, type, rotation }])
|
|
7634
7855
|
}
|
|
7635
7856
|
|
|
@@ -7649,9 +7870,13 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
7649
7870
|
)
|
|
7650
7871
|
|
|
7651
7872
|
// now calculate how far away the shape is from where it needs to be
|
|
7652
|
-
const pageBounds = this.getShapePageBounds(id)!
|
|
7653
7873
|
const pageTransform = this.getShapePageTransform(id)!
|
|
7654
|
-
|
|
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)
|
|
7655
7880
|
const shapePageTransformOrigin = pageTransform.point()
|
|
7656
7881
|
if (!currentPageCenter || !shapePageTransformOrigin) return this
|
|
7657
7882
|
const pageDelta = Vec.Sub(postScaleShapePageCenter, currentPageCenter)
|
|
@@ -7692,9 +7917,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
7692
7917
|
*
|
|
7693
7918
|
* @public
|
|
7694
7919
|
*/
|
|
7695
|
-
canCreateShape<
|
|
7696
|
-
shape: OptionalKeys<TLShapePartial<T>, 'id'> | T['id']
|
|
7697
|
-
): boolean {
|
|
7920
|
+
canCreateShape(shape: OptionalKeys<TLShapePartial<TLShape>, 'id'> | TLShape['id']): boolean {
|
|
7698
7921
|
return this.canCreateShapes([shape])
|
|
7699
7922
|
}
|
|
7700
7923
|
|
|
@@ -7705,8 +7928,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
7705
7928
|
*
|
|
7706
7929
|
* @public
|
|
7707
7930
|
*/
|
|
7708
|
-
canCreateShapes
|
|
7709
|
-
shapes: (
|
|
7931
|
+
canCreateShapes(
|
|
7932
|
+
shapes: (TLShape['id'] | OptionalKeys<TLShapePartial<TLShape>, 'id'>)[]
|
|
7710
7933
|
): boolean {
|
|
7711
7934
|
return shapes.length + this.getCurrentPageShapeIds().size <= this.options.maxShapesPerPage
|
|
7712
7935
|
}
|
|
@@ -7724,7 +7947,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
7724
7947
|
*
|
|
7725
7948
|
* @public
|
|
7726
7949
|
*/
|
|
7727
|
-
createShape<
|
|
7950
|
+
createShape<TShape extends TLShape>(shape: TLCreateShapePartial<TShape>): this {
|
|
7728
7951
|
this.createShapes([shape])
|
|
7729
7952
|
return this
|
|
7730
7953
|
}
|
|
@@ -7742,7 +7965,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
7742
7965
|
*
|
|
7743
7966
|
* @public
|
|
7744
7967
|
*/
|
|
7745
|
-
createShapes<
|
|
7968
|
+
createShapes<TShape extends TLShape = TLShape>(shapes: TLCreateShapePartial<TShape>[]): this {
|
|
7746
7969
|
if (!Array.isArray(shapes)) {
|
|
7747
7970
|
throw Error('Editor.createShapes: must provide an array of shapes or shape partials')
|
|
7748
7971
|
}
|
|
@@ -8101,7 +8324,12 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8101
8324
|
)
|
|
8102
8325
|
)
|
|
8103
8326
|
const sortedShapeIds = shapesToGroup.sort(sortByIndex).map((s) => s.id)
|
|
8104
|
-
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
|
+
}
|
|
8105
8333
|
|
|
8106
8334
|
const { x, y } = pageBounds.point
|
|
8107
8335
|
|
|
@@ -8123,7 +8351,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8123
8351
|
const highestIndex = shapesWithRootParent[shapesWithRootParent.length - 1]?.index
|
|
8124
8352
|
|
|
8125
8353
|
this.run(() => {
|
|
8126
|
-
this.createShapes
|
|
8354
|
+
this.createShapes([
|
|
8127
8355
|
{
|
|
8128
8356
|
id: groupId,
|
|
8129
8357
|
type: 'group',
|
|
@@ -8193,7 +8421,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8193
8421
|
const groups: TLGroupShape[] = []
|
|
8194
8422
|
|
|
8195
8423
|
shapesToUngroup.forEach((shape) => {
|
|
8196
|
-
if (this.isShapeOfType
|
|
8424
|
+
if (this.isShapeOfType(shape, 'group')) {
|
|
8197
8425
|
groups.push(shape)
|
|
8198
8426
|
} else {
|
|
8199
8427
|
idsToSelect.add(shape.id)
|
|
@@ -8239,7 +8467,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8239
8467
|
*
|
|
8240
8468
|
* @public
|
|
8241
8469
|
*/
|
|
8242
|
-
updateShape<T extends
|
|
8470
|
+
updateShape<T extends TLShape = TLShape>(partial: TLShapePartial<T> | null | undefined) {
|
|
8243
8471
|
this.updateShapes([partial])
|
|
8244
8472
|
return this
|
|
8245
8473
|
}
|
|
@@ -8256,7 +8484,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8256
8484
|
*
|
|
8257
8485
|
* @public
|
|
8258
8486
|
*/
|
|
8259
|
-
updateShapes<T extends
|
|
8487
|
+
updateShapes<T extends TLShape>(partials: (TLShapePartial<T> | null | undefined)[]) {
|
|
8260
8488
|
const compactedPartials: TLShapePartial<T>[] = Array(partials.length)
|
|
8261
8489
|
|
|
8262
8490
|
for (let i = 0, n = partials.length; i < n; i++) {
|
|
@@ -8408,7 +8636,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8408
8636
|
* @internal
|
|
8409
8637
|
*/
|
|
8410
8638
|
private _extractSharedStyles(shape: TLShape, sharedStyleMap: SharedStyleMap) {
|
|
8411
|
-
if (this.isShapeOfType
|
|
8639
|
+
if (this.isShapeOfType(shape, 'group')) {
|
|
8412
8640
|
// For groups, ignore the styles of the group shape and instead include the styles of the
|
|
8413
8641
|
// group's children. These are the shapes that would have their styles changed if the
|
|
8414
8642
|
// user called `setStyle` on the current selection.
|
|
@@ -8528,7 +8756,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8528
8756
|
// For groups, ignore the opacity of the group shape and instead include
|
|
8529
8757
|
// the opacity of the group's children. These are the shapes that would have
|
|
8530
8758
|
// their opacity changed if the user called `setOpacity` on the current selection.
|
|
8531
|
-
if (this.isShapeOfType
|
|
8759
|
+
if (this.isShapeOfType(shape, 'group')) {
|
|
8532
8760
|
for (const childId of this.getSortedChildIdsForParent(shape.id)) {
|
|
8533
8761
|
addShape(childId)
|
|
8534
8762
|
}
|
|
@@ -8589,7 +8817,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8589
8817
|
// We can have many deep levels of grouped shape
|
|
8590
8818
|
// Making a recursive function to look through all the levels
|
|
8591
8819
|
const addShapeById = (shape: TLShape) => {
|
|
8592
|
-
if (this.isShapeOfType
|
|
8820
|
+
if (this.isShapeOfType(shape, 'group')) {
|
|
8593
8821
|
const childIds = this.getSortedChildIdsForParent(shape)
|
|
8594
8822
|
for (const childId of childIds) {
|
|
8595
8823
|
addShapeById(this.getShape(childId)!)
|
|
@@ -8673,7 +8901,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8673
8901
|
// We can have many deep levels of grouped shape
|
|
8674
8902
|
// Making a recursive function to look through all the levels
|
|
8675
8903
|
const addShapeById = (shape: TLShape) => {
|
|
8676
|
-
if (this.isShapeOfType
|
|
8904
|
+
if (this.isShapeOfType(shape, 'group')) {
|
|
8677
8905
|
const childIds = this.getSortedChildIdsForParent(shape.id)
|
|
8678
8906
|
for (const childId of childIds) {
|
|
8679
8907
|
addShapeById(this.getShape(childId)!)
|
|
@@ -9098,7 +9326,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9098
9326
|
for (const shape of this.getSelectedShapes()) {
|
|
9099
9327
|
if (lowestDepth === 0) break
|
|
9100
9328
|
|
|
9101
|
-
const isFrame = this.isShapeOfType
|
|
9329
|
+
const isFrame = this.isShapeOfType(shape, 'frame')
|
|
9102
9330
|
const ancestors = this.getShapeAncestors(shape)
|
|
9103
9331
|
if (isFrame) ancestors.push(shape)
|
|
9104
9332
|
|
|
@@ -9126,6 +9354,30 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9126
9354
|
}
|
|
9127
9355
|
}
|
|
9128
9356
|
|
|
9357
|
+
if (point) {
|
|
9358
|
+
const shapesById = new Map<TLShapeId, TLShape>(shapes.map((shape) => [shape.id, shape]))
|
|
9359
|
+
const rootShapesFromContent = compact(rootShapeIds.map((id) => shapesById.get(id)))
|
|
9360
|
+
if (rootShapesFromContent.length > 0) {
|
|
9361
|
+
const targetParent = this.getShapeAtPoint(point, {
|
|
9362
|
+
hitInside: true,
|
|
9363
|
+
hitFrameInside: true,
|
|
9364
|
+
hitLocked: true,
|
|
9365
|
+
filter: (shape) => {
|
|
9366
|
+
const util = this.getShapeUtil(shape)
|
|
9367
|
+
if (!util.canReceiveNewChildrenOfType) return false
|
|
9368
|
+
return rootShapesFromContent.every((rootShape) =>
|
|
9369
|
+
util.canReceiveNewChildrenOfType!(shape, rootShape.type)
|
|
9370
|
+
)
|
|
9371
|
+
},
|
|
9372
|
+
})
|
|
9373
|
+
|
|
9374
|
+
// When pasting at a specific point (e.g. paste-at-cursor) prefer the
|
|
9375
|
+
// parent under the pointer so that we don't keep using the original
|
|
9376
|
+
// selection's parent (which can keep shapes clipped inside frames).
|
|
9377
|
+
pasteParentId = targetParent ? targetParent.id : currentPageId
|
|
9378
|
+
}
|
|
9379
|
+
}
|
|
9380
|
+
|
|
9129
9381
|
let isDuplicating = false
|
|
9130
9382
|
|
|
9131
9383
|
if (!isPageId(pasteParentId)) {
|
|
@@ -9137,8 +9389,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9137
9389
|
if (rootShapeIds.length === 1) {
|
|
9138
9390
|
const rootShape = shapes.find((s) => s.id === rootShapeIds[0])!
|
|
9139
9391
|
if (
|
|
9140
|
-
this.isShapeOfType
|
|
9141
|
-
this.isShapeOfType
|
|
9392
|
+
this.isShapeOfType(parent, 'frame') &&
|
|
9393
|
+
this.isShapeOfType(rootShape, 'frame') &&
|
|
9142
9394
|
rootShape.props.w === parent?.props.w &&
|
|
9143
9395
|
rootShape.props.h === parent?.props.h
|
|
9144
9396
|
) {
|
|
@@ -9313,11 +9565,11 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9313
9565
|
const onlyRoot = rootShapes[0] as TLFrameShape
|
|
9314
9566
|
// If the old bounds are in the viewport...
|
|
9315
9567
|
// todo: replace frame references with shapes that can accept children
|
|
9316
|
-
if (this.isShapeOfType
|
|
9568
|
+
if (this.isShapeOfType(onlyRoot, 'frame')) {
|
|
9317
9569
|
while (
|
|
9318
9570
|
this.getShapesAtPoint(point).some(
|
|
9319
9571
|
(shape) =>
|
|
9320
|
-
this.isShapeOfType
|
|
9572
|
+
this.isShapeOfType(shape, 'frame') &&
|
|
9321
9573
|
shape.props.w === onlyRoot.props.w &&
|
|
9322
9574
|
shape.props.h === onlyRoot.props.h
|
|
9323
9575
|
)
|
|
@@ -9463,126 +9715,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9463
9715
|
|
|
9464
9716
|
/* --------------------- Events --------------------- */
|
|
9465
9717
|
|
|
9466
|
-
/**
|
|
9467
|
-
* The app's current input state.
|
|
9468
|
-
*
|
|
9469
|
-
* @public
|
|
9470
|
-
*/
|
|
9471
|
-
inputs = {
|
|
9472
|
-
/** The most recent pointer down's position in the current page space. */
|
|
9473
|
-
originPagePoint: new Vec(),
|
|
9474
|
-
/** The most recent pointer down's position in screen space. */
|
|
9475
|
-
originScreenPoint: new Vec(),
|
|
9476
|
-
/** The previous pointer position in the current page space. */
|
|
9477
|
-
previousPagePoint: new Vec(),
|
|
9478
|
-
/** The previous pointer position in screen space. */
|
|
9479
|
-
previousScreenPoint: new Vec(),
|
|
9480
|
-
/** The most recent pointer position in the current page space. */
|
|
9481
|
-
currentPagePoint: new Vec(),
|
|
9482
|
-
/** The most recent pointer position in screen space. */
|
|
9483
|
-
currentScreenPoint: new Vec(),
|
|
9484
|
-
/** A set containing the currently pressed keys. */
|
|
9485
|
-
keys: new Set<string>(),
|
|
9486
|
-
/** A set containing the currently pressed buttons. */
|
|
9487
|
-
buttons: new Set<number>(),
|
|
9488
|
-
/** Whether the input is from a pe. */
|
|
9489
|
-
isPen: false,
|
|
9490
|
-
/** Whether the shift key is currently pressed. */
|
|
9491
|
-
shiftKey: false,
|
|
9492
|
-
/** Whether the meta key is currently pressed. */
|
|
9493
|
-
metaKey: false,
|
|
9494
|
-
/** Whether the control or command key is currently pressed. */
|
|
9495
|
-
ctrlKey: false,
|
|
9496
|
-
/** Whether the alt or option key is currently pressed. */
|
|
9497
|
-
altKey: false,
|
|
9498
|
-
/** Whether the user is dragging. */
|
|
9499
|
-
isDragging: false,
|
|
9500
|
-
/** Whether the user is pointing. */
|
|
9501
|
-
isPointing: false,
|
|
9502
|
-
/** Whether the user is pinching. */
|
|
9503
|
-
isPinching: false,
|
|
9504
|
-
/** Whether the user is editing. */
|
|
9505
|
-
isEditing: false,
|
|
9506
|
-
/** Whether the user is panning. */
|
|
9507
|
-
isPanning: false,
|
|
9508
|
-
/** Whether the user is spacebar panning. */
|
|
9509
|
-
isSpacebarPanning: false,
|
|
9510
|
-
/** Velocity of mouse pointer, in pixels per millisecond */
|
|
9511
|
-
pointerVelocity: new Vec(),
|
|
9512
|
-
}
|
|
9513
|
-
|
|
9514
|
-
/**
|
|
9515
|
-
* Update the input points from a pointer, pinch, or wheel event.
|
|
9516
|
-
*
|
|
9517
|
-
* @param info - The event info.
|
|
9518
|
-
*/
|
|
9519
|
-
private _updateInputsFromEvent(
|
|
9520
|
-
info: TLPointerEventInfo | TLPinchEventInfo | TLWheelEventInfo
|
|
9521
|
-
): void {
|
|
9522
|
-
const {
|
|
9523
|
-
pointerVelocity,
|
|
9524
|
-
previousScreenPoint,
|
|
9525
|
-
previousPagePoint,
|
|
9526
|
-
currentScreenPoint,
|
|
9527
|
-
currentPagePoint,
|
|
9528
|
-
originScreenPoint,
|
|
9529
|
-
originPagePoint,
|
|
9530
|
-
} = this.inputs
|
|
9531
|
-
|
|
9532
|
-
const { screenBounds } = this.store.unsafeGetWithoutCapture(TLINSTANCE_ID)!
|
|
9533
|
-
const { x: cx, y: cy, z: cz } = unsafe__withoutCapture(() => this.getCamera())
|
|
9534
|
-
|
|
9535
|
-
const sx = info.point.x - screenBounds.x
|
|
9536
|
-
const sy = info.point.y - screenBounds.y
|
|
9537
|
-
const sz = info.point.z ?? 0.5
|
|
9538
|
-
|
|
9539
|
-
previousScreenPoint.setTo(currentScreenPoint)
|
|
9540
|
-
previousPagePoint.setTo(currentPagePoint)
|
|
9541
|
-
|
|
9542
|
-
// The "screen bounds" is relative to the user's actual screen.
|
|
9543
|
-
// The "screen point" is relative to the "screen bounds";
|
|
9544
|
-
// it will be 0,0 when its actual screen position is equal
|
|
9545
|
-
// to screenBounds.point. This is confusing!
|
|
9546
|
-
currentScreenPoint.set(sx, sy)
|
|
9547
|
-
const nx = sx / cz - cx
|
|
9548
|
-
const ny = sy / cz - cy
|
|
9549
|
-
if (isFinite(nx) && isFinite(ny)) {
|
|
9550
|
-
currentPagePoint.set(nx, ny, sz)
|
|
9551
|
-
}
|
|
9552
|
-
|
|
9553
|
-
this.inputs.isPen = info.type === 'pointer' && info.isPen
|
|
9554
|
-
|
|
9555
|
-
// Reset velocity on pointer down, or when a pinch starts or ends
|
|
9556
|
-
if (info.name === 'pointer_down' || this.inputs.isPinching) {
|
|
9557
|
-
pointerVelocity.set(0, 0)
|
|
9558
|
-
originScreenPoint.setTo(currentScreenPoint)
|
|
9559
|
-
originPagePoint.setTo(currentPagePoint)
|
|
9560
|
-
}
|
|
9561
|
-
|
|
9562
|
-
// todo: We only have to do this if there are multiple users in the document
|
|
9563
|
-
this.run(
|
|
9564
|
-
() => {
|
|
9565
|
-
this.store.put([
|
|
9566
|
-
{
|
|
9567
|
-
id: TLPOINTER_ID,
|
|
9568
|
-
typeName: 'pointer',
|
|
9569
|
-
x: currentPagePoint.x,
|
|
9570
|
-
y: currentPagePoint.y,
|
|
9571
|
-
lastActivityTimestamp:
|
|
9572
|
-
// If our pointer moved only because we're following some other user, then don't
|
|
9573
|
-
// update our last activity timestamp; otherwise, update it to the current timestamp.
|
|
9574
|
-
info.type === 'pointer' && info.pointerId === INTERNAL_POINTER_IDS.CAMERA_MOVE
|
|
9575
|
-
? (this.store.unsafeGetWithoutCapture(TLPOINTER_ID)?.lastActivityTimestamp ??
|
|
9576
|
-
this._tickManager.now)
|
|
9577
|
-
: this._tickManager.now,
|
|
9578
|
-
meta: {},
|
|
9579
|
-
},
|
|
9580
|
-
])
|
|
9581
|
-
},
|
|
9582
|
-
{ history: 'ignore' }
|
|
9583
|
-
)
|
|
9584
|
-
}
|
|
9585
|
-
|
|
9586
9718
|
/**
|
|
9587
9719
|
* Dispatch a cancel event.
|
|
9588
9720
|
*
|
|
@@ -9652,19 +9784,22 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9652
9784
|
// weird but true: what `inputs` calls screen-space is actually viewport space. so
|
|
9653
9785
|
// we need to convert back into true screen space first. we should fix this...
|
|
9654
9786
|
Vec.Add(
|
|
9655
|
-
this.inputs.
|
|
9787
|
+
this.inputs.getCurrentScreenPoint(),
|
|
9656
9788
|
this.store.unsafeGetWithoutCapture(TLINSTANCE_ID)!.screenBounds
|
|
9657
9789
|
),
|
|
9658
9790
|
pointerId: options?.pointerId ?? 0,
|
|
9659
9791
|
button: options?.button ?? 0,
|
|
9660
|
-
isPen: options?.isPen ?? this.inputs.
|
|
9661
|
-
shiftKey: options?.shiftKey ?? this.inputs.
|
|
9662
|
-
altKey: options?.altKey ?? this.inputs.
|
|
9663
|
-
ctrlKey: options?.ctrlKey ?? this.inputs.
|
|
9664
|
-
metaKey: options?.metaKey ?? this.inputs.
|
|
9665
|
-
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,
|
|
9666
9798
|
}
|
|
9667
9799
|
|
|
9800
|
+
// needs to be calculated second
|
|
9801
|
+
event.accelKey = options?.accelKey ?? this.inputs.getAccelKey()
|
|
9802
|
+
|
|
9668
9803
|
if (options?.immediate) {
|
|
9669
9804
|
this._flushEventForTick(event)
|
|
9670
9805
|
} else {
|
|
@@ -10037,16 +10172,16 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10037
10172
|
/** @internal */
|
|
10038
10173
|
@bind
|
|
10039
10174
|
_setShiftKeyTimeout() {
|
|
10040
|
-
this.inputs.
|
|
10175
|
+
this.inputs.setShiftKey(false)
|
|
10041
10176
|
this.dispatch({
|
|
10042
10177
|
type: 'keyboard',
|
|
10043
10178
|
name: 'key_up',
|
|
10044
10179
|
key: 'Shift',
|
|
10045
|
-
shiftKey: this.inputs.
|
|
10046
|
-
ctrlKey: this.inputs.
|
|
10047
|
-
altKey: this.inputs.
|
|
10048
|
-
metaKey: this.inputs.
|
|
10049
|
-
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(),
|
|
10050
10185
|
code: 'ShiftLeft',
|
|
10051
10186
|
})
|
|
10052
10187
|
}
|
|
@@ -10057,16 +10192,16 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10057
10192
|
/** @internal */
|
|
10058
10193
|
@bind
|
|
10059
10194
|
_setAltKeyTimeout() {
|
|
10060
|
-
this.inputs.
|
|
10195
|
+
this.inputs.setAltKey(false)
|
|
10061
10196
|
this.dispatch({
|
|
10062
10197
|
type: 'keyboard',
|
|
10063
10198
|
name: 'key_up',
|
|
10064
10199
|
key: 'Alt',
|
|
10065
|
-
shiftKey: this.inputs.
|
|
10066
|
-
ctrlKey: this.inputs.
|
|
10067
|
-
altKey: this.inputs.
|
|
10068
|
-
metaKey: this.inputs.
|
|
10069
|
-
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(),
|
|
10070
10205
|
code: 'AltLeft',
|
|
10071
10206
|
})
|
|
10072
10207
|
}
|
|
@@ -10077,16 +10212,16 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10077
10212
|
/** @internal */
|
|
10078
10213
|
@bind
|
|
10079
10214
|
_setCtrlKeyTimeout() {
|
|
10080
|
-
this.inputs.
|
|
10215
|
+
this.inputs.setCtrlKey(false)
|
|
10081
10216
|
this.dispatch({
|
|
10082
10217
|
type: 'keyboard',
|
|
10083
10218
|
name: 'key_up',
|
|
10084
10219
|
key: 'Ctrl',
|
|
10085
|
-
shiftKey: this.inputs.
|
|
10086
|
-
ctrlKey: this.inputs.
|
|
10087
|
-
altKey: this.inputs.
|
|
10088
|
-
metaKey: this.inputs.
|
|
10089
|
-
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(),
|
|
10090
10225
|
code: 'ControlLeft',
|
|
10091
10226
|
})
|
|
10092
10227
|
}
|
|
@@ -10097,16 +10232,16 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10097
10232
|
/** @internal */
|
|
10098
10233
|
@bind
|
|
10099
10234
|
_setMetaKeyTimeout() {
|
|
10100
|
-
this.inputs.
|
|
10235
|
+
this.inputs.setMetaKey(false)
|
|
10101
10236
|
this.dispatch({
|
|
10102
10237
|
type: 'keyboard',
|
|
10103
10238
|
name: 'key_up',
|
|
10104
10239
|
key: 'Meta',
|
|
10105
|
-
shiftKey: this.inputs.
|
|
10106
|
-
ctrlKey: this.inputs.
|
|
10107
|
-
altKey: this.inputs.
|
|
10108
|
-
metaKey: this.inputs.
|
|
10109
|
-
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(),
|
|
10110
10245
|
code: 'MetaLeft',
|
|
10111
10246
|
})
|
|
10112
10247
|
}
|
|
@@ -10114,9 +10249,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10114
10249
|
/** @internal */
|
|
10115
10250
|
private _restoreToolId = 'select'
|
|
10116
10251
|
|
|
10117
|
-
/** @internal */
|
|
10118
|
-
private _pinchStart = 1
|
|
10119
|
-
|
|
10120
10252
|
/** @internal */
|
|
10121
10253
|
private _didPinch = false
|
|
10122
10254
|
|
|
@@ -10223,55 +10355,54 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10223
10355
|
if (info.type === 'misc') {
|
|
10224
10356
|
// stop panning if the interaction is cancelled or completed
|
|
10225
10357
|
if (info.name === 'cancel' || info.name === 'complete') {
|
|
10226
|
-
this.inputs.
|
|
10358
|
+
this.inputs.setIsDragging(false)
|
|
10227
10359
|
|
|
10228
|
-
if (this.inputs.
|
|
10229
|
-
this.inputs.
|
|
10230
|
-
this.inputs.
|
|
10360
|
+
if (this.inputs.getIsPanning()) {
|
|
10361
|
+
this.inputs.setIsPanning(false)
|
|
10362
|
+
this.inputs.setIsSpacebarPanning(false)
|
|
10231
10363
|
this.setCursor({ type: this._prevCursor, rotation: 0 })
|
|
10232
10364
|
}
|
|
10233
10365
|
}
|
|
10234
10366
|
|
|
10235
10367
|
this.root.handleEvent(info)
|
|
10368
|
+
this.emit('event', info)
|
|
10236
10369
|
return
|
|
10237
10370
|
}
|
|
10238
10371
|
|
|
10239
10372
|
if (info.shiftKey) {
|
|
10240
10373
|
clearTimeout(this._shiftKeyTimeout)
|
|
10241
10374
|
this._shiftKeyTimeout = -1
|
|
10242
|
-
inputs.
|
|
10243
|
-
} else if (!info.shiftKey && inputs.
|
|
10375
|
+
inputs.setShiftKey(true)
|
|
10376
|
+
} else if (!info.shiftKey && inputs.getShiftKey() && this._shiftKeyTimeout === -1) {
|
|
10244
10377
|
this._shiftKeyTimeout = this.timers.setTimeout(this._setShiftKeyTimeout, 150)
|
|
10245
10378
|
}
|
|
10246
10379
|
|
|
10247
10380
|
if (info.altKey) {
|
|
10248
10381
|
clearTimeout(this._altKeyTimeout)
|
|
10249
10382
|
this._altKeyTimeout = -1
|
|
10250
|
-
inputs.
|
|
10251
|
-
} else if (!info.altKey && inputs.
|
|
10383
|
+
inputs.setAltKey(true)
|
|
10384
|
+
} else if (!info.altKey && inputs.getAltKey() && this._altKeyTimeout === -1) {
|
|
10252
10385
|
this._altKeyTimeout = this.timers.setTimeout(this._setAltKeyTimeout, 150)
|
|
10253
10386
|
}
|
|
10254
10387
|
|
|
10255
10388
|
if (info.ctrlKey) {
|
|
10256
10389
|
clearTimeout(this._ctrlKeyTimeout)
|
|
10257
10390
|
this._ctrlKeyTimeout = -1
|
|
10258
|
-
inputs.
|
|
10259
|
-
} else if (!info.ctrlKey && inputs.
|
|
10391
|
+
inputs.setCtrlKey(true)
|
|
10392
|
+
} else if (!info.ctrlKey && inputs.getCtrlKey() && this._ctrlKeyTimeout === -1) {
|
|
10260
10393
|
this._ctrlKeyTimeout = this.timers.setTimeout(this._setCtrlKeyTimeout, 150)
|
|
10261
10394
|
}
|
|
10262
10395
|
|
|
10263
10396
|
if (info.metaKey) {
|
|
10264
10397
|
clearTimeout(this._metaKeyTimeout)
|
|
10265
10398
|
this._metaKeyTimeout = -1
|
|
10266
|
-
inputs.
|
|
10267
|
-
} else if (!info.metaKey && inputs.
|
|
10399
|
+
inputs.setMetaKey(true)
|
|
10400
|
+
} else if (!info.metaKey && inputs.getMetaKey() && this._metaKeyTimeout === -1) {
|
|
10268
10401
|
this._metaKeyTimeout = this.timers.setTimeout(this._setMetaKeyTimeout, 150)
|
|
10269
10402
|
}
|
|
10270
10403
|
|
|
10271
|
-
|
|
10272
|
-
|
|
10273
|
-
if (!inputs.isPointing) {
|
|
10274
|
-
inputs.isDragging = false
|
|
10404
|
+
if (!inputs.getIsPointing()) {
|
|
10405
|
+
inputs.setIsDragging(false)
|
|
10275
10406
|
}
|
|
10276
10407
|
|
|
10277
10408
|
const instanceState = this.store.unsafeGetWithoutCapture(TLINSTANCE_ID)!
|
|
@@ -10282,29 +10413,29 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10282
10413
|
case 'pinch': {
|
|
10283
10414
|
if (cameraOptions.isLocked) return
|
|
10284
10415
|
clearTimeout(this._longPressTimeout)
|
|
10285
|
-
this.
|
|
10416
|
+
this.inputs.updateFromEvent(info)
|
|
10286
10417
|
|
|
10287
10418
|
switch (info.name) {
|
|
10288
10419
|
case 'pinch_start': {
|
|
10289
|
-
if (inputs.
|
|
10420
|
+
if (inputs.getIsPinching()) return
|
|
10290
10421
|
|
|
10291
|
-
if (!inputs.
|
|
10292
|
-
this._pinchStart = this.getCamera().z
|
|
10422
|
+
if (!inputs.getIsEditing()) {
|
|
10293
10423
|
if (!this._selectedShapeIdsAtPointerDown.length) {
|
|
10294
10424
|
this._selectedShapeIdsAtPointerDown = [...pageState.selectedShapeIds]
|
|
10295
10425
|
}
|
|
10296
10426
|
|
|
10297
10427
|
this._didPinch = true
|
|
10298
10428
|
|
|
10299
|
-
inputs.
|
|
10429
|
+
inputs.setIsPinching(true)
|
|
10300
10430
|
|
|
10301
10431
|
this.interrupt()
|
|
10302
10432
|
}
|
|
10303
10433
|
|
|
10434
|
+
this.emit('event', info)
|
|
10304
10435
|
return // Stop here!
|
|
10305
10436
|
}
|
|
10306
10437
|
case 'pinch': {
|
|
10307
|
-
if (!inputs.
|
|
10438
|
+
if (!inputs.getIsPinching()) return
|
|
10308
10439
|
|
|
10309
10440
|
const {
|
|
10310
10441
|
point: { z = 1 },
|
|
@@ -10335,13 +10466,14 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10335
10466
|
{ immediate: true }
|
|
10336
10467
|
)
|
|
10337
10468
|
|
|
10469
|
+
this.emit('event', info)
|
|
10338
10470
|
return // Stop here!
|
|
10339
10471
|
}
|
|
10340
10472
|
case 'pinch_end': {
|
|
10341
|
-
if (!inputs.
|
|
10473
|
+
if (!inputs.getIsPinching()) return this
|
|
10342
10474
|
|
|
10343
10475
|
// Stop pinching
|
|
10344
|
-
inputs.
|
|
10476
|
+
inputs.setIsPinching(false)
|
|
10345
10477
|
|
|
10346
10478
|
// Stash and clear the shapes that were selected when the pinch started
|
|
10347
10479
|
const { _selectedShapeIdsAtPointerDown: shapesToReselect } = this
|
|
@@ -10361,6 +10493,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10361
10493
|
}
|
|
10362
10494
|
}
|
|
10363
10495
|
|
|
10496
|
+
this.emit('event', info)
|
|
10364
10497
|
return // Stop here!
|
|
10365
10498
|
}
|
|
10366
10499
|
}
|
|
@@ -10368,7 +10501,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10368
10501
|
case 'wheel': {
|
|
10369
10502
|
if (cameraOptions.isLocked) return
|
|
10370
10503
|
|
|
10371
|
-
this.
|
|
10504
|
+
this.inputs.updateFromEvent(info)
|
|
10372
10505
|
|
|
10373
10506
|
const { panSpeed, zoomSpeed } = cameraOptions
|
|
10374
10507
|
let wheelBehavior = cameraOptions.wheelBehavior
|
|
@@ -10399,7 +10532,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10399
10532
|
switch (behavior) {
|
|
10400
10533
|
case 'zoom': {
|
|
10401
10534
|
// Zoom in on current screen point using the wheel delta
|
|
10402
|
-
const { x, y } = this.inputs.
|
|
10535
|
+
const { x, y } = this.inputs.getCurrentScreenPoint()
|
|
10403
10536
|
let delta = dz
|
|
10404
10537
|
|
|
10405
10538
|
// If we're forcing zoom, then we need to do the wheel normalization math here
|
|
@@ -10416,6 +10549,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10416
10549
|
immediate: true,
|
|
10417
10550
|
})
|
|
10418
10551
|
this.maybeTrackPerformance('Zooming')
|
|
10552
|
+
this.root.handleEvent(info)
|
|
10553
|
+
this.emit('event', info)
|
|
10419
10554
|
return
|
|
10420
10555
|
}
|
|
10421
10556
|
case 'pan': {
|
|
@@ -10424,6 +10559,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10424
10559
|
immediate: true,
|
|
10425
10560
|
})
|
|
10426
10561
|
this.maybeTrackPerformance('Panning')
|
|
10562
|
+
this.root.handleEvent(info)
|
|
10563
|
+
this.emit('event', info)
|
|
10427
10564
|
return
|
|
10428
10565
|
}
|
|
10429
10566
|
}
|
|
@@ -10432,9 +10569,9 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10432
10569
|
}
|
|
10433
10570
|
case 'pointer': {
|
|
10434
10571
|
// Ignore pointer events while we're pinching
|
|
10435
|
-
if (inputs.
|
|
10572
|
+
if (inputs.getIsPinching()) return
|
|
10436
10573
|
|
|
10437
|
-
this.
|
|
10574
|
+
this.inputs.updateFromEvent(info)
|
|
10438
10575
|
const { isPen } = info
|
|
10439
10576
|
const { isPenMode } = instanceState
|
|
10440
10577
|
|
|
@@ -10443,7 +10580,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10443
10580
|
// If we're in pen mode and the input is not a pen type, then stop here
|
|
10444
10581
|
if (isPenMode && !isPen) return
|
|
10445
10582
|
|
|
10446
|
-
if (!this.inputs.
|
|
10583
|
+
if (!this.inputs.getIsPanning()) {
|
|
10447
10584
|
// Start a long press timeout
|
|
10448
10585
|
this._longPressTimeout = this.timers.setTimeout(() => {
|
|
10449
10586
|
const vsb = this.getViewportScreenBounds()
|
|
@@ -10453,7 +10590,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10453
10590
|
// viewport bounds, and will be again when this event is handled...
|
|
10454
10591
|
// so we need to counter-adjust from the stored value so that the
|
|
10455
10592
|
// new value is set correctly.
|
|
10456
|
-
point: this.inputs.
|
|
10593
|
+
point: this.inputs.getOriginScreenPoint().clone().addXY(vsb.x, vsb.y),
|
|
10457
10594
|
name: 'long_press',
|
|
10458
10595
|
})
|
|
10459
10596
|
}, this.options.longPressDurationMs)
|
|
@@ -10470,8 +10607,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10470
10607
|
inputs.buttons.add(info.button)
|
|
10471
10608
|
|
|
10472
10609
|
// Start pointing and stop dragging
|
|
10473
|
-
inputs.
|
|
10474
|
-
inputs.
|
|
10610
|
+
inputs.setIsPointing(true)
|
|
10611
|
+
inputs.setIsDragging(false)
|
|
10475
10612
|
|
|
10476
10613
|
// If pen mode is off but we're not already in pen mode, turn that on
|
|
10477
10614
|
if (!isPenMode && isPen) this.updateInstanceState({ isPenMode: true })
|
|
@@ -10483,16 +10620,16 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10483
10620
|
this.setCurrentTool('eraser')
|
|
10484
10621
|
} else if (info.button === MIDDLE_MOUSE_BUTTON) {
|
|
10485
10622
|
// Middle mouse pan activates panning unless we're already panning (with spacebar)
|
|
10486
|
-
if (!this.inputs.
|
|
10623
|
+
if (!this.inputs.getIsPanning()) {
|
|
10487
10624
|
this._prevCursor = this.getInstanceState().cursor.type
|
|
10488
10625
|
}
|
|
10489
|
-
this.inputs.
|
|
10626
|
+
this.inputs.setIsPanning(true)
|
|
10490
10627
|
clearTimeout(this._longPressTimeout)
|
|
10491
10628
|
}
|
|
10492
10629
|
|
|
10493
10630
|
// We might be panning because we did a middle mouse click, or because we're holding spacebar and started a regular click
|
|
10494
10631
|
// Also stop here, we don't want the state chart to receive the event
|
|
10495
|
-
if (this.inputs.
|
|
10632
|
+
if (this.inputs.getIsPanning()) {
|
|
10496
10633
|
this.stopCameraAnimation()
|
|
10497
10634
|
this.setCursor({ type: 'grabbing', rotation: 0 })
|
|
10498
10635
|
return this
|
|
@@ -10507,9 +10644,10 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10507
10644
|
const { x: cx, y: cy, z: cz } = unsafe__withoutCapture(() => this.getCamera())
|
|
10508
10645
|
|
|
10509
10646
|
// If we've started panning, then clear any long press timeout
|
|
10510
|
-
if (this.inputs.
|
|
10647
|
+
if (this.inputs.getIsPanning() && this.inputs.getIsPointing()) {
|
|
10511
10648
|
// Handle spacebar / middle mouse button panning
|
|
10512
|
-
const
|
|
10649
|
+
const currentScreenPoint = this.inputs.getCurrentScreenPoint()
|
|
10650
|
+
const previousScreenPoint = this.inputs.getPreviousScreenPoint()
|
|
10513
10651
|
const offset = Vec.Sub(currentScreenPoint, previousScreenPoint)
|
|
10514
10652
|
this.setCamera(new Vec(cx + offset.x / cz, cy + offset.y / cz, cz), {
|
|
10515
10653
|
immediate: true,
|
|
@@ -10519,24 +10657,25 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10519
10657
|
}
|
|
10520
10658
|
|
|
10521
10659
|
if (
|
|
10522
|
-
inputs.
|
|
10523
|
-
!inputs.
|
|
10524
|
-
Vec.Dist2(
|
|
10660
|
+
inputs.getIsPointing() &&
|
|
10661
|
+
!inputs.getIsDragging() &&
|
|
10662
|
+
Vec.Dist2(inputs.getOriginPagePoint(), inputs.getCurrentPagePoint()) *
|
|
10663
|
+
this.getZoomLevel() >
|
|
10525
10664
|
(instanceState.isCoarsePointer
|
|
10526
10665
|
? this.options.coarseDragDistanceSquared
|
|
10527
10666
|
: this.options.dragDistanceSquared) /
|
|
10528
10667
|
cz
|
|
10529
10668
|
) {
|
|
10530
10669
|
// Start dragging
|
|
10531
|
-
inputs.
|
|
10670
|
+
inputs.setIsDragging(true)
|
|
10532
10671
|
clearTimeout(this._longPressTimeout)
|
|
10533
10672
|
}
|
|
10534
10673
|
break
|
|
10535
10674
|
}
|
|
10536
10675
|
case 'pointer_up': {
|
|
10537
10676
|
// Stop dragging / pointing
|
|
10538
|
-
inputs.
|
|
10539
|
-
inputs.
|
|
10677
|
+
inputs.setIsDragging(false)
|
|
10678
|
+
inputs.setIsPointing(false)
|
|
10540
10679
|
clearTimeout(this._longPressTimeout)
|
|
10541
10680
|
|
|
10542
10681
|
// Remove the button from the buttons set
|
|
@@ -10553,12 +10692,12 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10553
10692
|
info.button = 0
|
|
10554
10693
|
}
|
|
10555
10694
|
|
|
10556
|
-
if (inputs.
|
|
10695
|
+
if (inputs.getIsPanning()) {
|
|
10557
10696
|
if (!inputs.keys.has('Space')) {
|
|
10558
|
-
inputs.
|
|
10559
|
-
inputs.
|
|
10697
|
+
inputs.setIsPanning(false)
|
|
10698
|
+
inputs.setIsSpacebarPanning(false)
|
|
10560
10699
|
}
|
|
10561
|
-
const slideDirection = this.inputs.
|
|
10700
|
+
const slideDirection = this.inputs.getPointerVelocity()
|
|
10562
10701
|
const slideSpeed = Math.min(2, slideDirection.len())
|
|
10563
10702
|
|
|
10564
10703
|
switch (info.button) {
|
|
@@ -10602,43 +10741,48 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10602
10741
|
// Add the key from the keys set
|
|
10603
10742
|
inputs.keys.add(info.code)
|
|
10604
10743
|
|
|
10605
|
-
|
|
10606
|
-
|
|
10607
|
-
if (!
|
|
10608
|
-
this.
|
|
10609
|
-
|
|
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
|
+
}
|
|
10610
10750
|
|
|
10611
|
-
|
|
10612
|
-
|
|
10613
|
-
|
|
10614
|
-
|
|
10615
|
-
|
|
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
|
+
}
|
|
10616
10759
|
|
|
10617
|
-
|
|
10618
|
-
|
|
10619
|
-
|
|
10620
|
-
|
|
10621
|
-
|
|
10622
|
-
|
|
10623
|
-
|
|
10624
|
-
|
|
10625
|
-
|
|
10626
|
-
|
|
10627
|
-
|
|
10628
|
-
|
|
10629
|
-
|
|
10630
|
-
|
|
10631
|
-
|
|
10632
|
-
|
|
10633
|
-
|
|
10634
|
-
|
|
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
|
+
}
|
|
10635
10779
|
}
|
|
10636
|
-
}
|
|
10637
10780
|
|
|
10638
|
-
|
|
10639
|
-
|
|
10640
|
-
|
|
10641
|
-
|
|
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
|
+
}
|
|
10642
10786
|
}
|
|
10643
10787
|
}
|
|
10644
10788
|
|
|
@@ -10648,15 +10792,17 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10648
10792
|
// Remove the key from the keys set
|
|
10649
10793
|
inputs.keys.delete(info.code)
|
|
10650
10794
|
|
|
10651
|
-
|
|
10652
|
-
|
|
10653
|
-
if (
|
|
10654
|
-
|
|
10655
|
-
|
|
10656
|
-
|
|
10657
|
-
|
|
10658
|
-
|
|
10659
|
-
|
|
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
|
+
}
|
|
10660
10806
|
}
|
|
10661
10807
|
}
|
|
10662
10808
|
break
|
|
@@ -10730,7 +10876,10 @@ function alertMaxShapes(editor: Editor, pageId = editor.getCurrentPageId()) {
|
|
|
10730
10876
|
|
|
10731
10877
|
function applyPartialToRecordWithProps<
|
|
10732
10878
|
T extends UnknownRecord & { type: string; props: object; meta: object },
|
|
10733
|
-
>(
|
|
10879
|
+
>(
|
|
10880
|
+
prev: T,
|
|
10881
|
+
partial?: T extends T ? Omit<Partial<T>, 'props'> & { props?: Partial<T['props']> } : never
|
|
10882
|
+
): T {
|
|
10734
10883
|
if (!partial) return prev
|
|
10735
10884
|
let next = null as null | T
|
|
10736
10885
|
const entries = Object.entries(partial)
|