@tldraw/editor 4.3.0-next.82cfddd7ee89 → 4.3.0-next.842fb21476f2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-cjs/index.d.ts +498 -155
- package/dist-cjs/index.js +6 -1
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/components/ErrorBoundary.js.map +1 -1
- package/dist-cjs/lib/components/GeometryDebuggingView.js +1 -17
- package/dist-cjs/lib/components/GeometryDebuggingView.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js +3 -3
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
- package/dist-cjs/lib/constants.js +1 -3
- package/dist-cjs/lib/constants.js.map +2 -2
- package/dist-cjs/lib/editor/Editor.js +292 -286
- 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 +18 -17
- package/dist-cjs/lib/editor/derivations/notVisibleShapes.js.map +3 -3
- package/dist-cjs/lib/editor/derivations/parentsToChildren.js +12 -3
- package/dist-cjs/lib/editor/derivations/parentsToChildren.js.map +2 -2
- package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js +1 -1
- package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.js +5 -6
- package/dist-cjs/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/InputsManager/InputsManager.js +591 -0
- package/dist-cjs/lib/editor/managers/InputsManager/InputsManager.js.map +7 -0
- package/dist-cjs/lib/editor/managers/SnapManager/SnapManager.js +1 -1
- package/dist-cjs/lib/editor/managers/SnapManager/SnapManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/TickManager/TickManager.js +1 -22
- package/dist-cjs/lib/editor/managers/TickManager/TickManager.js.map +2 -2
- package/dist-cjs/lib/editor/shapes/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 +498 -155
- package/dist-esm/index.mjs +7 -2
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/components/ErrorBoundary.mjs.map +1 -1
- package/dist-esm/lib/components/GeometryDebuggingView.mjs +1 -17
- package/dist-esm/lib/components/GeometryDebuggingView.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +3 -3
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
- package/dist-esm/lib/constants.mjs +1 -3
- package/dist-esm/lib/constants.mjs.map +2 -2
- package/dist-esm/lib/editor/Editor.mjs +293 -289
- 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 +18 -17
- package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs.map +3 -3
- package/dist-esm/lib/editor/derivations/parentsToChildren.mjs +13 -4
- package/dist-esm/lib/editor/derivations/parentsToChildren.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs +1 -1
- package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.mjs +5 -6
- package/dist-esm/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/InputsManager/InputsManager.mjs +573 -0
- package/dist-esm/lib/editor/managers/InputsManager/InputsManager.mjs.map +7 -0
- package/dist-esm/lib/editor/managers/SnapManager/SnapManager.mjs +1 -1
- package/dist-esm/lib/editor/managers/SnapManager/SnapManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs +1 -22
- package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs.map +2 -2
- package/dist-esm/lib/editor/shapes/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 +18 -16
- package/src/index.ts +4 -1
- package/src/lib/components/ErrorBoundary.tsx +1 -1
- package/src/lib/components/GeometryDebuggingView.tsx +1 -19
- package/src/lib/components/default-components/DefaultCanvas.tsx +4 -3
- package/src/lib/config/TLUserPreferences.test.ts +40 -0
- package/src/lib/constants.ts +0 -2
- package/src/lib/editor/Editor.test.ts +150 -10
- package/src/lib/editor/Editor.ts +459 -379
- 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 +37 -23
- package/src/lib/editor/derivations/parentsToChildren.ts +18 -7
- package/src/lib/editor/managers/ClickManager/ClickManager.test.ts +17 -31
- package/src/lib/editor/managers/ClickManager/ClickManager.ts +1 -1
- package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.test.ts +129 -79
- package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.ts +10 -6
- package/src/lib/editor/managers/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/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,18 @@ 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'
|
|
158
152
|
import { TextManager } from './managers/TextManager/TextManager'
|
|
159
153
|
import { TickManager } from './managers/TickManager/TickManager'
|
|
160
154
|
import { UserPreferencesManager } from './managers/UserPreferencesManager/UserPreferencesManager'
|
|
161
|
-
import { ShapeUtil, TLGeometryOpts, TLResizeMode } from './shapes/ShapeUtil'
|
|
155
|
+
import { ShapeUtil, TLEditStartInfo, TLGeometryOpts, TLResizeMode } from './shapes/ShapeUtil'
|
|
162
156
|
import { RootState } from './tools/RootState'
|
|
163
157
|
import { StateNode, TLStateNodeConstructor } from './tools/StateNode'
|
|
164
158
|
import { TLContent } from './types/clipboard-types'
|
|
165
159
|
import { TLEventMap } from './types/emit-types'
|
|
166
|
-
import {
|
|
167
|
-
TLEventInfo,
|
|
168
|
-
TLPinchEventInfo,
|
|
169
|
-
TLPointerEventInfo,
|
|
170
|
-
TLWheelEventInfo,
|
|
171
|
-
} from './types/event-types'
|
|
160
|
+
import { TLEventInfo, TLPointerEventInfo } from './types/event-types'
|
|
172
161
|
import { TLExternalAsset, TLExternalContent } from './types/external-content'
|
|
173
162
|
import { TLHistoryBatchOptions } from './types/history-types'
|
|
174
163
|
import {
|
|
@@ -199,7 +188,7 @@ export type TLResizeShapeOptions = Partial<{
|
|
|
199
188
|
/** @public */
|
|
200
189
|
export interface TLEditorOptions {
|
|
201
190
|
/**
|
|
202
|
-
* The Store instance to use for keeping the
|
|
191
|
+
* The Store instance to use for keeping the editor's data. This may be prepopulated, e.g. by loading
|
|
203
192
|
* from a server or database.
|
|
204
193
|
*/
|
|
205
194
|
store: TLStore
|
|
@@ -337,6 +326,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
337
326
|
|
|
338
327
|
this._tickManager = new TickManager(this)
|
|
339
328
|
|
|
329
|
+
this.inputs = new InputsManager(this)
|
|
330
|
+
|
|
340
331
|
class NewRoot extends RootState {
|
|
341
332
|
static override initial = initialState ?? ''
|
|
342
333
|
}
|
|
@@ -447,7 +438,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
447
438
|
let deletedBindings = new Map<TLBindingId, BindingOnDeleteOptions<any>>()
|
|
448
439
|
const deletedShapeIds = new Set<TLShapeId>()
|
|
449
440
|
const invalidParents = new Set<TLShapeId>()
|
|
450
|
-
let invalidBindingTypes = new Set<
|
|
441
|
+
let invalidBindingTypes = new Set<TLBinding['type']>()
|
|
451
442
|
this.disposables.add(
|
|
452
443
|
this.sideEffects.registerOperationCompleteHandler(() => {
|
|
453
444
|
// this needs to be cleared here because further effects may delete more shapes
|
|
@@ -710,7 +701,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
710
701
|
if (filtered.length > 0) {
|
|
711
702
|
const commonGroupAncestor = this.findCommonAncestor(
|
|
712
703
|
compact(filtered.map((id) => this.getShape(id))),
|
|
713
|
-
(shape) => this.isShapeOfType
|
|
704
|
+
(shape) => this.isShapeOfType(shape, 'group')
|
|
714
705
|
)
|
|
715
706
|
|
|
716
707
|
if (commonGroupAncestor) {
|
|
@@ -871,7 +862,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
871
862
|
}
|
|
872
863
|
|
|
873
864
|
/**
|
|
874
|
-
* A set of functions to call when the
|
|
865
|
+
* A set of functions to call when the editor is disposed.
|
|
875
866
|
*
|
|
876
867
|
* @public
|
|
877
868
|
*/
|
|
@@ -884,11 +875,21 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
884
875
|
*/
|
|
885
876
|
isDisposed = false
|
|
886
877
|
|
|
887
|
-
/**
|
|
888
|
-
|
|
878
|
+
/**
|
|
879
|
+
* A manager for the editor's tick events.
|
|
880
|
+
*
|
|
881
|
+
* @internal */
|
|
882
|
+
private readonly _tickManager: TickManager
|
|
883
|
+
|
|
884
|
+
/**
|
|
885
|
+
* A manager for the editor's input state.
|
|
886
|
+
*
|
|
887
|
+
* @public
|
|
888
|
+
*/
|
|
889
|
+
readonly inputs: InputsManager
|
|
889
890
|
|
|
890
891
|
/**
|
|
891
|
-
* A manager for the
|
|
892
|
+
* A manager for the editor's snapping feature.
|
|
892
893
|
*
|
|
893
894
|
* @public
|
|
894
895
|
*/
|
|
@@ -973,6 +974,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
973
974
|
this.disposables.clear()
|
|
974
975
|
this.store.dispose()
|
|
975
976
|
this.isDisposed = true
|
|
977
|
+
this.emit('dispose')
|
|
976
978
|
}
|
|
977
979
|
|
|
978
980
|
/* ------------------- Shape Utils ------------------ */
|
|
@@ -982,7 +984,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
982
984
|
*
|
|
983
985
|
* @public
|
|
984
986
|
*/
|
|
985
|
-
shapeUtils: { readonly [K in string]?: ShapeUtil<
|
|
987
|
+
shapeUtils: { readonly [K in string]?: ShapeUtil<TLShape> }
|
|
986
988
|
|
|
987
989
|
styleProps: { [key: string]: Map<StyleProp<any>, string> }
|
|
988
990
|
|
|
@@ -1001,8 +1003,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1001
1003
|
*
|
|
1002
1004
|
* @public
|
|
1003
1005
|
*/
|
|
1004
|
-
getShapeUtil<
|
|
1005
|
-
getShapeUtil<S extends
|
|
1006
|
+
getShapeUtil<K extends TLShape['type']>(type: K): ShapeUtil<Extract<TLShape, { type: K }>>
|
|
1007
|
+
getShapeUtil<S extends TLShape>(shape: S | TLShapePartial<S> | S['type']): ShapeUtil<S>
|
|
1006
1008
|
getShapeUtil<T extends ShapeUtil>(type: T extends ShapeUtil<infer R> ? R['type'] : string): T
|
|
1007
1009
|
getShapeUtil(arg: string | { type: string }) {
|
|
1008
1010
|
const type = typeof arg === 'string' ? arg : arg.type
|
|
@@ -1016,8 +1018,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1016
1018
|
*
|
|
1017
1019
|
* @param shape - A shape, shape partial, or shape type.
|
|
1018
1020
|
*/
|
|
1019
|
-
hasShapeUtil
|
|
1020
|
-
hasShapeUtil
|
|
1021
|
+
hasShapeUtil(shape: TLShape | TLShapePartial<TLShape>): boolean
|
|
1022
|
+
hasShapeUtil(type: TLShape['type']): boolean
|
|
1021
1023
|
hasShapeUtil<T extends ShapeUtil>(
|
|
1022
1024
|
type: T extends ShapeUtil<infer R> ? R['type'] : string
|
|
1023
1025
|
): boolean
|
|
@@ -1032,7 +1034,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1032
1034
|
*
|
|
1033
1035
|
* @public
|
|
1034
1036
|
*/
|
|
1035
|
-
bindingUtils: { readonly [K in string]?: BindingUtil<
|
|
1037
|
+
bindingUtils: { readonly [K in string]?: BindingUtil<TLBinding> }
|
|
1036
1038
|
|
|
1037
1039
|
/**
|
|
1038
1040
|
* Get a binding util from a binding itself.
|
|
@@ -1049,8 +1051,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1049
1051
|
*
|
|
1050
1052
|
* @public
|
|
1051
1053
|
*/
|
|
1052
|
-
getBindingUtil<
|
|
1053
|
-
getBindingUtil<S extends
|
|
1054
|
+
getBindingUtil<K extends TLBinding['type']>(type: K): BindingUtil<Extract<TLBinding, { type: K }>>
|
|
1055
|
+
getBindingUtil<S extends TLBinding>(binding: S | { type: S['type'] }): BindingUtil<S>
|
|
1054
1056
|
getBindingUtil<T extends BindingUtil>(
|
|
1055
1057
|
type: T extends BindingUtil<infer R> ? R['type'] : string
|
|
1056
1058
|
): T
|
|
@@ -1064,7 +1066,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1064
1066
|
/* --------------------- History -------------------- */
|
|
1065
1067
|
|
|
1066
1068
|
/**
|
|
1067
|
-
* A manager for the
|
|
1069
|
+
* A manager for the editor's history.
|
|
1068
1070
|
*
|
|
1069
1071
|
* @readonly
|
|
1070
1072
|
*/
|
|
@@ -1088,14 +1090,18 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1088
1090
|
}
|
|
1089
1091
|
|
|
1090
1092
|
/**
|
|
1091
|
-
* Whether the
|
|
1093
|
+
* Whether the editor can undo.
|
|
1092
1094
|
*
|
|
1093
1095
|
* @public
|
|
1094
1096
|
*/
|
|
1095
|
-
@computed
|
|
1097
|
+
@computed canUndo(): boolean {
|
|
1096
1098
|
return this.history.getNumUndos() > 0
|
|
1097
1099
|
}
|
|
1098
1100
|
|
|
1101
|
+
getCanUndo() {
|
|
1102
|
+
return this.canUndo()
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1099
1105
|
/**
|
|
1100
1106
|
* Redo to the next mark.
|
|
1101
1107
|
*
|
|
@@ -1113,20 +1119,24 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1113
1119
|
return this
|
|
1114
1120
|
}
|
|
1115
1121
|
|
|
1116
|
-
clearHistory() {
|
|
1117
|
-
this.history.clear()
|
|
1118
|
-
return this
|
|
1119
|
-
}
|
|
1120
|
-
|
|
1121
1122
|
/**
|
|
1122
|
-
* Whether the
|
|
1123
|
+
* Whether the editor can redo.
|
|
1123
1124
|
*
|
|
1124
1125
|
* @public
|
|
1125
1126
|
*/
|
|
1126
|
-
@computed
|
|
1127
|
+
@computed canRedo(): boolean {
|
|
1127
1128
|
return this.history.getNumRedos() > 0
|
|
1128
1129
|
}
|
|
1129
1130
|
|
|
1131
|
+
getCanRedo() {
|
|
1132
|
+
return this.canRedo()
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
clearHistory() {
|
|
1136
|
+
this.history.clear()
|
|
1137
|
+
return this
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1130
1140
|
/**
|
|
1131
1141
|
* Create a new "mark", or stopping point, in the undo redo history. Creating a mark will clear
|
|
1132
1142
|
* any redos. You typically want to do this just before a user interaction begins or is handled.
|
|
@@ -1300,7 +1310,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1300
1310
|
}),
|
|
1301
1311
|
selectionCount: this.getSelectedShapes().length,
|
|
1302
1312
|
editingShape: editingShapeId ? this.getShape(editingShapeId) : undefined,
|
|
1303
|
-
inputs: this.inputs,
|
|
1313
|
+
inputs: this.inputs.toJson(),
|
|
1304
1314
|
pageState: this.getCurrentPageState(),
|
|
1305
1315
|
instanceState: this.getInstanceState(),
|
|
1306
1316
|
collaboratorCount: this.getCollaboratorsOnCurrentPage().length,
|
|
@@ -1325,7 +1335,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1325
1335
|
* we're in a transaction that's about to be rolled back due to the same error we're currently
|
|
1326
1336
|
* reporting.
|
|
1327
1337
|
*
|
|
1328
|
-
* Instead, to listen to changes to this value, you need to listen to
|
|
1338
|
+
* Instead, to listen to changes to this value, you need to listen to editor's `crash` event.
|
|
1329
1339
|
*
|
|
1330
1340
|
* @internal
|
|
1331
1341
|
*/
|
|
@@ -2028,7 +2038,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2028
2038
|
}
|
|
2029
2039
|
|
|
2030
2040
|
/**
|
|
2031
|
-
* The id of the
|
|
2041
|
+
* The id of the editor's only selected shape.
|
|
2032
2042
|
*
|
|
2033
2043
|
* @returns Null if there is no shape or more than one selected shape, otherwise the selected shape's id.
|
|
2034
2044
|
*
|
|
@@ -2040,7 +2050,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2040
2050
|
}
|
|
2041
2051
|
|
|
2042
2052
|
/**
|
|
2043
|
-
* The
|
|
2053
|
+
* The editor's only selected shape.
|
|
2044
2054
|
*
|
|
2045
2055
|
* @returns Null if there is no shape or more than one selected shape, otherwise the selected shape.
|
|
2046
2056
|
*
|
|
@@ -2220,7 +2230,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2220
2230
|
throw Error(`Editor.setFocusedGroup: Shape with id ${id} does not exist`)
|
|
2221
2231
|
}
|
|
2222
2232
|
|
|
2223
|
-
if (!this.isShapeOfType
|
|
2233
|
+
if (!this.isShapeOfType(shape, 'group')) {
|
|
2224
2234
|
throw Error(
|
|
2225
2235
|
`Editor.setFocusedGroup: Cannot set focused group to shape of type ${shape.type}`
|
|
2226
2236
|
)
|
|
@@ -2248,7 +2258,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2248
2258
|
if (focusedGroup) {
|
|
2249
2259
|
// If we have a focused layer, look for an ancestor of the focused shape that is a group
|
|
2250
2260
|
const match = this.findShapeAncestor(focusedGroup, (shape) =>
|
|
2251
|
-
this.isShapeOfType
|
|
2261
|
+
this.isShapeOfType(shape, 'group')
|
|
2252
2262
|
)
|
|
2253
2263
|
// If we have an ancestor that can become a focused layer, set it as the focused layer
|
|
2254
2264
|
this.setFocusedGroup(match?.id ?? null)
|
|
@@ -2281,6 +2291,29 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2281
2291
|
return editingShapeId ? this.getShape(editingShapeId) : undefined
|
|
2282
2292
|
}
|
|
2283
2293
|
|
|
2294
|
+
/**
|
|
2295
|
+
* Whether the shape can be edited.
|
|
2296
|
+
*
|
|
2297
|
+
* @param shape - The shape (or shape id) to check if it can be edited.
|
|
2298
|
+
* @param info - The info about the edit start.
|
|
2299
|
+
*
|
|
2300
|
+
* @public
|
|
2301
|
+
* @returns true if the shape can be edited, false otherwise.
|
|
2302
|
+
*/
|
|
2303
|
+
canEditShape<T extends TLShape | TLShapeId>(shape: T | null, info?: TLEditStartInfo): shape is T {
|
|
2304
|
+
const id = typeof shape === 'string' ? shape : (shape?.id ?? null)
|
|
2305
|
+
if (!id) return false // no shape
|
|
2306
|
+
if (id === this.getEditingShapeId()) return false // already editing this shape
|
|
2307
|
+
const _shape = this.getShape(id)
|
|
2308
|
+
if (!_shape) return false // no shape
|
|
2309
|
+
const util = this.getShapeUtil(_shape)
|
|
2310
|
+
const _info: TLEditStartInfo = info ?? { type: 'unknown' }
|
|
2311
|
+
if (!util.canEdit(_shape, _info)) return false // shape is not editable
|
|
2312
|
+
if (this.getIsReadonly() && !util.canEditInReadonly(_shape)) return false // readonly and no exception
|
|
2313
|
+
if (this.isShapeOrAncestorLocked(_shape) && !util.canEditWhileLocked(_shape)) return false // locked and no exception. Note here: we're not distinguishing between a locked shape and a shape that is the descendant of a locked shape.
|
|
2314
|
+
return true // shape is editable
|
|
2315
|
+
}
|
|
2316
|
+
|
|
2284
2317
|
/**
|
|
2285
2318
|
* Set the current editing shape.
|
|
2286
2319
|
*
|
|
@@ -2296,44 +2329,59 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2296
2329
|
*/
|
|
2297
2330
|
setEditingShape(shape: TLShapeId | TLShape | null): this {
|
|
2298
2331
|
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
2332
|
|
|
2322
|
-
|
|
2333
|
+
if (!id) {
|
|
2334
|
+
// setting the editing shape to null
|
|
2323
2335
|
this.run(
|
|
2324
2336
|
() => {
|
|
2325
|
-
|
|
2326
|
-
this.
|
|
2337
|
+
// Clean up the previous editing shape
|
|
2338
|
+
const prevEditingShapeId = this.getEditingShapeId()
|
|
2327
2339
|
if (prevEditingShapeId) {
|
|
2328
2340
|
const prevEditingShape = this.getShape(prevEditingShapeId)
|
|
2329
2341
|
if (prevEditingShape) {
|
|
2330
2342
|
this.getShapeUtil(prevEditingShape).onEditEnd?.(prevEditingShape)
|
|
2331
2343
|
}
|
|
2332
2344
|
}
|
|
2345
|
+
|
|
2346
|
+
// Clean up the editing shape state and rich text editor
|
|
2347
|
+
this._updateCurrentPageState({ editingShapeId: null })
|
|
2348
|
+
this._currentRichTextEditor.set(null)
|
|
2333
2349
|
},
|
|
2334
2350
|
{ history: 'ignore' }
|
|
2335
2351
|
)
|
|
2352
|
+
|
|
2353
|
+
return this
|
|
2336
2354
|
}
|
|
2355
|
+
|
|
2356
|
+
// id was provided but the next editing shape was not editable or didn't exist, so do nothing
|
|
2357
|
+
if (!this.canEditShape(id)) return this
|
|
2358
|
+
|
|
2359
|
+
// id was provided and the next editing shape is editable, so set the rich text editor to null
|
|
2360
|
+
this.run(
|
|
2361
|
+
() => {
|
|
2362
|
+
// Clean up the previous editing shape
|
|
2363
|
+
const prevEditingShapeId = this.getEditingShapeId()
|
|
2364
|
+
if (prevEditingShapeId) {
|
|
2365
|
+
const prevEditingShape = this.getShape(prevEditingShapeId)
|
|
2366
|
+
if (prevEditingShape) {
|
|
2367
|
+
this.getShapeUtil(prevEditingShape).onEditEnd?.(prevEditingShape)
|
|
2368
|
+
}
|
|
2369
|
+
}
|
|
2370
|
+
|
|
2371
|
+
// Clean up the editing shape state and rich text editor
|
|
2372
|
+
this._updateCurrentPageState({ editingShapeId: null })
|
|
2373
|
+
this._currentRichTextEditor.set(null)
|
|
2374
|
+
|
|
2375
|
+
// Set the new editing shape
|
|
2376
|
+
this.select(id)
|
|
2377
|
+
this._updateCurrentPageState({ editingShapeId: id })
|
|
2378
|
+
|
|
2379
|
+
const nextEditingShape = this.getShape(id)! // shape should be there because canEditShape checked it. Possible small chance that onEditEnd deleted it?
|
|
2380
|
+
this.getShapeUtil(nextEditingShape).onEditStart?.(nextEditingShape)
|
|
2381
|
+
},
|
|
2382
|
+
{ history: 'ignore' }
|
|
2383
|
+
)
|
|
2384
|
+
|
|
2337
2385
|
return this
|
|
2338
2386
|
}
|
|
2339
2387
|
|
|
@@ -2537,6 +2585,26 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2537
2585
|
return this.getCurrentPageState().croppingShapeId
|
|
2538
2586
|
}
|
|
2539
2587
|
|
|
2588
|
+
/**
|
|
2589
|
+
* Whether the shape can be cropped.
|
|
2590
|
+
*
|
|
2591
|
+
* @param shape - The shape (or shape id) to check if it can be cropped.
|
|
2592
|
+
*
|
|
2593
|
+
* @public
|
|
2594
|
+
* @returns true if the shape can be cropped, false otherwise.
|
|
2595
|
+
*/
|
|
2596
|
+
canCropShape<T extends TLShape | TLShapeId>(shape: T | null): shape is T {
|
|
2597
|
+
if (!shape) return false
|
|
2598
|
+
const id = typeof shape === 'string' ? shape : (shape?.id ?? null)
|
|
2599
|
+
if (!id) return false
|
|
2600
|
+
const _shape = this.getShape(id)
|
|
2601
|
+
if (!_shape) return false
|
|
2602
|
+
const util = this.getShapeUtil(_shape)
|
|
2603
|
+
if (!util.canCrop(_shape)) return false
|
|
2604
|
+
if (this.isShapeOrAncestorLocked(_shape)) return false
|
|
2605
|
+
return true
|
|
2606
|
+
}
|
|
2607
|
+
|
|
2540
2608
|
/**
|
|
2541
2609
|
* Set the current cropping shape.
|
|
2542
2610
|
*
|
|
@@ -2558,12 +2626,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2558
2626
|
() => {
|
|
2559
2627
|
if (!id) {
|
|
2560
2628
|
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
|
-
}
|
|
2629
|
+
} else if (this.canCropShape(id)) {
|
|
2630
|
+
this.updateCurrentPageState({ croppingShapeId: id })
|
|
2567
2631
|
}
|
|
2568
2632
|
},
|
|
2569
2633
|
{ history: 'ignore' }
|
|
@@ -2673,6 +2737,52 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2673
2737
|
return this.getCamera().z
|
|
2674
2738
|
}
|
|
2675
2739
|
|
|
2740
|
+
private _debouncedZoomLevel = atom('debounced zoom level', 1)
|
|
2741
|
+
|
|
2742
|
+
/**
|
|
2743
|
+
* Get the debounced zoom level. When the camera is moving, this returns the zoom level
|
|
2744
|
+
* from when the camera started moving rather than the current zoom level. This can be
|
|
2745
|
+
* used to avoid expensive re-renders during camera movements.
|
|
2746
|
+
*
|
|
2747
|
+
* This behavior is controlled by the `useDebouncedZoom` option. When `useDebouncedZoom`
|
|
2748
|
+
* is `false`, this method always returns the current zoom level.
|
|
2749
|
+
*
|
|
2750
|
+
* @public
|
|
2751
|
+
*/
|
|
2752
|
+
@computed getDebouncedZoomLevel() {
|
|
2753
|
+
if (this.options.debouncedZoom) {
|
|
2754
|
+
if (this.getCameraState() === 'idle') {
|
|
2755
|
+
return this.getZoomLevel()
|
|
2756
|
+
} else {
|
|
2757
|
+
return this._debouncedZoomLevel.get()
|
|
2758
|
+
}
|
|
2759
|
+
}
|
|
2760
|
+
|
|
2761
|
+
return this.getZoomLevel()
|
|
2762
|
+
}
|
|
2763
|
+
|
|
2764
|
+
@computed private _getAboveDebouncedZoomThreshold() {
|
|
2765
|
+
return this.getCurrentPageShapeIds().size > this.options.debouncedZoomThreshold
|
|
2766
|
+
}
|
|
2767
|
+
|
|
2768
|
+
/**
|
|
2769
|
+
* Get the efficient zoom level. This returns the current zoom level if there are less than 300 shapes on the page,
|
|
2770
|
+
* otherwise it returns the debounced zoom level. This can be used to avoid expensive re-renders during camera movements.
|
|
2771
|
+
*
|
|
2772
|
+
* @public
|
|
2773
|
+
* @example
|
|
2774
|
+
* ```ts
|
|
2775
|
+
* editor.getEfficientZoomLevel()
|
|
2776
|
+
* ```
|
|
2777
|
+
*
|
|
2778
|
+
* @public
|
|
2779
|
+
*/
|
|
2780
|
+
@computed getEfficientZoomLevel() {
|
|
2781
|
+
return this._getAboveDebouncedZoomThreshold()
|
|
2782
|
+
? this.getDebouncedZoomLevel()
|
|
2783
|
+
: this.getZoomLevel()
|
|
2784
|
+
}
|
|
2785
|
+
|
|
2676
2786
|
/**
|
|
2677
2787
|
* Get the camera's initial or reset zoom level.
|
|
2678
2788
|
*
|
|
@@ -2999,7 +3109,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2999
3109
|
|
|
3000
3110
|
// Dispatch a new pointer move because the pointer's page will have changed
|
|
3001
3111
|
// (its screen position will compute to a new page position given the new camera position)
|
|
3002
|
-
const
|
|
3112
|
+
const currentScreenPoint = this.inputs.getCurrentScreenPoint()
|
|
3113
|
+
const currentPagePoint = this.inputs.getCurrentPagePoint()
|
|
3003
3114
|
|
|
3004
3115
|
// compare the next page point (derived from the current camera) to the current page point
|
|
3005
3116
|
if (
|
|
@@ -3163,7 +3274,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3163
3274
|
* ```ts
|
|
3164
3275
|
* editor.zoomIn()
|
|
3165
3276
|
* editor.zoomIn(editor.getViewportScreenCenter(), { animation: { duration: 200 } })
|
|
3166
|
-
* editor.zoomIn(editor.inputs.
|
|
3277
|
+
* editor.zoomIn(editor.inputs.getCurrentScreenPoint(), { animation: { duration: 200 } })
|
|
3167
3278
|
* ```
|
|
3168
3279
|
*
|
|
3169
3280
|
* @param point - The screen point to zoom in on. Defaults to the screen center
|
|
@@ -3208,7 +3319,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3208
3319
|
* ```ts
|
|
3209
3320
|
* editor.zoomOut()
|
|
3210
3321
|
* editor.zoomOut(editor.getViewportScreenCenter(), { animation: { duration: 120 } })
|
|
3211
|
-
* editor.zoomOut(editor.inputs.
|
|
3322
|
+
* editor.zoomOut(editor.inputs.getCurrentScreenPoint(), { animation: { duration: 120 } })
|
|
3212
3323
|
* ```
|
|
3213
3324
|
*
|
|
3214
3325
|
* @param point - The point to zoom out on. Defaults to the viewport screen center.
|
|
@@ -3265,10 +3376,17 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3265
3376
|
|
|
3266
3377
|
const selectionPageBounds = this.getSelectionPageBounds()
|
|
3267
3378
|
if (selectionPageBounds) {
|
|
3268
|
-
this.
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3379
|
+
const currentZoom = this.getZoomLevel()
|
|
3380
|
+
// If already at 100%, zoom to fit the selection in the viewport
|
|
3381
|
+
// Otherwise, zoom to 100% centered on the selection
|
|
3382
|
+
if (Math.abs(currentZoom - 1) < 0.01) {
|
|
3383
|
+
this.zoomToBounds(selectionPageBounds, opts)
|
|
3384
|
+
} else {
|
|
3385
|
+
this.zoomToBounds(selectionPageBounds, {
|
|
3386
|
+
targetZoom: 1,
|
|
3387
|
+
...opts,
|
|
3388
|
+
})
|
|
3389
|
+
}
|
|
3272
3390
|
}
|
|
3273
3391
|
return this
|
|
3274
3392
|
}
|
|
@@ -3325,7 +3443,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3325
3443
|
|
|
3326
3444
|
const viewportScreenBounds = this.getViewportScreenBounds()
|
|
3327
3445
|
|
|
3328
|
-
const inset =
|
|
3446
|
+
const inset =
|
|
3447
|
+
opts?.inset ?? Math.min(this.options.zoomToFitPadding, viewportScreenBounds.width * 0.28)
|
|
3329
3448
|
|
|
3330
3449
|
const baseZoom = this.getBaseZoom()
|
|
3331
3450
|
const zoomMin = cameraOptions.zoomSteps[0]
|
|
@@ -3635,22 +3754,23 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3635
3754
|
if (_willSetInitialBounds) {
|
|
3636
3755
|
// If we have just received the initial bounds, don't center the camera.
|
|
3637
3756
|
this.updateInstanceState({ screenBounds: screenBounds.toJson(), insets })
|
|
3757
|
+
this.emit('resize', screenBounds.toJson())
|
|
3638
3758
|
this.setCamera(this.getCamera())
|
|
3639
3759
|
} else {
|
|
3640
3760
|
if (center && !this.getInstanceState().followingUserId) {
|
|
3641
3761
|
// Get the page center before the change, make the change, and restore it
|
|
3642
3762
|
const before = this.getViewportPageBounds().center
|
|
3643
3763
|
this.updateInstanceState({ screenBounds: screenBounds.toJson(), insets })
|
|
3764
|
+
this.emit('resize', screenBounds.toJson())
|
|
3644
3765
|
this.centerOnPoint(before)
|
|
3645
3766
|
} else {
|
|
3646
3767
|
// Otherwise,
|
|
3647
3768
|
this.updateInstanceState({ screenBounds: screenBounds.toJson(), insets })
|
|
3769
|
+
this.emit('resize', screenBounds.toJson())
|
|
3648
3770
|
this._setCamera(Vec.From({ ...this.getCamera() }))
|
|
3649
3771
|
}
|
|
3650
3772
|
}
|
|
3651
3773
|
|
|
3652
|
-
this._tickCameraState()
|
|
3653
|
-
|
|
3654
3774
|
return this
|
|
3655
3775
|
}
|
|
3656
3776
|
|
|
@@ -4056,18 +4176,19 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
4056
4176
|
// box just for rendering, and we only update after the camera stops moving.
|
|
4057
4177
|
private _cameraState = atom('camera state', 'idle' as 'idle' | 'moving')
|
|
4058
4178
|
private _cameraStateTimeoutRemaining = 0
|
|
4059
|
-
_decayCameraStateTimeout(elapsed: number) {
|
|
4179
|
+
private _decayCameraStateTimeout(elapsed: number) {
|
|
4060
4180
|
this._cameraStateTimeoutRemaining -= elapsed
|
|
4061
4181
|
if (this._cameraStateTimeoutRemaining > 0) return
|
|
4062
4182
|
this.off('tick', this._decayCameraStateTimeout)
|
|
4063
4183
|
this._cameraState.set('idle')
|
|
4064
4184
|
}
|
|
4065
|
-
_tickCameraState() {
|
|
4185
|
+
private _tickCameraState() {
|
|
4066
4186
|
// always reset the timeout
|
|
4067
4187
|
this._cameraStateTimeoutRemaining = this.options.cameraMovingTimeoutMs
|
|
4068
4188
|
// If the state is idle, then start the tick
|
|
4069
4189
|
if (this._cameraState.__unsafe__getWithoutCapture() !== 'idle') return
|
|
4070
4190
|
this._cameraState.set('moving')
|
|
4191
|
+
this._debouncedZoomLevel.set(unsafe__withoutCapture(() => this.getCamera().z))
|
|
4071
4192
|
this.on('tick', this._decayCameraStateTimeout)
|
|
4072
4193
|
}
|
|
4073
4194
|
|
|
@@ -5127,10 +5248,10 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5127
5248
|
|
|
5128
5249
|
// Check labels first
|
|
5129
5250
|
if (
|
|
5130
|
-
this.isShapeOfType
|
|
5131
|
-
((this.isShapeOfType
|
|
5132
|
-
this.isShapeOfType
|
|
5133
|
-
(this.isShapeOfType
|
|
5251
|
+
this.isShapeOfType(shape, 'frame') ||
|
|
5252
|
+
((this.isShapeOfType(shape, 'note') ||
|
|
5253
|
+
this.isShapeOfType(shape, 'arrow') ||
|
|
5254
|
+
(this.isShapeOfType(shape, 'geo') && shape.props.fill === 'none')) &&
|
|
5134
5255
|
this.getShapeUtil(shape).getText(shape)?.trim())
|
|
5135
5256
|
) {
|
|
5136
5257
|
for (const childGeometry of (geometry as Group2d).children) {
|
|
@@ -5140,7 +5261,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5140
5261
|
}
|
|
5141
5262
|
}
|
|
5142
5263
|
|
|
5143
|
-
if (this.isShapeOfType
|
|
5264
|
+
if (this.isShapeOfType(shape, 'frame')) {
|
|
5144
5265
|
// On the rare case that we've hit a frame (not its label), test again hitInside to be forced true;
|
|
5145
5266
|
// this prevents clicks from passing through the body of a frame to shapes behind it.
|
|
5146
5267
|
|
|
@@ -5421,7 +5542,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5421
5542
|
*
|
|
5422
5543
|
* @example
|
|
5423
5544
|
* ```ts
|
|
5424
|
-
* const isArrowShape = isShapeOfType
|
|
5545
|
+
* const isArrowShape = isShapeOfType(someShape, 'arrow')
|
|
5425
5546
|
* ```
|
|
5426
5547
|
*
|
|
5427
5548
|
* @param util - the TLShapeUtil constructor to test against
|
|
@@ -5429,15 +5550,16 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5429
5550
|
*
|
|
5430
5551
|
* @public
|
|
5431
5552
|
*/
|
|
5432
|
-
isShapeOfType<
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
arg: TLUnknownShape | TLUnknownShape['id'],
|
|
5553
|
+
isShapeOfType<K extends TLShape['type']>(
|
|
5554
|
+
shape: TLShape,
|
|
5555
|
+
type: K
|
|
5556
|
+
): shape is Extract<TLShape, { type: K }>
|
|
5557
|
+
isShapeOfType<T extends TLShape>(
|
|
5558
|
+
shape: TLShape,
|
|
5439
5559
|
type: T['type']
|
|
5440
|
-
) {
|
|
5560
|
+
): shape is Extract<TLShape, { type: T['type'] }>
|
|
5561
|
+
isShapeOfType<T extends TLShape = TLShape>(shapeId: TLShapeId, type: T['type']): boolean
|
|
5562
|
+
isShapeOfType(arg: TLShape | TLShapeId, type: TLShape['type']) {
|
|
5441
5563
|
const shape = typeof arg === 'string' ? this.getShape(arg) : arg
|
|
5442
5564
|
if (!shape) return false
|
|
5443
5565
|
return shape.type === type
|
|
@@ -5833,7 +5955,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5833
5955
|
|
|
5834
5956
|
while (node) {
|
|
5835
5957
|
if (
|
|
5836
|
-
this.isShapeOfType
|
|
5958
|
+
this.isShapeOfType(node, 'group') &&
|
|
5837
5959
|
focusedGroup?.id !== node.id &&
|
|
5838
5960
|
!this.hasAncestor(focusedGroup, node.id) &&
|
|
5839
5961
|
(filter?.(node) ?? true)
|
|
@@ -5875,7 +5997,15 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5875
5997
|
* Get all bindings of a certain type _from_ a particular shape. These are the bindings whose
|
|
5876
5998
|
* `fromId` matched the shape's ID.
|
|
5877
5999
|
*/
|
|
5878
|
-
getBindingsFromShape<
|
|
6000
|
+
getBindingsFromShape<K extends TLBinding['type']>(
|
|
6001
|
+
shape: TLShape | TLShapeId,
|
|
6002
|
+
type: K
|
|
6003
|
+
): Extract<TLBinding, { type: K }>[]
|
|
6004
|
+
getBindingsFromShape<Binding extends TLBinding = TLBinding>(
|
|
6005
|
+
shape: TLShape | TLShapeId,
|
|
6006
|
+
type: Binding['type']
|
|
6007
|
+
): Binding[]
|
|
6008
|
+
getBindingsFromShape<Binding extends TLBinding = TLBinding>(
|
|
5879
6009
|
shape: TLShape | TLShapeId,
|
|
5880
6010
|
type: Binding['type']
|
|
5881
6011
|
): Binding[] {
|
|
@@ -5889,7 +6019,15 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5889
6019
|
* Get all bindings of a certain type _to_ a particular shape. These are the bindings whose
|
|
5890
6020
|
* `toId` matches the shape's ID.
|
|
5891
6021
|
*/
|
|
5892
|
-
getBindingsToShape<
|
|
6022
|
+
getBindingsToShape<K extends TLBinding['type']>(
|
|
6023
|
+
shape: TLShape | TLShapeId,
|
|
6024
|
+
type: K
|
|
6025
|
+
): Extract<TLBinding, { type: K }>[]
|
|
6026
|
+
getBindingsToShape<Binding extends TLBinding = TLBinding>(
|
|
6027
|
+
shape: TLShape | TLShapeId,
|
|
6028
|
+
type: Binding['type']
|
|
6029
|
+
): Binding[]
|
|
6030
|
+
getBindingsToShape<Binding extends TLBinding = TLBinding>(
|
|
5893
6031
|
shape: TLShape | TLShapeId,
|
|
5894
6032
|
type: Binding['type']
|
|
5895
6033
|
): Binding[] {
|
|
@@ -5903,7 +6041,15 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5903
6041
|
* Get all bindings involving a particular shape. This includes bindings where the shape is the
|
|
5904
6042
|
* `fromId` or `toId`. If a type is provided, only bindings of that type are returned.
|
|
5905
6043
|
*/
|
|
5906
|
-
getBindingsInvolvingShape<
|
|
6044
|
+
getBindingsInvolvingShape<K extends TLBinding['type']>(
|
|
6045
|
+
shape: TLShape | TLShapeId,
|
|
6046
|
+
type: K
|
|
6047
|
+
): Extract<TLBinding, { type: K }>[]
|
|
6048
|
+
getBindingsInvolvingShape<Binding extends TLBinding = TLBinding>(
|
|
6049
|
+
shape: TLShape | TLShapeId,
|
|
6050
|
+
type?: Binding['type']
|
|
6051
|
+
): Binding[]
|
|
6052
|
+
getBindingsInvolvingShape<Binding extends TLBinding = TLBinding>(
|
|
5907
6053
|
shape: TLShape | TLShapeId,
|
|
5908
6054
|
type?: Binding['type']
|
|
5909
6055
|
): Binding[] {
|
|
@@ -5925,7 +6071,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5925
6071
|
if (!fromShape || !toShape) continue
|
|
5926
6072
|
if (!this.canBindShapes({ fromShape, toShape, binding: partial })) continue
|
|
5927
6073
|
|
|
5928
|
-
const util = this.getBindingUtil
|
|
6074
|
+
const util = this.getBindingUtil(partial.type)
|
|
5929
6075
|
const defaultProps = util.getDefaultProps()
|
|
5930
6076
|
const binding = this.store.schema.types.binding.create({
|
|
5931
6077
|
...partial,
|
|
@@ -6030,7 +6176,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
6030
6176
|
const toShapeType = typeof toShape === 'string' ? toShape : toShape.type
|
|
6031
6177
|
const bindingType = typeof binding === 'string' ? binding : binding.type
|
|
6032
6178
|
|
|
6033
|
-
const canBindOpts = { fromShapeType, toShapeType, bindingType }
|
|
6179
|
+
const canBindOpts = { fromShapeType, toShapeType, bindingType } as const
|
|
6034
6180
|
|
|
6035
6181
|
if (fromShapeType === toShapeType) {
|
|
6036
6182
|
return this.getShapeUtil(fromShapeType).canBind(canBindOpts)
|
|
@@ -6571,7 +6717,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
6571
6717
|
const shapesToFlipFirstPass = compact(ids.map((id) => this.getShape(id)))
|
|
6572
6718
|
|
|
6573
6719
|
for (const shape of shapesToFlipFirstPass) {
|
|
6574
|
-
if (this.isShapeOfType
|
|
6720
|
+
if (this.isShapeOfType(shape, 'group')) {
|
|
6575
6721
|
const childrenOfGroups = compact(
|
|
6576
6722
|
this.getSortedChildIdsForParent(shape.id).map((id) => this.getShape(id))
|
|
6577
6723
|
)
|
|
@@ -7628,8 +7774,14 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
7628
7774
|
// then if the shape is flipped in one axis only, we need to apply an extra rotation
|
|
7629
7775
|
// to make sure the shape is mirrored correctly
|
|
7630
7776
|
if (Math.sign(scale.x) * Math.sign(scale.y) < 0) {
|
|
7631
|
-
|
|
7632
|
-
rotation
|
|
7777
|
+
// We need to compute the new local rotation that will result in the negated page rotation.
|
|
7778
|
+
// For a shape with local rotation `localRot` and parent page rotation `parentRot`:
|
|
7779
|
+
// - pageRot = parentRot + localRot
|
|
7780
|
+
// - newPageRot = -pageRot (we want to negate the page rotation)
|
|
7781
|
+
// - newPageRot = parentRot + newLocalRot (parent hasn't changed)
|
|
7782
|
+
// - Therefore: newLocalRot = -pageRot - parentRot = -(parentRot + localRot) - parentRot = -localRot - 2*parentRot
|
|
7783
|
+
const parentRotation = this.getShapeParentTransform(id).rotation()
|
|
7784
|
+
const rotation = -options.initialShape.rotation - 2 * parentRotation
|
|
7633
7785
|
this.updateShapes([{ id, type, rotation }])
|
|
7634
7786
|
}
|
|
7635
7787
|
|
|
@@ -7649,9 +7801,13 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
7649
7801
|
)
|
|
7650
7802
|
|
|
7651
7803
|
// now calculate how far away the shape is from where it needs to be
|
|
7652
|
-
const pageBounds = this.getShapePageBounds(id)!
|
|
7653
7804
|
const pageTransform = this.getShapePageTransform(id)!
|
|
7654
|
-
|
|
7805
|
+
// We need to use the local bounds center transformed to page space, not the axis-aligned
|
|
7806
|
+
// page bounds center. This is because the page bounds are axis-aligned and their center
|
|
7807
|
+
// changes when the rotation changes, but we want to use the same reference point as
|
|
7808
|
+
// preScaleShapePageCenter (which used initialBounds.center transformed by the page transform).
|
|
7809
|
+
const currentLocalBounds = this.getShapeGeometry(id).bounds
|
|
7810
|
+
const currentPageCenter = Mat.applyToPoint(pageTransform, currentLocalBounds.center)
|
|
7655
7811
|
const shapePageTransformOrigin = pageTransform.point()
|
|
7656
7812
|
if (!currentPageCenter || !shapePageTransformOrigin) return this
|
|
7657
7813
|
const pageDelta = Vec.Sub(postScaleShapePageCenter, currentPageCenter)
|
|
@@ -7692,9 +7848,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
7692
7848
|
*
|
|
7693
7849
|
* @public
|
|
7694
7850
|
*/
|
|
7695
|
-
canCreateShape<
|
|
7696
|
-
shape: OptionalKeys<TLShapePartial<T>, 'id'> | T['id']
|
|
7697
|
-
): boolean {
|
|
7851
|
+
canCreateShape(shape: OptionalKeys<TLShapePartial<TLShape>, 'id'> | TLShape['id']): boolean {
|
|
7698
7852
|
return this.canCreateShapes([shape])
|
|
7699
7853
|
}
|
|
7700
7854
|
|
|
@@ -7705,8 +7859,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
7705
7859
|
*
|
|
7706
7860
|
* @public
|
|
7707
7861
|
*/
|
|
7708
|
-
canCreateShapes
|
|
7709
|
-
shapes: (
|
|
7862
|
+
canCreateShapes(
|
|
7863
|
+
shapes: (TLShape['id'] | OptionalKeys<TLShapePartial<TLShape>, 'id'>)[]
|
|
7710
7864
|
): boolean {
|
|
7711
7865
|
return shapes.length + this.getCurrentPageShapeIds().size <= this.options.maxShapesPerPage
|
|
7712
7866
|
}
|
|
@@ -7724,7 +7878,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
7724
7878
|
*
|
|
7725
7879
|
* @public
|
|
7726
7880
|
*/
|
|
7727
|
-
createShape<
|
|
7881
|
+
createShape<TShape extends TLShape>(shape: TLCreateShapePartial<TShape>): this {
|
|
7728
7882
|
this.createShapes([shape])
|
|
7729
7883
|
return this
|
|
7730
7884
|
}
|
|
@@ -7742,7 +7896,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
7742
7896
|
*
|
|
7743
7897
|
* @public
|
|
7744
7898
|
*/
|
|
7745
|
-
createShapes<
|
|
7899
|
+
createShapes<TShape extends TLShape = TLShape>(shapes: TLCreateShapePartial<TShape>[]): this {
|
|
7746
7900
|
if (!Array.isArray(shapes)) {
|
|
7747
7901
|
throw Error('Editor.createShapes: must provide an array of shapes or shape partials')
|
|
7748
7902
|
}
|
|
@@ -8101,7 +8255,12 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8101
8255
|
)
|
|
8102
8256
|
)
|
|
8103
8257
|
const sortedShapeIds = shapesToGroup.sort(sortByIndex).map((s) => s.id)
|
|
8104
|
-
const
|
|
8258
|
+
const childBounds = compact(shapesToGroup.map((shape) => this.getShapePageBounds(shape)))
|
|
8259
|
+
const pageBounds = Box.Common(childBounds)
|
|
8260
|
+
|
|
8261
|
+
if (!pageBounds.isValid()) {
|
|
8262
|
+
throw Error(`Editor.groupShapes: group bounds are invalid (NaN).`)
|
|
8263
|
+
}
|
|
8105
8264
|
|
|
8106
8265
|
const { x, y } = pageBounds.point
|
|
8107
8266
|
|
|
@@ -8123,7 +8282,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8123
8282
|
const highestIndex = shapesWithRootParent[shapesWithRootParent.length - 1]?.index
|
|
8124
8283
|
|
|
8125
8284
|
this.run(() => {
|
|
8126
|
-
this.createShapes
|
|
8285
|
+
this.createShapes([
|
|
8127
8286
|
{
|
|
8128
8287
|
id: groupId,
|
|
8129
8288
|
type: 'group',
|
|
@@ -8193,7 +8352,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8193
8352
|
const groups: TLGroupShape[] = []
|
|
8194
8353
|
|
|
8195
8354
|
shapesToUngroup.forEach((shape) => {
|
|
8196
|
-
if (this.isShapeOfType
|
|
8355
|
+
if (this.isShapeOfType(shape, 'group')) {
|
|
8197
8356
|
groups.push(shape)
|
|
8198
8357
|
} else {
|
|
8199
8358
|
idsToSelect.add(shape.id)
|
|
@@ -8239,7 +8398,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8239
8398
|
*
|
|
8240
8399
|
* @public
|
|
8241
8400
|
*/
|
|
8242
|
-
updateShape<T extends
|
|
8401
|
+
updateShape<T extends TLShape = TLShape>(partial: TLShapePartial<T> | null | undefined) {
|
|
8243
8402
|
this.updateShapes([partial])
|
|
8244
8403
|
return this
|
|
8245
8404
|
}
|
|
@@ -8256,7 +8415,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8256
8415
|
*
|
|
8257
8416
|
* @public
|
|
8258
8417
|
*/
|
|
8259
|
-
updateShapes<T extends
|
|
8418
|
+
updateShapes<T extends TLShape>(partials: (TLShapePartial<T> | null | undefined)[]) {
|
|
8260
8419
|
const compactedPartials: TLShapePartial<T>[] = Array(partials.length)
|
|
8261
8420
|
|
|
8262
8421
|
for (let i = 0, n = partials.length; i < n; i++) {
|
|
@@ -8408,7 +8567,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8408
8567
|
* @internal
|
|
8409
8568
|
*/
|
|
8410
8569
|
private _extractSharedStyles(shape: TLShape, sharedStyleMap: SharedStyleMap) {
|
|
8411
|
-
if (this.isShapeOfType
|
|
8570
|
+
if (this.isShapeOfType(shape, 'group')) {
|
|
8412
8571
|
// For groups, ignore the styles of the group shape and instead include the styles of the
|
|
8413
8572
|
// group's children. These are the shapes that would have their styles changed if the
|
|
8414
8573
|
// user called `setStyle` on the current selection.
|
|
@@ -8528,7 +8687,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8528
8687
|
// For groups, ignore the opacity of the group shape and instead include
|
|
8529
8688
|
// the opacity of the group's children. These are the shapes that would have
|
|
8530
8689
|
// their opacity changed if the user called `setOpacity` on the current selection.
|
|
8531
|
-
if (this.isShapeOfType
|
|
8690
|
+
if (this.isShapeOfType(shape, 'group')) {
|
|
8532
8691
|
for (const childId of this.getSortedChildIdsForParent(shape.id)) {
|
|
8533
8692
|
addShape(childId)
|
|
8534
8693
|
}
|
|
@@ -8589,7 +8748,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8589
8748
|
// We can have many deep levels of grouped shape
|
|
8590
8749
|
// Making a recursive function to look through all the levels
|
|
8591
8750
|
const addShapeById = (shape: TLShape) => {
|
|
8592
|
-
if (this.isShapeOfType
|
|
8751
|
+
if (this.isShapeOfType(shape, 'group')) {
|
|
8593
8752
|
const childIds = this.getSortedChildIdsForParent(shape)
|
|
8594
8753
|
for (const childId of childIds) {
|
|
8595
8754
|
addShapeById(this.getShape(childId)!)
|
|
@@ -8673,7 +8832,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8673
8832
|
// We can have many deep levels of grouped shape
|
|
8674
8833
|
// Making a recursive function to look through all the levels
|
|
8675
8834
|
const addShapeById = (shape: TLShape) => {
|
|
8676
|
-
if (this.isShapeOfType
|
|
8835
|
+
if (this.isShapeOfType(shape, 'group')) {
|
|
8677
8836
|
const childIds = this.getSortedChildIdsForParent(shape.id)
|
|
8678
8837
|
for (const childId of childIds) {
|
|
8679
8838
|
addShapeById(this.getShape(childId)!)
|
|
@@ -9098,7 +9257,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9098
9257
|
for (const shape of this.getSelectedShapes()) {
|
|
9099
9258
|
if (lowestDepth === 0) break
|
|
9100
9259
|
|
|
9101
|
-
const isFrame = this.isShapeOfType
|
|
9260
|
+
const isFrame = this.isShapeOfType(shape, 'frame')
|
|
9102
9261
|
const ancestors = this.getShapeAncestors(shape)
|
|
9103
9262
|
if (isFrame) ancestors.push(shape)
|
|
9104
9263
|
|
|
@@ -9126,6 +9285,30 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9126
9285
|
}
|
|
9127
9286
|
}
|
|
9128
9287
|
|
|
9288
|
+
if (point) {
|
|
9289
|
+
const shapesById = new Map<TLShapeId, TLShape>(shapes.map((shape) => [shape.id, shape]))
|
|
9290
|
+
const rootShapesFromContent = compact(rootShapeIds.map((id) => shapesById.get(id)))
|
|
9291
|
+
if (rootShapesFromContent.length > 0) {
|
|
9292
|
+
const targetParent = this.getShapeAtPoint(point, {
|
|
9293
|
+
hitInside: true,
|
|
9294
|
+
hitFrameInside: true,
|
|
9295
|
+
hitLocked: true,
|
|
9296
|
+
filter: (shape) => {
|
|
9297
|
+
const util = this.getShapeUtil(shape)
|
|
9298
|
+
if (!util.canReceiveNewChildrenOfType) return false
|
|
9299
|
+
return rootShapesFromContent.every((rootShape) =>
|
|
9300
|
+
util.canReceiveNewChildrenOfType!(shape, rootShape.type)
|
|
9301
|
+
)
|
|
9302
|
+
},
|
|
9303
|
+
})
|
|
9304
|
+
|
|
9305
|
+
// When pasting at a specific point (e.g. paste-at-cursor) prefer the
|
|
9306
|
+
// parent under the pointer so that we don't keep using the original
|
|
9307
|
+
// selection's parent (which can keep shapes clipped inside frames).
|
|
9308
|
+
pasteParentId = targetParent ? targetParent.id : currentPageId
|
|
9309
|
+
}
|
|
9310
|
+
}
|
|
9311
|
+
|
|
9129
9312
|
let isDuplicating = false
|
|
9130
9313
|
|
|
9131
9314
|
if (!isPageId(pasteParentId)) {
|
|
@@ -9137,8 +9320,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9137
9320
|
if (rootShapeIds.length === 1) {
|
|
9138
9321
|
const rootShape = shapes.find((s) => s.id === rootShapeIds[0])!
|
|
9139
9322
|
if (
|
|
9140
|
-
this.isShapeOfType
|
|
9141
|
-
this.isShapeOfType
|
|
9323
|
+
this.isShapeOfType(parent, 'frame') &&
|
|
9324
|
+
this.isShapeOfType(rootShape, 'frame') &&
|
|
9142
9325
|
rootShape.props.w === parent?.props.w &&
|
|
9143
9326
|
rootShape.props.h === parent?.props.h
|
|
9144
9327
|
) {
|
|
@@ -9313,11 +9496,11 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9313
9496
|
const onlyRoot = rootShapes[0] as TLFrameShape
|
|
9314
9497
|
// If the old bounds are in the viewport...
|
|
9315
9498
|
// todo: replace frame references with shapes that can accept children
|
|
9316
|
-
if (this.isShapeOfType
|
|
9499
|
+
if (this.isShapeOfType(onlyRoot, 'frame')) {
|
|
9317
9500
|
while (
|
|
9318
9501
|
this.getShapesAtPoint(point).some(
|
|
9319
9502
|
(shape) =>
|
|
9320
|
-
this.isShapeOfType
|
|
9503
|
+
this.isShapeOfType(shape, 'frame') &&
|
|
9321
9504
|
shape.props.w === onlyRoot.props.w &&
|
|
9322
9505
|
shape.props.h === onlyRoot.props.h
|
|
9323
9506
|
)
|
|
@@ -9463,126 +9646,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9463
9646
|
|
|
9464
9647
|
/* --------------------- Events --------------------- */
|
|
9465
9648
|
|
|
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
9649
|
/**
|
|
9587
9650
|
* Dispatch a cancel event.
|
|
9588
9651
|
*
|
|
@@ -9652,19 +9715,22 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9652
9715
|
// weird but true: what `inputs` calls screen-space is actually viewport space. so
|
|
9653
9716
|
// we need to convert back into true screen space first. we should fix this...
|
|
9654
9717
|
Vec.Add(
|
|
9655
|
-
this.inputs.
|
|
9718
|
+
this.inputs.getCurrentScreenPoint(),
|
|
9656
9719
|
this.store.unsafeGetWithoutCapture(TLINSTANCE_ID)!.screenBounds
|
|
9657
9720
|
),
|
|
9658
9721
|
pointerId: options?.pointerId ?? 0,
|
|
9659
9722
|
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:
|
|
9723
|
+
isPen: options?.isPen ?? this.inputs.getIsPen(),
|
|
9724
|
+
shiftKey: options?.shiftKey ?? this.inputs.getShiftKey(),
|
|
9725
|
+
altKey: options?.altKey ?? this.inputs.getAltKey(),
|
|
9726
|
+
ctrlKey: options?.ctrlKey ?? this.inputs.getCtrlKey(),
|
|
9727
|
+
metaKey: options?.metaKey ?? this.inputs.getMetaKey(),
|
|
9728
|
+
accelKey: false,
|
|
9666
9729
|
}
|
|
9667
9730
|
|
|
9731
|
+
// needs to be calculated second
|
|
9732
|
+
event.accelKey = options?.accelKey ?? this.inputs.getAccelKey()
|
|
9733
|
+
|
|
9668
9734
|
if (options?.immediate) {
|
|
9669
9735
|
this._flushEventForTick(event)
|
|
9670
9736
|
} else {
|
|
@@ -10037,16 +10103,16 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10037
10103
|
/** @internal */
|
|
10038
10104
|
@bind
|
|
10039
10105
|
_setShiftKeyTimeout() {
|
|
10040
|
-
this.inputs.
|
|
10106
|
+
this.inputs.setShiftKey(false)
|
|
10041
10107
|
this.dispatch({
|
|
10042
10108
|
type: 'keyboard',
|
|
10043
10109
|
name: 'key_up',
|
|
10044
10110
|
key: 'Shift',
|
|
10045
|
-
shiftKey: this.inputs.
|
|
10046
|
-
ctrlKey: this.inputs.
|
|
10047
|
-
altKey: this.inputs.
|
|
10048
|
-
metaKey: this.inputs.
|
|
10049
|
-
accelKey:
|
|
10111
|
+
shiftKey: this.inputs.getShiftKey(),
|
|
10112
|
+
ctrlKey: this.inputs.getCtrlKey(),
|
|
10113
|
+
altKey: this.inputs.getAltKey(),
|
|
10114
|
+
metaKey: this.inputs.getMetaKey(),
|
|
10115
|
+
accelKey: this.inputs.getAccelKey(),
|
|
10050
10116
|
code: 'ShiftLeft',
|
|
10051
10117
|
})
|
|
10052
10118
|
}
|
|
@@ -10057,16 +10123,16 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10057
10123
|
/** @internal */
|
|
10058
10124
|
@bind
|
|
10059
10125
|
_setAltKeyTimeout() {
|
|
10060
|
-
this.inputs.
|
|
10126
|
+
this.inputs.setAltKey(false)
|
|
10061
10127
|
this.dispatch({
|
|
10062
10128
|
type: 'keyboard',
|
|
10063
10129
|
name: 'key_up',
|
|
10064
10130
|
key: 'Alt',
|
|
10065
|
-
shiftKey: this.inputs.
|
|
10066
|
-
ctrlKey: this.inputs.
|
|
10067
|
-
altKey: this.inputs.
|
|
10068
|
-
metaKey: this.inputs.
|
|
10069
|
-
accelKey:
|
|
10131
|
+
shiftKey: this.inputs.getShiftKey(),
|
|
10132
|
+
ctrlKey: this.inputs.getCtrlKey(),
|
|
10133
|
+
altKey: this.inputs.getAltKey(),
|
|
10134
|
+
metaKey: this.inputs.getMetaKey(),
|
|
10135
|
+
accelKey: this.inputs.getAccelKey(),
|
|
10070
10136
|
code: 'AltLeft',
|
|
10071
10137
|
})
|
|
10072
10138
|
}
|
|
@@ -10077,16 +10143,16 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10077
10143
|
/** @internal */
|
|
10078
10144
|
@bind
|
|
10079
10145
|
_setCtrlKeyTimeout() {
|
|
10080
|
-
this.inputs.
|
|
10146
|
+
this.inputs.setCtrlKey(false)
|
|
10081
10147
|
this.dispatch({
|
|
10082
10148
|
type: 'keyboard',
|
|
10083
10149
|
name: 'key_up',
|
|
10084
10150
|
key: 'Ctrl',
|
|
10085
|
-
shiftKey: this.inputs.
|
|
10086
|
-
ctrlKey: this.inputs.
|
|
10087
|
-
altKey: this.inputs.
|
|
10088
|
-
metaKey: this.inputs.
|
|
10089
|
-
accelKey:
|
|
10151
|
+
shiftKey: this.inputs.getShiftKey(),
|
|
10152
|
+
ctrlKey: this.inputs.getCtrlKey(),
|
|
10153
|
+
altKey: this.inputs.getAltKey(),
|
|
10154
|
+
metaKey: this.inputs.getMetaKey(),
|
|
10155
|
+
accelKey: this.inputs.getAccelKey(),
|
|
10090
10156
|
code: 'ControlLeft',
|
|
10091
10157
|
})
|
|
10092
10158
|
}
|
|
@@ -10097,16 +10163,16 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10097
10163
|
/** @internal */
|
|
10098
10164
|
@bind
|
|
10099
10165
|
_setMetaKeyTimeout() {
|
|
10100
|
-
this.inputs.
|
|
10166
|
+
this.inputs.setMetaKey(false)
|
|
10101
10167
|
this.dispatch({
|
|
10102
10168
|
type: 'keyboard',
|
|
10103
10169
|
name: 'key_up',
|
|
10104
10170
|
key: 'Meta',
|
|
10105
|
-
shiftKey: this.inputs.
|
|
10106
|
-
ctrlKey: this.inputs.
|
|
10107
|
-
altKey: this.inputs.
|
|
10108
|
-
metaKey: this.inputs.
|
|
10109
|
-
accelKey:
|
|
10171
|
+
shiftKey: this.inputs.getShiftKey(),
|
|
10172
|
+
ctrlKey: this.inputs.getCtrlKey(),
|
|
10173
|
+
altKey: this.inputs.getAltKey(),
|
|
10174
|
+
metaKey: this.inputs.getMetaKey(),
|
|
10175
|
+
accelKey: this.inputs.getAccelKey(),
|
|
10110
10176
|
code: 'MetaLeft',
|
|
10111
10177
|
})
|
|
10112
10178
|
}
|
|
@@ -10114,9 +10180,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10114
10180
|
/** @internal */
|
|
10115
10181
|
private _restoreToolId = 'select'
|
|
10116
10182
|
|
|
10117
|
-
/** @internal */
|
|
10118
|
-
private _pinchStart = 1
|
|
10119
|
-
|
|
10120
10183
|
/** @internal */
|
|
10121
10184
|
private _didPinch = false
|
|
10122
10185
|
|
|
@@ -10223,55 +10286,54 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10223
10286
|
if (info.type === 'misc') {
|
|
10224
10287
|
// stop panning if the interaction is cancelled or completed
|
|
10225
10288
|
if (info.name === 'cancel' || info.name === 'complete') {
|
|
10226
|
-
this.inputs.
|
|
10289
|
+
this.inputs.setIsDragging(false)
|
|
10227
10290
|
|
|
10228
|
-
if (this.inputs.
|
|
10229
|
-
this.inputs.
|
|
10230
|
-
this.inputs.
|
|
10291
|
+
if (this.inputs.getIsPanning()) {
|
|
10292
|
+
this.inputs.setIsPanning(false)
|
|
10293
|
+
this.inputs.setIsSpacebarPanning(false)
|
|
10231
10294
|
this.setCursor({ type: this._prevCursor, rotation: 0 })
|
|
10232
10295
|
}
|
|
10233
10296
|
}
|
|
10234
10297
|
|
|
10235
10298
|
this.root.handleEvent(info)
|
|
10299
|
+
this.emit('event', info)
|
|
10236
10300
|
return
|
|
10237
10301
|
}
|
|
10238
10302
|
|
|
10239
10303
|
if (info.shiftKey) {
|
|
10240
10304
|
clearTimeout(this._shiftKeyTimeout)
|
|
10241
10305
|
this._shiftKeyTimeout = -1
|
|
10242
|
-
inputs.
|
|
10243
|
-
} else if (!info.shiftKey && inputs.
|
|
10306
|
+
inputs.setShiftKey(true)
|
|
10307
|
+
} else if (!info.shiftKey && inputs.getShiftKey() && this._shiftKeyTimeout === -1) {
|
|
10244
10308
|
this._shiftKeyTimeout = this.timers.setTimeout(this._setShiftKeyTimeout, 150)
|
|
10245
10309
|
}
|
|
10246
10310
|
|
|
10247
10311
|
if (info.altKey) {
|
|
10248
10312
|
clearTimeout(this._altKeyTimeout)
|
|
10249
10313
|
this._altKeyTimeout = -1
|
|
10250
|
-
inputs.
|
|
10251
|
-
} else if (!info.altKey && inputs.
|
|
10314
|
+
inputs.setAltKey(true)
|
|
10315
|
+
} else if (!info.altKey && inputs.getAltKey() && this._altKeyTimeout === -1) {
|
|
10252
10316
|
this._altKeyTimeout = this.timers.setTimeout(this._setAltKeyTimeout, 150)
|
|
10253
10317
|
}
|
|
10254
10318
|
|
|
10255
10319
|
if (info.ctrlKey) {
|
|
10256
10320
|
clearTimeout(this._ctrlKeyTimeout)
|
|
10257
10321
|
this._ctrlKeyTimeout = -1
|
|
10258
|
-
inputs.
|
|
10259
|
-
} else if (!info.ctrlKey && inputs.
|
|
10322
|
+
inputs.setCtrlKey(true)
|
|
10323
|
+
} else if (!info.ctrlKey && inputs.getCtrlKey() && this._ctrlKeyTimeout === -1) {
|
|
10260
10324
|
this._ctrlKeyTimeout = this.timers.setTimeout(this._setCtrlKeyTimeout, 150)
|
|
10261
10325
|
}
|
|
10262
10326
|
|
|
10263
10327
|
if (info.metaKey) {
|
|
10264
10328
|
clearTimeout(this._metaKeyTimeout)
|
|
10265
10329
|
this._metaKeyTimeout = -1
|
|
10266
|
-
inputs.
|
|
10267
|
-
} else if (!info.metaKey && inputs.
|
|
10330
|
+
inputs.setMetaKey(true)
|
|
10331
|
+
} else if (!info.metaKey && inputs.getMetaKey() && this._metaKeyTimeout === -1) {
|
|
10268
10332
|
this._metaKeyTimeout = this.timers.setTimeout(this._setMetaKeyTimeout, 150)
|
|
10269
10333
|
}
|
|
10270
10334
|
|
|
10271
|
-
|
|
10272
|
-
|
|
10273
|
-
if (!inputs.isPointing) {
|
|
10274
|
-
inputs.isDragging = false
|
|
10335
|
+
if (!inputs.getIsPointing()) {
|
|
10336
|
+
inputs.setIsDragging(false)
|
|
10275
10337
|
}
|
|
10276
10338
|
|
|
10277
10339
|
const instanceState = this.store.unsafeGetWithoutCapture(TLINSTANCE_ID)!
|
|
@@ -10282,29 +10344,29 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10282
10344
|
case 'pinch': {
|
|
10283
10345
|
if (cameraOptions.isLocked) return
|
|
10284
10346
|
clearTimeout(this._longPressTimeout)
|
|
10285
|
-
this.
|
|
10347
|
+
this.inputs.updateFromEvent(info)
|
|
10286
10348
|
|
|
10287
10349
|
switch (info.name) {
|
|
10288
10350
|
case 'pinch_start': {
|
|
10289
|
-
if (inputs.
|
|
10351
|
+
if (inputs.getIsPinching()) return
|
|
10290
10352
|
|
|
10291
|
-
if (!inputs.
|
|
10292
|
-
this._pinchStart = this.getCamera().z
|
|
10353
|
+
if (!inputs.getIsEditing()) {
|
|
10293
10354
|
if (!this._selectedShapeIdsAtPointerDown.length) {
|
|
10294
10355
|
this._selectedShapeIdsAtPointerDown = [...pageState.selectedShapeIds]
|
|
10295
10356
|
}
|
|
10296
10357
|
|
|
10297
10358
|
this._didPinch = true
|
|
10298
10359
|
|
|
10299
|
-
inputs.
|
|
10360
|
+
inputs.setIsPinching(true)
|
|
10300
10361
|
|
|
10301
10362
|
this.interrupt()
|
|
10302
10363
|
}
|
|
10303
10364
|
|
|
10365
|
+
this.emit('event', info)
|
|
10304
10366
|
return // Stop here!
|
|
10305
10367
|
}
|
|
10306
10368
|
case 'pinch': {
|
|
10307
|
-
if (!inputs.
|
|
10369
|
+
if (!inputs.getIsPinching()) return
|
|
10308
10370
|
|
|
10309
10371
|
const {
|
|
10310
10372
|
point: { z = 1 },
|
|
@@ -10335,13 +10397,14 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10335
10397
|
{ immediate: true }
|
|
10336
10398
|
)
|
|
10337
10399
|
|
|
10400
|
+
this.emit('event', info)
|
|
10338
10401
|
return // Stop here!
|
|
10339
10402
|
}
|
|
10340
10403
|
case 'pinch_end': {
|
|
10341
|
-
if (!inputs.
|
|
10404
|
+
if (!inputs.getIsPinching()) return this
|
|
10342
10405
|
|
|
10343
10406
|
// Stop pinching
|
|
10344
|
-
inputs.
|
|
10407
|
+
inputs.setIsPinching(false)
|
|
10345
10408
|
|
|
10346
10409
|
// Stash and clear the shapes that were selected when the pinch started
|
|
10347
10410
|
const { _selectedShapeIdsAtPointerDown: shapesToReselect } = this
|
|
@@ -10361,6 +10424,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10361
10424
|
}
|
|
10362
10425
|
}
|
|
10363
10426
|
|
|
10427
|
+
this.emit('event', info)
|
|
10364
10428
|
return // Stop here!
|
|
10365
10429
|
}
|
|
10366
10430
|
}
|
|
@@ -10368,7 +10432,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10368
10432
|
case 'wheel': {
|
|
10369
10433
|
if (cameraOptions.isLocked) return
|
|
10370
10434
|
|
|
10371
|
-
this.
|
|
10435
|
+
this.inputs.updateFromEvent(info)
|
|
10372
10436
|
|
|
10373
10437
|
const { panSpeed, zoomSpeed } = cameraOptions
|
|
10374
10438
|
let wheelBehavior = cameraOptions.wheelBehavior
|
|
@@ -10399,7 +10463,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10399
10463
|
switch (behavior) {
|
|
10400
10464
|
case 'zoom': {
|
|
10401
10465
|
// Zoom in on current screen point using the wheel delta
|
|
10402
|
-
const { x, y } = this.inputs.
|
|
10466
|
+
const { x, y } = this.inputs.getCurrentScreenPoint()
|
|
10403
10467
|
let delta = dz
|
|
10404
10468
|
|
|
10405
10469
|
// If we're forcing zoom, then we need to do the wheel normalization math here
|
|
@@ -10416,6 +10480,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10416
10480
|
immediate: true,
|
|
10417
10481
|
})
|
|
10418
10482
|
this.maybeTrackPerformance('Zooming')
|
|
10483
|
+
this.root.handleEvent(info)
|
|
10484
|
+
this.emit('event', info)
|
|
10419
10485
|
return
|
|
10420
10486
|
}
|
|
10421
10487
|
case 'pan': {
|
|
@@ -10424,6 +10490,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10424
10490
|
immediate: true,
|
|
10425
10491
|
})
|
|
10426
10492
|
this.maybeTrackPerformance('Panning')
|
|
10493
|
+
this.root.handleEvent(info)
|
|
10494
|
+
this.emit('event', info)
|
|
10427
10495
|
return
|
|
10428
10496
|
}
|
|
10429
10497
|
}
|
|
@@ -10432,9 +10500,9 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10432
10500
|
}
|
|
10433
10501
|
case 'pointer': {
|
|
10434
10502
|
// Ignore pointer events while we're pinching
|
|
10435
|
-
if (inputs.
|
|
10503
|
+
if (inputs.getIsPinching()) return
|
|
10436
10504
|
|
|
10437
|
-
this.
|
|
10505
|
+
this.inputs.updateFromEvent(info)
|
|
10438
10506
|
const { isPen } = info
|
|
10439
10507
|
const { isPenMode } = instanceState
|
|
10440
10508
|
|
|
@@ -10443,7 +10511,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10443
10511
|
// If we're in pen mode and the input is not a pen type, then stop here
|
|
10444
10512
|
if (isPenMode && !isPen) return
|
|
10445
10513
|
|
|
10446
|
-
if (!this.inputs.
|
|
10514
|
+
if (!this.inputs.getIsPanning()) {
|
|
10447
10515
|
// Start a long press timeout
|
|
10448
10516
|
this._longPressTimeout = this.timers.setTimeout(() => {
|
|
10449
10517
|
const vsb = this.getViewportScreenBounds()
|
|
@@ -10453,7 +10521,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10453
10521
|
// viewport bounds, and will be again when this event is handled...
|
|
10454
10522
|
// so we need to counter-adjust from the stored value so that the
|
|
10455
10523
|
// new value is set correctly.
|
|
10456
|
-
point: this.inputs.
|
|
10524
|
+
point: this.inputs.getOriginScreenPoint().clone().addXY(vsb.x, vsb.y),
|
|
10457
10525
|
name: 'long_press',
|
|
10458
10526
|
})
|
|
10459
10527
|
}, this.options.longPressDurationMs)
|
|
@@ -10470,8 +10538,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10470
10538
|
inputs.buttons.add(info.button)
|
|
10471
10539
|
|
|
10472
10540
|
// Start pointing and stop dragging
|
|
10473
|
-
inputs.
|
|
10474
|
-
inputs.
|
|
10541
|
+
inputs.setIsPointing(true)
|
|
10542
|
+
inputs.setIsDragging(false)
|
|
10475
10543
|
|
|
10476
10544
|
// If pen mode is off but we're not already in pen mode, turn that on
|
|
10477
10545
|
if (!isPenMode && isPen) this.updateInstanceState({ isPenMode: true })
|
|
@@ -10483,16 +10551,16 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10483
10551
|
this.setCurrentTool('eraser')
|
|
10484
10552
|
} else if (info.button === MIDDLE_MOUSE_BUTTON) {
|
|
10485
10553
|
// Middle mouse pan activates panning unless we're already panning (with spacebar)
|
|
10486
|
-
if (!this.inputs.
|
|
10554
|
+
if (!this.inputs.getIsPanning()) {
|
|
10487
10555
|
this._prevCursor = this.getInstanceState().cursor.type
|
|
10488
10556
|
}
|
|
10489
|
-
this.inputs.
|
|
10557
|
+
this.inputs.setIsPanning(true)
|
|
10490
10558
|
clearTimeout(this._longPressTimeout)
|
|
10491
10559
|
}
|
|
10492
10560
|
|
|
10493
10561
|
// We might be panning because we did a middle mouse click, or because we're holding spacebar and started a regular click
|
|
10494
10562
|
// Also stop here, we don't want the state chart to receive the event
|
|
10495
|
-
if (this.inputs.
|
|
10563
|
+
if (this.inputs.getIsPanning()) {
|
|
10496
10564
|
this.stopCameraAnimation()
|
|
10497
10565
|
this.setCursor({ type: 'grabbing', rotation: 0 })
|
|
10498
10566
|
return this
|
|
@@ -10507,9 +10575,10 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10507
10575
|
const { x: cx, y: cy, z: cz } = unsafe__withoutCapture(() => this.getCamera())
|
|
10508
10576
|
|
|
10509
10577
|
// If we've started panning, then clear any long press timeout
|
|
10510
|
-
if (this.inputs.
|
|
10578
|
+
if (this.inputs.getIsPanning() && this.inputs.getIsPointing()) {
|
|
10511
10579
|
// Handle spacebar / middle mouse button panning
|
|
10512
|
-
const
|
|
10580
|
+
const currentScreenPoint = this.inputs.getCurrentScreenPoint()
|
|
10581
|
+
const previousScreenPoint = this.inputs.getPreviousScreenPoint()
|
|
10513
10582
|
const offset = Vec.Sub(currentScreenPoint, previousScreenPoint)
|
|
10514
10583
|
this.setCamera(new Vec(cx + offset.x / cz, cy + offset.y / cz, cz), {
|
|
10515
10584
|
immediate: true,
|
|
@@ -10519,24 +10588,25 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10519
10588
|
}
|
|
10520
10589
|
|
|
10521
10590
|
if (
|
|
10522
|
-
inputs.
|
|
10523
|
-
!inputs.
|
|
10524
|
-
Vec.Dist2(
|
|
10591
|
+
inputs.getIsPointing() &&
|
|
10592
|
+
!inputs.getIsDragging() &&
|
|
10593
|
+
Vec.Dist2(inputs.getOriginPagePoint(), inputs.getCurrentPagePoint()) *
|
|
10594
|
+
this.getZoomLevel() >
|
|
10525
10595
|
(instanceState.isCoarsePointer
|
|
10526
10596
|
? this.options.coarseDragDistanceSquared
|
|
10527
10597
|
: this.options.dragDistanceSquared) /
|
|
10528
10598
|
cz
|
|
10529
10599
|
) {
|
|
10530
10600
|
// Start dragging
|
|
10531
|
-
inputs.
|
|
10601
|
+
inputs.setIsDragging(true)
|
|
10532
10602
|
clearTimeout(this._longPressTimeout)
|
|
10533
10603
|
}
|
|
10534
10604
|
break
|
|
10535
10605
|
}
|
|
10536
10606
|
case 'pointer_up': {
|
|
10537
10607
|
// Stop dragging / pointing
|
|
10538
|
-
inputs.
|
|
10539
|
-
inputs.
|
|
10608
|
+
inputs.setIsDragging(false)
|
|
10609
|
+
inputs.setIsPointing(false)
|
|
10540
10610
|
clearTimeout(this._longPressTimeout)
|
|
10541
10611
|
|
|
10542
10612
|
// Remove the button from the buttons set
|
|
@@ -10553,12 +10623,12 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10553
10623
|
info.button = 0
|
|
10554
10624
|
}
|
|
10555
10625
|
|
|
10556
|
-
if (inputs.
|
|
10626
|
+
if (inputs.getIsPanning()) {
|
|
10557
10627
|
if (!inputs.keys.has('Space')) {
|
|
10558
|
-
inputs.
|
|
10559
|
-
inputs.
|
|
10628
|
+
inputs.setIsPanning(false)
|
|
10629
|
+
inputs.setIsSpacebarPanning(false)
|
|
10560
10630
|
}
|
|
10561
|
-
const slideDirection = this.inputs.
|
|
10631
|
+
const slideDirection = this.inputs.getPointerVelocity()
|
|
10562
10632
|
const slideSpeed = Math.min(2, slideDirection.len())
|
|
10563
10633
|
|
|
10564
10634
|
switch (info.button) {
|
|
@@ -10602,43 +10672,48 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10602
10672
|
// Add the key from the keys set
|
|
10603
10673
|
inputs.keys.add(info.code)
|
|
10604
10674
|
|
|
10605
|
-
|
|
10606
|
-
|
|
10607
|
-
if (!
|
|
10608
|
-
this.
|
|
10609
|
-
|
|
10675
|
+
if (this.options.spacebarPanning) {
|
|
10676
|
+
// If the space key is pressed (but meta / control isn't!) activate panning
|
|
10677
|
+
if (info.code === 'Space' && !info.ctrlKey) {
|
|
10678
|
+
if (!this.inputs.getIsPanning()) {
|
|
10679
|
+
this._prevCursor = instanceState.cursor.type
|
|
10680
|
+
}
|
|
10610
10681
|
|
|
10611
|
-
|
|
10612
|
-
|
|
10613
|
-
|
|
10614
|
-
|
|
10615
|
-
|
|
10682
|
+
this.inputs.setIsPanning(true)
|
|
10683
|
+
this.inputs.setIsSpacebarPanning(true)
|
|
10684
|
+
clearTimeout(this._longPressTimeout)
|
|
10685
|
+
this.setCursor({
|
|
10686
|
+
type: this.inputs.getIsPointing() ? 'grabbing' : 'grab',
|
|
10687
|
+
rotation: 0,
|
|
10688
|
+
})
|
|
10689
|
+
}
|
|
10616
10690
|
|
|
10617
|
-
|
|
10618
|
-
|
|
10619
|
-
|
|
10620
|
-
|
|
10621
|
-
|
|
10622
|
-
|
|
10623
|
-
|
|
10624
|
-
|
|
10625
|
-
|
|
10626
|
-
|
|
10627
|
-
|
|
10628
|
-
|
|
10629
|
-
|
|
10630
|
-
|
|
10631
|
-
|
|
10632
|
-
|
|
10633
|
-
|
|
10634
|
-
|
|
10691
|
+
if (this.inputs.getIsSpacebarPanning()) {
|
|
10692
|
+
let offset: Vec | undefined
|
|
10693
|
+
switch (info.code) {
|
|
10694
|
+
case 'ArrowUp': {
|
|
10695
|
+
offset = new Vec(0, -1)
|
|
10696
|
+
break
|
|
10697
|
+
}
|
|
10698
|
+
case 'ArrowRight': {
|
|
10699
|
+
offset = new Vec(1, 0)
|
|
10700
|
+
break
|
|
10701
|
+
}
|
|
10702
|
+
case 'ArrowDown': {
|
|
10703
|
+
offset = new Vec(0, 1)
|
|
10704
|
+
break
|
|
10705
|
+
}
|
|
10706
|
+
case 'ArrowLeft': {
|
|
10707
|
+
offset = new Vec(-1, 0)
|
|
10708
|
+
break
|
|
10709
|
+
}
|
|
10635
10710
|
}
|
|
10636
|
-
}
|
|
10637
10711
|
|
|
10638
|
-
|
|
10639
|
-
|
|
10640
|
-
|
|
10641
|
-
|
|
10712
|
+
if (offset) {
|
|
10713
|
+
const bounds = this.getViewportPageBounds()
|
|
10714
|
+
const next = bounds.clone().translate(offset.mulV({ x: bounds.w, y: bounds.h }))
|
|
10715
|
+
this._animateToViewport(next, { animation: { duration: 320 } })
|
|
10716
|
+
}
|
|
10642
10717
|
}
|
|
10643
10718
|
}
|
|
10644
10719
|
|
|
@@ -10648,15 +10723,17 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10648
10723
|
// Remove the key from the keys set
|
|
10649
10724
|
inputs.keys.delete(info.code)
|
|
10650
10725
|
|
|
10651
|
-
|
|
10652
|
-
|
|
10653
|
-
if (
|
|
10654
|
-
|
|
10655
|
-
|
|
10656
|
-
|
|
10657
|
-
|
|
10658
|
-
|
|
10659
|
-
|
|
10726
|
+
if (this.options.spacebarPanning) {
|
|
10727
|
+
// If we've lifted the space key,
|
|
10728
|
+
if (info.code === 'Space') {
|
|
10729
|
+
if (this.inputs.buttons.has(MIDDLE_MOUSE_BUTTON)) {
|
|
10730
|
+
// If we're still middle dragging, continue panning
|
|
10731
|
+
} else {
|
|
10732
|
+
// otherwise, stop panning
|
|
10733
|
+
this.inputs.setIsPanning(false)
|
|
10734
|
+
this.inputs.setIsSpacebarPanning(false)
|
|
10735
|
+
this.setCursor({ type: this._prevCursor, rotation: 0 })
|
|
10736
|
+
}
|
|
10660
10737
|
}
|
|
10661
10738
|
}
|
|
10662
10739
|
break
|
|
@@ -10730,7 +10807,10 @@ function alertMaxShapes(editor: Editor, pageId = editor.getCurrentPageId()) {
|
|
|
10730
10807
|
|
|
10731
10808
|
function applyPartialToRecordWithProps<
|
|
10732
10809
|
T extends UnknownRecord & { type: string; props: object; meta: object },
|
|
10733
|
-
>(
|
|
10810
|
+
>(
|
|
10811
|
+
prev: T,
|
|
10812
|
+
partial?: T extends T ? Omit<Partial<T>, 'props'> & { props?: Partial<T['props']> } : never
|
|
10813
|
+
): T {
|
|
10734
10814
|
if (!partial) return prev
|
|
10735
10815
|
let next = null as null | T
|
|
10736
10816
|
const entries = Object.entries(partial)
|