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