@tldraw/editor 4.4.0-next.84d68f44c848 → 4.4.0-next.bde73a32273d
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 +277 -30
- package/dist-cjs/index.js +1 -6
- package/dist-cjs/index.js.map +3 -3
- package/dist-cjs/lib/TldrawEditor.js +20 -8
- package/dist-cjs/lib/TldrawEditor.js.map +2 -2
- package/dist-cjs/lib/components/LiveCollaborators.js +14 -24
- package/dist-cjs/lib/components/LiveCollaborators.js.map +2 -2
- package/dist-cjs/lib/components/Shape.js +12 -17
- package/dist-cjs/lib/components/Shape.js.map +2 -2
- package/dist-cjs/lib/components/default-components/CanvasShapeIndicators.js +226 -0
- package/dist-cjs/lib/components/default-components/CanvasShapeIndicators.js.map +7 -0
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js +45 -15
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js +3 -1
- package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultShapeIndicators.js +13 -1
- package/dist-cjs/lib/components/default-components/DefaultShapeIndicators.js.map +2 -2
- package/dist-cjs/lib/config/TLUserPreferences.js +9 -3
- package/dist-cjs/lib/config/TLUserPreferences.js.map +2 -2
- package/dist-cjs/lib/config/createTLStore.js.map +1 -1
- package/dist-cjs/lib/editor/Editor.js +39 -18
- package/dist-cjs/lib/editor/Editor.js.map +2 -2
- package/dist-cjs/lib/editor/derivations/notVisibleShapes.js +32 -13
- package/dist-cjs/lib/editor/derivations/notVisibleShapes.js.map +2 -2
- package/dist-cjs/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.js +2 -3
- package/dist-cjs/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/HistoryManager/HistoryManager.js +13 -38
- package/dist-cjs/lib/editor/managers/HistoryManager/HistoryManager.js.map +3 -3
- package/dist-cjs/lib/editor/managers/ScribbleManager/ScribbleManager.js +378 -89
- package/dist-cjs/lib/editor/managers/ScribbleManager/ScribbleManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js +8 -3
- package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js.map +2 -2
- package/dist-cjs/lib/editor/shapes/ShapeUtil.js +29 -0
- package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
- package/dist-cjs/lib/editor/tools/RootState.js +0 -13
- package/dist-cjs/lib/editor/tools/RootState.js.map +2 -2
- package/dist-cjs/lib/hooks/usePeerIds.js +35 -0
- package/dist-cjs/lib/hooks/usePeerIds.js.map +2 -2
- package/dist-cjs/lib/hooks/useShapeCulling.js +75 -0
- package/dist-cjs/lib/hooks/useShapeCulling.js.map +7 -0
- package/dist-cjs/lib/license/LicenseManager.js +6 -6
- package/dist-cjs/lib/license/LicenseManager.js.map +2 -2
- package/dist-cjs/lib/options.js +7 -1
- package/dist-cjs/lib/options.js.map +2 -2
- package/dist-cjs/lib/utils/collaboratorState.js +42 -0
- package/dist-cjs/lib/utils/collaboratorState.js.map +7 -0
- package/dist-cjs/version.js +3 -3
- package/dist-cjs/version.js.map +1 -1
- package/dist-esm/index.d.mts +277 -30
- package/dist-esm/index.mjs +1 -6
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/TldrawEditor.mjs +20 -8
- package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
- package/dist-esm/lib/components/LiveCollaborators.mjs +17 -24
- package/dist-esm/lib/components/LiveCollaborators.mjs.map +2 -2
- package/dist-esm/lib/components/Shape.mjs +12 -17
- package/dist-esm/lib/components/Shape.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/CanvasShapeIndicators.mjs +206 -0
- package/dist-esm/lib/components/default-components/CanvasShapeIndicators.mjs.map +7 -0
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +45 -15
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs +3 -1
- package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultShapeIndicators.mjs +13 -1
- package/dist-esm/lib/components/default-components/DefaultShapeIndicators.mjs.map +2 -2
- package/dist-esm/lib/config/TLUserPreferences.mjs +9 -3
- package/dist-esm/lib/config/TLUserPreferences.mjs.map +2 -2
- package/dist-esm/lib/config/createTLStore.mjs.map +1 -1
- package/dist-esm/lib/editor/Editor.mjs +39 -18
- package/dist-esm/lib/editor/Editor.mjs.map +2 -2
- package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs +32 -13
- package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.mjs +2 -3
- package/dist-esm/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/HistoryManager/HistoryManager.mjs +14 -39
- package/dist-esm/lib/editor/managers/HistoryManager/HistoryManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/ScribbleManager/ScribbleManager.mjs +378 -89
- package/dist-esm/lib/editor/managers/ScribbleManager/ScribbleManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs +8 -3
- package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs.map +2 -2
- package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +29 -0
- package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/editor/tools/RootState.mjs +0 -13
- package/dist-esm/lib/editor/tools/RootState.mjs.map +2 -2
- package/dist-esm/lib/hooks/usePeerIds.mjs +39 -1
- package/dist-esm/lib/hooks/usePeerIds.mjs.map +2 -2
- package/dist-esm/lib/hooks/useShapeCulling.mjs +55 -0
- package/dist-esm/lib/hooks/useShapeCulling.mjs.map +7 -0
- package/dist-esm/lib/license/LicenseManager.mjs +6 -6
- package/dist-esm/lib/license/LicenseManager.mjs.map +2 -2
- package/dist-esm/lib/options.mjs +7 -1
- package/dist-esm/lib/options.mjs.map +2 -2
- package/dist-esm/lib/utils/collaboratorState.mjs +22 -0
- package/dist-esm/lib/utils/collaboratorState.mjs.map +7 -0
- package/dist-esm/version.mjs +3 -3
- package/dist-esm/version.mjs.map +1 -1
- package/editor.css +28 -11
- package/package.json +8 -11
- package/src/index.ts +2 -5
- package/src/lib/TldrawEditor.tsx +35 -13
- package/src/lib/components/LiveCollaborators.tsx +26 -37
- package/src/lib/components/Shape.tsx +15 -16
- package/src/lib/components/default-components/CanvasShapeIndicators.tsx +288 -0
- package/src/lib/components/default-components/DefaultCanvas.tsx +39 -3
- package/src/lib/components/default-components/DefaultShapeIndicator.tsx +6 -1
- package/src/lib/components/default-components/DefaultShapeIndicators.tsx +16 -1
- package/src/lib/config/TLUserPreferences.test.ts +1 -0
- package/src/lib/config/TLUserPreferences.ts +8 -0
- package/src/lib/config/createTLStore.ts +1 -1
- package/src/lib/editor/Editor.ts +72 -20
- package/src/lib/editor/derivations/notVisibleShapes.ts +39 -17
- package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.test.ts +0 -35
- package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.ts +4 -8
- package/src/lib/editor/managers/HistoryManager/HistoryManager.ts +19 -47
- package/src/lib/editor/managers/ScribbleManager/ScribbleManager.ts +491 -106
- package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.test.ts +24 -0
- package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.ts +8 -0
- package/src/lib/editor/shapes/ShapeUtil.ts +63 -5
- package/src/lib/editor/tools/RootState.ts +0 -16
- package/src/lib/hooks/usePeerIds.ts +53 -1
- package/src/lib/hooks/useShapeCulling.tsx +98 -0
- package/src/lib/license/LicenseManager.ts +6 -6
- package/src/lib/options.ts +48 -2
- package/src/lib/utils/collaboratorState.ts +54 -0
- package/src/version.ts +3 -3
- package/src/lib/editor/managers/ScribbleManager/ScribbleManager.test.ts +0 -621
package/src/lib/editor/Editor.ts
CHANGED
|
@@ -153,7 +153,13 @@ import { SpatialIndexManager } from './managers/SpatialIndexManager/SpatialIndex
|
|
|
153
153
|
import { TextManager } from './managers/TextManager/TextManager'
|
|
154
154
|
import { TickManager } from './managers/TickManager/TickManager'
|
|
155
155
|
import { UserPreferencesManager } from './managers/UserPreferencesManager/UserPreferencesManager'
|
|
156
|
-
import {
|
|
156
|
+
import {
|
|
157
|
+
ShapeUtil,
|
|
158
|
+
TLEditStartInfo,
|
|
159
|
+
TLGeometryOpts,
|
|
160
|
+
TLResizeMode,
|
|
161
|
+
TLShapeUtilCanBindOpts,
|
|
162
|
+
} from './shapes/ShapeUtil'
|
|
157
163
|
import { RootState } from './tools/RootState'
|
|
158
164
|
import { StateNode, TLStateNodeConstructor } from './tools/StateNode'
|
|
159
165
|
import { TLContent } from './types/clipboard-types'
|
|
@@ -228,10 +234,17 @@ export interface TLEditorOptions {
|
|
|
228
234
|
inferDarkMode?: boolean
|
|
229
235
|
/**
|
|
230
236
|
* Options for the editor's camera.
|
|
237
|
+
*
|
|
238
|
+
* @deprecated Use `options.cameraOptions` instead. This will be removed in a future release.
|
|
231
239
|
*/
|
|
232
240
|
cameraOptions?: Partial<TLCameraOptions>
|
|
233
|
-
textOptions?: TLTextOptions
|
|
234
241
|
options?: Partial<TldrawOptions>
|
|
242
|
+
/**
|
|
243
|
+
* Text options for the editor.
|
|
244
|
+
*
|
|
245
|
+
* @deprecated Use `options.text` instead. This prop will be removed in a future release.
|
|
246
|
+
*/
|
|
247
|
+
textOptions?: TLTextOptions
|
|
235
248
|
licenseKey?: string
|
|
236
249
|
fontAssetUrls?: { [key: string]: string | undefined }
|
|
237
250
|
/**
|
|
@@ -283,12 +296,16 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
283
296
|
bindingUtils,
|
|
284
297
|
tools,
|
|
285
298
|
getContainer,
|
|
299
|
+
// needs to be here for backwards compatibility with TldrawEditor
|
|
300
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
286
301
|
cameraOptions,
|
|
287
|
-
textOptions,
|
|
288
302
|
initialState,
|
|
289
303
|
autoFocus,
|
|
290
304
|
inferDarkMode,
|
|
291
|
-
options,
|
|
305
|
+
options: _options,
|
|
306
|
+
// needs to be here for backwards compatibility with TldrawEditor
|
|
307
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
308
|
+
textOptions: _textOptions,
|
|
292
309
|
getShapeVisibility,
|
|
293
310
|
fontAssetUrls,
|
|
294
311
|
}: TLEditorOptions) {
|
|
@@ -296,6 +313,10 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
296
313
|
|
|
297
314
|
this._getShapeVisibility = getShapeVisibility
|
|
298
315
|
|
|
316
|
+
// Merge deprecated textOptions prop with options.text
|
|
317
|
+
// options.text takes precedence over the deprecated textOptions prop
|
|
318
|
+
const options = _textOptions ? { ..._options, text: _options?.text ?? _textOptions } : _options
|
|
319
|
+
|
|
299
320
|
this.options = { ...defaultTldrawOptions, ...options }
|
|
300
321
|
|
|
301
322
|
this.store = store
|
|
@@ -314,9 +335,14 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
314
335
|
|
|
315
336
|
this.disposables.add(this.timers.dispose)
|
|
316
337
|
|
|
317
|
-
|
|
338
|
+
// Merge camera options: options.cameraOptions takes precedence over deprecated cameraOptions prop
|
|
339
|
+
this._cameraOptions.set({
|
|
340
|
+
...DEFAULT_CAMERA_OPTIONS,
|
|
341
|
+
...cameraOptions,
|
|
342
|
+
...options?.camera,
|
|
343
|
+
})
|
|
318
344
|
|
|
319
|
-
this._textOptions = atom('text options',
|
|
345
|
+
this._textOptions = atom('text options', options?.text ?? null)
|
|
320
346
|
|
|
321
347
|
this.user = new UserPreferencesManager(user ?? createTLUser(), inferDarkMode ?? false)
|
|
322
348
|
this.disposables.add(() => this.user.dispose())
|
|
@@ -443,6 +469,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
443
469
|
const deletedShapeIds = new Set<TLShapeId>()
|
|
444
470
|
const invalidParents = new Set<TLShapeId>()
|
|
445
471
|
let invalidBindingTypes = new Set<TLBinding['type']>()
|
|
472
|
+
|
|
446
473
|
this.disposables.add(
|
|
447
474
|
this.sideEffects.registerOperationCompleteHandler(() => {
|
|
448
475
|
// this needs to be cleared here because further effects may delete more shapes
|
|
@@ -2772,7 +2799,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2772
2799
|
}
|
|
2773
2800
|
|
|
2774
2801
|
/**
|
|
2775
|
-
* Get the efficient zoom level. This returns the current zoom level if there are less than
|
|
2802
|
+
* Get the efficient zoom level. This returns the current zoom level if there are less than a certain number of shapes on the page,
|
|
2776
2803
|
* otherwise it returns the debounced zoom level. This can be used to avoid expensive re-renders during camera movements.
|
|
2777
2804
|
*
|
|
2778
2805
|
* @public
|
|
@@ -3225,7 +3252,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3225
3252
|
* @public
|
|
3226
3253
|
*/
|
|
3227
3254
|
zoomToFit(opts?: TLCameraMoveOptions): this {
|
|
3228
|
-
const ids = [...this.getCurrentPageShapeIds()]
|
|
3255
|
+
const ids = [...this.getCurrentPageShapeIds()].filter((id) => !this.isShapeHidden(id))
|
|
3229
3256
|
if (ids.length <= 0) return this
|
|
3230
3257
|
const pageBounds = Box.Common(compact(ids.map((id) => this.getShapePageBounds(id))))
|
|
3231
3258
|
this.zoomToBounds(pageBounds, opts)
|
|
@@ -4180,23 +4207,25 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
4180
4207
|
// unmount / remount in the DOM, which is expensive; and computing visibility is
|
|
4181
4208
|
// also expensive in large projects. For this reason, we use a second bounding
|
|
4182
4209
|
// box just for rendering, and we only update after the camera stops moving.
|
|
4183
|
-
private _cameraState = atom('camera state', 'idle' as 'idle' | 'moving')
|
|
4184
4210
|
private _cameraStateTimeoutRemaining = 0
|
|
4185
4211
|
private _decayCameraStateTimeout(elapsed: number) {
|
|
4186
4212
|
this._cameraStateTimeoutRemaining -= elapsed
|
|
4187
4213
|
if (this._cameraStateTimeoutRemaining > 0) return
|
|
4188
4214
|
this.off('tick', this._decayCameraStateTimeout)
|
|
4189
|
-
this.
|
|
4215
|
+
this._setCameraState('idle')
|
|
4190
4216
|
}
|
|
4191
4217
|
private _tickCameraState() {
|
|
4192
4218
|
// always reset the timeout
|
|
4193
4219
|
this._cameraStateTimeoutRemaining = this.options.cameraMovingTimeoutMs
|
|
4194
4220
|
// If the state is idle, then start the tick
|
|
4195
|
-
if (this.
|
|
4196
|
-
this.
|
|
4221
|
+
if (this.getInstanceState().cameraState !== 'idle') return
|
|
4222
|
+
this._setCameraState('moving')
|
|
4197
4223
|
this._debouncedZoomLevel.set(unsafe__withoutCapture(() => this.getCamera().z))
|
|
4198
4224
|
this.on('tick', this._decayCameraStateTimeout)
|
|
4199
4225
|
}
|
|
4226
|
+
private _setCameraState(cameraState: 'idle' | 'moving') {
|
|
4227
|
+
this.updateInstanceState({ cameraState }, { history: 'ignore' })
|
|
4228
|
+
}
|
|
4200
4229
|
|
|
4201
4230
|
/**
|
|
4202
4231
|
* Whether the camera is moving or idle.
|
|
@@ -4209,7 +4238,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
4209
4238
|
* @public
|
|
4210
4239
|
*/
|
|
4211
4240
|
getCameraState() {
|
|
4212
|
-
return this.
|
|
4241
|
+
return this.getInstanceState().cameraState
|
|
4213
4242
|
}
|
|
4214
4243
|
|
|
4215
4244
|
/**
|
|
@@ -5199,6 +5228,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5199
5228
|
let commonBounds: Box | undefined
|
|
5200
5229
|
|
|
5201
5230
|
this.getCurrentPageShapeIdsSorted().forEach((shapeId) => {
|
|
5231
|
+
if (this.isShapeHidden(shapeId)) return
|
|
5202
5232
|
const bounds = this.getShapeMaskedPageBounds(shapeId)
|
|
5203
5233
|
if (!bounds) return
|
|
5204
5234
|
if (!commonBounds) {
|
|
@@ -5480,7 +5510,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5480
5510
|
* @param bounds - The bounds to search within.
|
|
5481
5511
|
* @returns Unordered set of shape IDs within the given bounds.
|
|
5482
5512
|
*
|
|
5483
|
-
* @
|
|
5513
|
+
* @public
|
|
5484
5514
|
*/
|
|
5485
5515
|
getShapeIdsInsideBounds(bounds: Box): Set<TLShapeId> {
|
|
5486
5516
|
return this._spatialIndex.getShapeIdsInsideBounds(bounds)
|
|
@@ -6245,7 +6275,13 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
6245
6275
|
const toShapeType = typeof toShape === 'string' ? toShape : toShape.type
|
|
6246
6276
|
const bindingType = typeof binding === 'string' ? binding : binding.type
|
|
6247
6277
|
|
|
6248
|
-
const canBindOpts = {
|
|
6278
|
+
const canBindOpts: TLShapeUtilCanBindOpts = {
|
|
6279
|
+
fromShape: typeof fromShape === 'string' ? { type: fromShape } : fromShape,
|
|
6280
|
+
toShape: typeof toShape === 'string' ? { type: toShape } : toShape,
|
|
6281
|
+
bindingType,
|
|
6282
|
+
fromShapeType,
|
|
6283
|
+
toShapeType,
|
|
6284
|
+
}
|
|
6249
6285
|
|
|
6250
6286
|
if (fromShapeType === toShapeType) {
|
|
6251
6287
|
return this.getShapeUtil(fromShapeType).canBind(canBindOpts)
|
|
@@ -7838,6 +7874,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
7838
7874
|
initialShape: options.initialShape,
|
|
7839
7875
|
initialBounds: options.initialBounds,
|
|
7840
7876
|
isAspectRatioLocked: options.isAspectRatioLocked,
|
|
7877
|
+
initialPageTransform: options.initialPageTransform,
|
|
7841
7878
|
})
|
|
7842
7879
|
|
|
7843
7880
|
// then if the shape is flipped in one axis only, we need to apply an extra rotation
|
|
@@ -9961,7 +9998,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9961
9998
|
/**
|
|
9962
9999
|
* Handles navigating to the content specified by the query param in the given URL.
|
|
9963
10000
|
*
|
|
9964
|
-
* Use {@link Editor
|
|
10001
|
+
* Use {@link Editor.createDeepLink} to create a URL with a deep link query param.
|
|
9965
10002
|
*
|
|
9966
10003
|
* If no URL is provided, it will look for the param in the current `window.location.href`.
|
|
9967
10004
|
*
|
|
@@ -10420,9 +10457,10 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10420
10457
|
if (inputs.getIsPinching()) return
|
|
10421
10458
|
|
|
10422
10459
|
if (!inputs.getIsEditing()) {
|
|
10423
|
-
|
|
10424
|
-
|
|
10425
|
-
|
|
10460
|
+
// Always capture the current selection when pinch starts.
|
|
10461
|
+
// This ensures Safari (which uses gesture events instead of wheel)
|
|
10462
|
+
// doesn't restore a stale selection from an earlier pointer_down.
|
|
10463
|
+
this._selectedShapeIdsAtPointerDown = [...pageState.selectedShapeIds]
|
|
10426
10464
|
|
|
10427
10465
|
this._didPinch = true
|
|
10428
10466
|
|
|
@@ -10544,7 +10582,16 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10544
10582
|
}
|
|
10545
10583
|
}
|
|
10546
10584
|
|
|
10547
|
-
|
|
10585
|
+
// because we can't for sure detect whether a user is using a mouse or a trackpad,
|
|
10586
|
+
// we need to check the input mode preference, and only invert the zoom direction
|
|
10587
|
+
// if the user has specifically set it to a mouse.
|
|
10588
|
+
const isZoomDirectionInverted =
|
|
10589
|
+
(this.user.getUserPreferences().isZoomDirectionInverted && inputMode === 'mouse') ??
|
|
10590
|
+
false
|
|
10591
|
+
const deltaValue = delta ?? 0
|
|
10592
|
+
const finalDelta = isZoomDirectionInverted ? -deltaValue : deltaValue
|
|
10593
|
+
|
|
10594
|
+
const zoom = cz + finalDelta * zoomSpeed * cz
|
|
10548
10595
|
this._setCamera(new Vec(cx + x / zoom - x / cz, cy + y / zoom - y / cz, zoom), {
|
|
10549
10596
|
immediate: true,
|
|
10550
10597
|
})
|
|
@@ -10724,6 +10771,11 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10724
10771
|
this.setCurrentTool(this._restoreToolId)
|
|
10725
10772
|
}
|
|
10726
10773
|
}
|
|
10774
|
+
|
|
10775
|
+
// Clear the stashed selection so the next pinch captures fresh state.
|
|
10776
|
+
// This fixes Safari pinch zoom restoring outdated selections.
|
|
10777
|
+
this._selectedShapeIdsAtPointerDown = []
|
|
10778
|
+
|
|
10727
10779
|
break
|
|
10728
10780
|
}
|
|
10729
10781
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { computed, isUninitialized } from '@tldraw/state'
|
|
2
|
-
import { TLShapeId } from '@tldraw/tlschema'
|
|
2
|
+
import { TLShape, TLShapeId } from '@tldraw/tlschema'
|
|
3
3
|
import { Editor } from '../Editor'
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -9,34 +9,56 @@ import { Editor } from '../Editor'
|
|
|
9
9
|
* @returns Incremental derivation of non visible shapes.
|
|
10
10
|
*/
|
|
11
11
|
export function notVisibleShapes(editor: Editor) {
|
|
12
|
+
const emptySet = new Set<TLShapeId>()
|
|
13
|
+
|
|
12
14
|
return computed<Set<TLShapeId>>('notVisibleShapes', function (prevValue) {
|
|
13
|
-
const
|
|
15
|
+
const allShapes = editor.getCurrentPageShapes()
|
|
14
16
|
const viewportPageBounds = editor.getViewportPageBounds()
|
|
15
17
|
const visibleIds = editor.getShapeIdsInsideBounds(viewportPageBounds)
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
// Non-visible shapes are all shapes minus visible shapes
|
|
20
|
-
for (const id of allShapeIds) {
|
|
21
|
-
if (!visibleIds.has(id)) {
|
|
22
|
-
const shape = editor.getShape(id)
|
|
23
|
-
if (!shape) continue
|
|
19
|
+
let shape: TLShape | undefined
|
|
24
20
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
21
|
+
// Fast path: if all shapes are visible, return empty set
|
|
22
|
+
if (visibleIds.size === allShapes.length) {
|
|
23
|
+
if (isUninitialized(prevValue) || prevValue.size > 0) {
|
|
24
|
+
return emptySet
|
|
29
25
|
}
|
|
26
|
+
return prevValue
|
|
30
27
|
}
|
|
31
28
|
|
|
32
|
-
|
|
29
|
+
// First run: compute from scratch
|
|
30
|
+
if (isUninitialized(prevValue)) {
|
|
31
|
+
const nextValue = new Set<TLShapeId>()
|
|
32
|
+
for (let i = 0; i < allShapes.length; i++) {
|
|
33
|
+
shape = allShapes[i]
|
|
34
|
+
if (visibleIds.has(shape.id)) continue
|
|
35
|
+
if (!editor.getShapeUtil(shape.type).canCull(shape)) continue
|
|
36
|
+
nextValue.add(shape.id)
|
|
37
|
+
}
|
|
33
38
|
return nextValue
|
|
34
39
|
}
|
|
35
40
|
|
|
36
|
-
|
|
37
|
-
|
|
41
|
+
// Subsequent runs: single pass to collect IDs and detect changes
|
|
42
|
+
const notVisibleIds: TLShapeId[] = []
|
|
43
|
+
for (let i = 0; i < allShapes.length; i++) {
|
|
44
|
+
shape = allShapes[i]
|
|
45
|
+
if (visibleIds.has(shape.id)) continue
|
|
46
|
+
if (!editor.getShapeUtil(shape.type).canCull(shape)) continue
|
|
47
|
+
notVisibleIds.push(shape.id)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Check if the result changed
|
|
51
|
+
if (notVisibleIds.length === prevValue.size) {
|
|
52
|
+
let same = true
|
|
53
|
+
for (let i = 0; i < notVisibleIds.length; i++) {
|
|
54
|
+
if (!prevValue.has(notVisibleIds[i])) {
|
|
55
|
+
same = false
|
|
56
|
+
break
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (same) return prevValue
|
|
38
60
|
}
|
|
39
61
|
|
|
40
|
-
return
|
|
62
|
+
return new Set(notVisibleIds)
|
|
41
63
|
})
|
|
42
64
|
}
|
|
@@ -240,41 +240,6 @@ describe('EdgeScrollManager', () => {
|
|
|
240
240
|
})
|
|
241
241
|
})
|
|
242
242
|
|
|
243
|
-
describe('camera movement conditions', () => {
|
|
244
|
-
it('should not move camera when not dragging', () => {
|
|
245
|
-
editor.inputs.setIsDragging(false)
|
|
246
|
-
mockInputs.setCurrentScreenPoint(new Vec(5, 300))
|
|
247
|
-
|
|
248
|
-
edgeScrollManager.updateEdgeScrolling(300)
|
|
249
|
-
|
|
250
|
-
expect(editor.setCamera).not.toHaveBeenCalled()
|
|
251
|
-
})
|
|
252
|
-
|
|
253
|
-
it('should not move camera when panning', () => {
|
|
254
|
-
editor.inputs.setIsPanning(true)
|
|
255
|
-
mockInputs.setCurrentScreenPoint(new Vec(5, 300))
|
|
256
|
-
|
|
257
|
-
edgeScrollManager.updateEdgeScrolling(300)
|
|
258
|
-
|
|
259
|
-
expect(editor.setCamera).not.toHaveBeenCalled()
|
|
260
|
-
})
|
|
261
|
-
|
|
262
|
-
it('should not move camera when camera is locked', () => {
|
|
263
|
-
editor.getCameraOptions.mockReturnValue({
|
|
264
|
-
isLocked: true,
|
|
265
|
-
panSpeed: 1,
|
|
266
|
-
zoomSpeed: 1,
|
|
267
|
-
zoomSteps: [1],
|
|
268
|
-
wheelBehavior: 'pan' as const,
|
|
269
|
-
})
|
|
270
|
-
mockInputs.setCurrentScreenPoint(new Vec(5, 300))
|
|
271
|
-
|
|
272
|
-
edgeScrollManager.updateEdgeScrolling(300)
|
|
273
|
-
|
|
274
|
-
expect(editor.setCamera).not.toHaveBeenCalled()
|
|
275
|
-
})
|
|
276
|
-
})
|
|
277
|
-
|
|
278
243
|
describe('camera movement calculation', () => {
|
|
279
244
|
it('should calculate scroll speed based on user preference', () => {
|
|
280
245
|
editor.user.getEdgeScrollSpeed.mockReturnValue(2)
|
|
@@ -21,6 +21,9 @@ export class EdgeScrollManager {
|
|
|
21
21
|
*/
|
|
22
22
|
updateEdgeScrolling(elapsed: number) {
|
|
23
23
|
const { editor } = this
|
|
24
|
+
|
|
25
|
+
if (editor.getCameraOptions().isLocked) return
|
|
26
|
+
|
|
24
27
|
const edgeScrollProximityFactor = this.getEdgeScroll()
|
|
25
28
|
if (edgeScrollProximityFactor.x === 0 && edgeScrollProximityFactor.y === 0) {
|
|
26
29
|
if (this._isEdgeScrolling) {
|
|
@@ -106,15 +109,8 @@ export class EdgeScrollManager {
|
|
|
106
109
|
* @public
|
|
107
110
|
*/
|
|
108
111
|
private moveCameraWhenCloseToEdge(proximityFactor: { x: number; y: number }) {
|
|
109
|
-
const { editor } = this
|
|
110
|
-
if (
|
|
111
|
-
!editor.inputs.getIsDragging() ||
|
|
112
|
-
editor.inputs.getIsPanning() ||
|
|
113
|
-
editor.getCameraOptions().isLocked
|
|
114
|
-
)
|
|
115
|
-
return
|
|
116
|
-
|
|
117
112
|
if (proximityFactor.x === 0 && proximityFactor.y === 0) return
|
|
113
|
+
const { editor } = this
|
|
118
114
|
|
|
119
115
|
const screenBounds = editor.getViewportScreenBounds()
|
|
120
116
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { atom, transact } from '@tldraw/state'
|
|
1
|
+
import { atom, EMPTY_ARRAY, transact } from '@tldraw/state'
|
|
2
2
|
import {
|
|
3
|
-
RecordsDiff,
|
|
4
|
-
Store,
|
|
5
|
-
UnknownRecord,
|
|
6
3
|
createEmptyRecordsDiff,
|
|
7
4
|
isRecordsDiffEmpty,
|
|
5
|
+
RecordsDiff,
|
|
8
6
|
reverseRecordsDiff,
|
|
9
7
|
squashRecordDiffsMutable,
|
|
8
|
+
Store,
|
|
9
|
+
UnknownRecord,
|
|
10
10
|
} from '@tldraw/store'
|
|
11
11
|
import { exhaustiveSwitchError, noop } from '@tldraw/utils'
|
|
12
12
|
import { TLHistoryBatchOptions, TLHistoryEntry } from '../../types/history-types'
|
|
@@ -312,8 +312,8 @@ export class HistoryManager<R extends UnknownRecord> {
|
|
|
312
312
|
debug() {
|
|
313
313
|
const { undos, redos } = this.stacks.get()
|
|
314
314
|
return {
|
|
315
|
-
undos: undos
|
|
316
|
-
redos: redos
|
|
315
|
+
undos: stackToArray(undos),
|
|
316
|
+
redos: stackToArray(redos),
|
|
317
317
|
pendingDiff: this.pendingDiff.debug(),
|
|
318
318
|
state: this.state as string,
|
|
319
319
|
}
|
|
@@ -351,22 +351,13 @@ class PendingDiff<R extends UnknownRecord> {
|
|
|
351
351
|
}
|
|
352
352
|
}
|
|
353
353
|
|
|
354
|
-
|
|
354
|
+
type Stack<T> = StackItem<T> | EmptyStackItem<T>
|
|
355
355
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
export function stack<T>(items?: Array<T>): Stack<T> {
|
|
359
|
-
if (items) {
|
|
360
|
-
let result = EMPTY_STACK_ITEM as Stack<T>
|
|
361
|
-
while (items.length) {
|
|
362
|
-
result = result.push(items.pop()!)
|
|
363
|
-
}
|
|
364
|
-
return result
|
|
365
|
-
}
|
|
356
|
+
function stack<T>(): Stack<T> {
|
|
366
357
|
return EMPTY_STACK_ITEM as any
|
|
367
358
|
}
|
|
368
359
|
|
|
369
|
-
class EmptyStackItem<T>
|
|
360
|
+
class EmptyStackItem<T> {
|
|
370
361
|
readonly length = 0
|
|
371
362
|
readonly head = null
|
|
372
363
|
readonly tail: Stack<T> = this
|
|
@@ -374,23 +365,11 @@ class EmptyStackItem<T> implements Iterable<T> {
|
|
|
374
365
|
push(head: T): Stack<T> {
|
|
375
366
|
return new StackItem<T>(head, this)
|
|
376
367
|
}
|
|
377
|
-
|
|
378
|
-
toArray() {
|
|
379
|
-
return EMPTY_ARRAY
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
[Symbol.iterator]() {
|
|
383
|
-
return {
|
|
384
|
-
next() {
|
|
385
|
-
return { value: undefined, done: true as const }
|
|
386
|
-
},
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
368
|
}
|
|
390
369
|
|
|
391
370
|
const EMPTY_STACK_ITEM = new EmptyStackItem()
|
|
392
371
|
|
|
393
|
-
class StackItem<T>
|
|
372
|
+
class StackItem<T> {
|
|
394
373
|
length: number
|
|
395
374
|
constructor(
|
|
396
375
|
public readonly head: T,
|
|
@@ -402,23 +381,16 @@ class StackItem<T> implements Iterable<T> {
|
|
|
402
381
|
push(head: T): Stack<T> {
|
|
403
382
|
return new StackItem(head, this)
|
|
404
383
|
}
|
|
384
|
+
}
|
|
405
385
|
|
|
406
|
-
|
|
407
|
-
|
|
386
|
+
function stackToArray<T>(stack: Stack<T>) {
|
|
387
|
+
if (!stack.length) {
|
|
388
|
+
return EMPTY_ARRAY
|
|
408
389
|
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
next() {
|
|
414
|
-
if (stack.length) {
|
|
415
|
-
const value = stack.head!
|
|
416
|
-
stack = stack.tail
|
|
417
|
-
return { value, done: false as const }
|
|
418
|
-
} else {
|
|
419
|
-
return { value: undefined, done: true as const }
|
|
420
|
-
}
|
|
421
|
-
},
|
|
422
|
-
}
|
|
390
|
+
const arr: T[] = []
|
|
391
|
+
while (stack.length) {
|
|
392
|
+
arr.push(stack.head!)
|
|
393
|
+
stack = stack.tail
|
|
423
394
|
}
|
|
395
|
+
return arr
|
|
424
396
|
}
|