@tldraw/editor 4.6.0-next.fe1474dc57d8 → 5.0.0
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 +412 -179
- package/dist-cjs/index.js +12 -23
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/TldrawEditor.js +3 -0
- package/dist-cjs/lib/TldrawEditor.js.map +2 -2
- package/dist-cjs/lib/components/default-components/CanvasOverlays.js +180 -0
- package/dist-cjs/lib/components/default-components/CanvasOverlays.js.map +7 -0
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js +44 -249
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +3 -3
- package/dist-cjs/lib/editor/Editor.js +78 -28
- package/dist-cjs/lib/editor/Editor.js.map +2 -2
- package/dist-cjs/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.js +98 -0
- package/dist-cjs/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.js.map +7 -0
- package/dist-cjs/lib/editor/managers/ThemeManager/defaultThemes.js +14 -0
- package/dist-cjs/lib/editor/managers/ThemeManager/defaultThemes.js.map +2 -2
- package/dist-cjs/lib/editor/overlays/OverlayManager.js +154 -0
- package/dist-cjs/lib/editor/overlays/OverlayManager.js.map +7 -0
- package/dist-cjs/lib/editor/overlays/OverlayUtil.js +92 -0
- package/dist-cjs/lib/editor/overlays/OverlayUtil.js.map +7 -0
- package/dist-cjs/lib/editor/overlays/ShapeIndicatorOverlayUtil.js +161 -0
- package/dist-cjs/lib/editor/overlays/ShapeIndicatorOverlayUtil.js.map +7 -0
- package/dist-cjs/lib/editor/overlays/getOverlayDisplayValues.js +39 -0
- package/dist-cjs/lib/editor/overlays/getOverlayDisplayValues.js.map +7 -0
- package/dist-cjs/lib/editor/shapes/BaseFrameLikeShapeUtil.js +3 -0
- package/dist-cjs/lib/editor/shapes/BaseFrameLikeShapeUtil.js.map +2 -2
- package/dist-cjs/lib/editor/shapes/ShapeUtil.js +25 -23
- package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
- package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js +32 -2
- package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js.map +2 -2
- package/dist-cjs/lib/editor/types/event-types.js.map +2 -2
- package/dist-cjs/lib/exports/fetchCache.js +1 -1
- package/dist-cjs/lib/exports/fetchCache.js.map +2 -2
- package/dist-cjs/lib/hooks/EditorComponentsContext.js.map +2 -2
- package/dist-cjs/lib/hooks/useCanvasEvents.js +3 -3
- package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/useEditorComponents.js +0 -28
- package/dist-cjs/lib/hooks/useEditorComponents.js.map +2 -2
- package/dist-cjs/lib/hooks/usePeerIds.js +1 -36
- package/dist-cjs/lib/hooks/usePeerIds.js.map +2 -2
- package/dist-cjs/lib/hooks/useShapeCulling.js +2 -1
- package/dist-cjs/lib/hooks/useShapeCulling.js.map +2 -2
- package/dist-cjs/lib/options.js +0 -1
- package/dist-cjs/lib/options.js.map +2 -2
- package/dist-cjs/lib/utils/reparenting.js +20 -7
- package/dist-cjs/lib/utils/reparenting.js.map +2 -2
- package/dist-cjs/lib/utils/sync/TLLocalSyncClient.js +3 -0
- package/dist-cjs/lib/utils/sync/TLLocalSyncClient.js.map +2 -2
- package/dist-cjs/version.js +4 -4
- package/dist-cjs/version.js.map +1 -1
- package/dist-esm/index.d.mts +412 -179
- package/dist-esm/index.mjs +19 -41
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/TldrawEditor.mjs +3 -0
- package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/CanvasOverlays.mjs +160 -0
- package/dist-esm/lib/components/default-components/CanvasOverlays.mjs.map +7 -0
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +45 -250
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +3 -3
- package/dist-esm/lib/editor/Editor.mjs +78 -29
- package/dist-esm/lib/editor/Editor.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.mjs +83 -0
- package/dist-esm/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.mjs.map +7 -0
- package/dist-esm/lib/editor/managers/ThemeManager/defaultThemes.mjs +14 -0
- package/dist-esm/lib/editor/managers/ThemeManager/defaultThemes.mjs.map +2 -2
- package/dist-esm/lib/editor/overlays/OverlayManager.mjs +136 -0
- package/dist-esm/lib/editor/overlays/OverlayManager.mjs.map +7 -0
- package/dist-esm/lib/editor/overlays/OverlayUtil.mjs +72 -0
- package/dist-esm/lib/editor/overlays/OverlayUtil.mjs.map +7 -0
- package/dist-esm/lib/editor/overlays/ShapeIndicatorOverlayUtil.mjs +141 -0
- package/dist-esm/lib/editor/overlays/ShapeIndicatorOverlayUtil.mjs.map +7 -0
- package/dist-esm/lib/editor/overlays/getOverlayDisplayValues.mjs +19 -0
- package/dist-esm/lib/editor/overlays/getOverlayDisplayValues.mjs.map +7 -0
- package/dist-esm/lib/editor/shapes/BaseFrameLikeShapeUtil.mjs +3 -0
- package/dist-esm/lib/editor/shapes/BaseFrameLikeShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +25 -23
- package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs +32 -2
- package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/editor/types/event-types.mjs.map +2 -2
- package/dist-esm/lib/exports/fetchCache.mjs +2 -2
- package/dist-esm/lib/exports/fetchCache.mjs.map +2 -2
- package/dist-esm/lib/hooks/EditorComponentsContext.mjs.map +2 -2
- package/dist-esm/lib/hooks/useCanvasEvents.mjs +3 -3
- package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useEditorComponents.mjs +0 -28
- package/dist-esm/lib/hooks/useEditorComponents.mjs.map +2 -2
- package/dist-esm/lib/hooks/usePeerIds.mjs +2 -40
- package/dist-esm/lib/hooks/usePeerIds.mjs.map +2 -2
- package/dist-esm/lib/hooks/useShapeCulling.mjs +2 -1
- package/dist-esm/lib/hooks/useShapeCulling.mjs.map +2 -2
- package/dist-esm/lib/options.mjs +0 -1
- package/dist-esm/lib/options.mjs.map +2 -2
- package/dist-esm/lib/utils/reparenting.mjs +20 -7
- package/dist-esm/lib/utils/reparenting.mjs.map +2 -2
- package/dist-esm/lib/utils/sync/TLLocalSyncClient.mjs +3 -0
- package/dist-esm/lib/utils/sync/TLLocalSyncClient.mjs.map +2 -2
- package/dist-esm/version.mjs +4 -4
- package/dist-esm/version.mjs.map +1 -1
- package/editor.css +4 -239
- package/package.json +7 -7
- package/src/index.ts +17 -39
- package/src/lib/TldrawEditor.tsx +9 -0
- package/src/lib/components/default-components/CanvasOverlays.tsx +208 -0
- package/src/lib/components/default-components/DefaultCanvas.tsx +49 -324
- package/src/lib/editor/Editor.test.ts +3 -1
- package/src/lib/editor/Editor.ts +80 -24
- package/src/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.ts +98 -0
- package/src/lib/editor/managers/ThemeManager/defaultThemes.ts +14 -0
- package/src/lib/editor/overlays/OverlayManager.ts +183 -0
- package/src/lib/editor/overlays/OverlayUtil.ts +143 -0
- package/src/lib/editor/overlays/ShapeIndicatorOverlayUtil.ts +216 -0
- package/src/lib/editor/overlays/getOverlayDisplayValues.ts +51 -0
- package/src/lib/editor/shapes/BaseFrameLikeShapeUtil.tsx +9 -2
- package/src/lib/editor/shapes/ShapeUtil.ts +34 -26
- package/src/lib/editor/shapes/group/GroupShapeUtil.tsx +40 -3
- package/src/lib/editor/types/event-types.ts +2 -0
- package/src/lib/exports/fetchCache.ts +2 -4
- package/src/lib/exports/getSvgJsx.test.ts +3 -1
- package/src/lib/hooks/EditorComponentsContext.tsx +0 -27
- package/src/lib/hooks/useCanvasEvents.ts +13 -8
- package/src/lib/hooks/useEditorComponents.tsx +0 -28
- package/src/lib/hooks/usePeerIds.ts +6 -55
- package/src/lib/hooks/useShapeCulling.tsx +3 -1
- package/src/lib/options.ts +0 -7
- package/src/lib/utils/reparenting.ts +22 -9
- package/src/lib/utils/sync/TLLocalSyncClient.ts +3 -0
- package/src/version.ts +4 -4
- package/dist-cjs/lib/components/GeometryDebuggingView.js +0 -115
- package/dist-cjs/lib/components/GeometryDebuggingView.js.map +0 -7
- package/dist-cjs/lib/components/LiveCollaborators.js +0 -152
- package/dist-cjs/lib/components/LiveCollaborators.js.map +0 -7
- package/dist-cjs/lib/components/default-components/CanvasShapeIndicators.js +0 -234
- package/dist-cjs/lib/components/default-components/CanvasShapeIndicators.js.map +0 -7
- package/dist-cjs/lib/components/default-components/DefaultBrush.js +0 -38
- package/dist-cjs/lib/components/default-components/DefaultBrush.js.map +0 -7
- package/dist-cjs/lib/components/default-components/DefaultCollaboratorHint.js +0 -71
- package/dist-cjs/lib/components/default-components/DefaultCollaboratorHint.js.map +0 -7
- package/dist-cjs/lib/components/default-components/DefaultCursor.js +0 -59
- package/dist-cjs/lib/components/default-components/DefaultCursor.js.map +0 -7
- package/dist-cjs/lib/components/default-components/DefaultHandle.js +0 -56
- package/dist-cjs/lib/components/default-components/DefaultHandle.js.map +0 -7
- package/dist-cjs/lib/components/default-components/DefaultHandles.js +0 -28
- package/dist-cjs/lib/components/default-components/DefaultHandles.js.map +0 -7
- package/dist-cjs/lib/components/default-components/DefaultScribble.js +0 -51
- package/dist-cjs/lib/components/default-components/DefaultScribble.js.map +0 -7
- package/dist-cjs/lib/components/default-components/DefaultSelectionForeground.js +0 -69
- package/dist-cjs/lib/components/default-components/DefaultSelectionForeground.js.map +0 -7
- package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js +0 -107
- package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js.map +0 -7
- package/dist-cjs/lib/components/default-components/DefaultShapeIndicatorErrorFallback.js +0 -28
- package/dist-cjs/lib/components/default-components/DefaultShapeIndicatorErrorFallback.js.map +0 -7
- package/dist-cjs/lib/components/default-components/DefaultShapeIndicators.js +0 -102
- package/dist-cjs/lib/components/default-components/DefaultShapeIndicators.js.map +0 -7
- package/dist-cjs/lib/components/default-components/DefaultSnapIndictor.js +0 -170
- package/dist-cjs/lib/components/default-components/DefaultSnapIndictor.js.map +0 -7
- package/dist-cjs/lib/hooks/useHandleEvents.js +0 -100
- package/dist-cjs/lib/hooks/useHandleEvents.js.map +0 -7
- package/dist-cjs/lib/hooks/useSelectionEvents.js +0 -98
- package/dist-cjs/lib/hooks/useSelectionEvents.js.map +0 -7
- package/dist-esm/lib/components/GeometryDebuggingView.mjs +0 -95
- package/dist-esm/lib/components/GeometryDebuggingView.mjs.map +0 -7
- package/dist-esm/lib/components/LiveCollaborators.mjs +0 -135
- package/dist-esm/lib/components/LiveCollaborators.mjs.map +0 -7
- package/dist-esm/lib/components/default-components/CanvasShapeIndicators.mjs +0 -214
- package/dist-esm/lib/components/default-components/CanvasShapeIndicators.mjs.map +0 -7
- package/dist-esm/lib/components/default-components/DefaultBrush.mjs +0 -18
- package/dist-esm/lib/components/default-components/DefaultBrush.mjs.map +0 -7
- package/dist-esm/lib/components/default-components/DefaultCollaboratorHint.mjs +0 -41
- package/dist-esm/lib/components/default-components/DefaultCollaboratorHint.mjs.map +0 -7
- package/dist-esm/lib/components/default-components/DefaultCursor.mjs +0 -29
- package/dist-esm/lib/components/default-components/DefaultCursor.mjs.map +0 -7
- package/dist-esm/lib/components/default-components/DefaultHandle.mjs +0 -26
- package/dist-esm/lib/components/default-components/DefaultHandle.mjs.map +0 -7
- package/dist-esm/lib/components/default-components/DefaultHandles.mjs +0 -8
- package/dist-esm/lib/components/default-components/DefaultHandles.mjs.map +0 -7
- package/dist-esm/lib/components/default-components/DefaultScribble.mjs +0 -21
- package/dist-esm/lib/components/default-components/DefaultScribble.mjs.map +0 -7
- package/dist-esm/lib/components/default-components/DefaultSelectionForeground.mjs +0 -39
- package/dist-esm/lib/components/default-components/DefaultSelectionForeground.mjs.map +0 -7
- package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs +0 -77
- package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs.map +0 -7
- package/dist-esm/lib/components/default-components/DefaultShapeIndicatorErrorFallback.mjs +0 -8
- package/dist-esm/lib/components/default-components/DefaultShapeIndicatorErrorFallback.mjs.map +0 -7
- package/dist-esm/lib/components/default-components/DefaultShapeIndicators.mjs +0 -82
- package/dist-esm/lib/components/default-components/DefaultShapeIndicators.mjs.map +0 -7
- package/dist-esm/lib/components/default-components/DefaultSnapIndictor.mjs +0 -142
- package/dist-esm/lib/components/default-components/DefaultSnapIndictor.mjs.map +0 -7
- package/dist-esm/lib/hooks/useHandleEvents.mjs +0 -70
- package/dist-esm/lib/hooks/useHandleEvents.mjs.map +0 -7
- package/dist-esm/lib/hooks/useSelectionEvents.mjs +0 -78
- package/dist-esm/lib/hooks/useSelectionEvents.mjs.map +0 -7
- package/src/lib/components/GeometryDebuggingView.tsx +0 -108
- package/src/lib/components/LiveCollaborators.tsx +0 -180
- package/src/lib/components/default-components/CanvasShapeIndicators.tsx +0 -300
- package/src/lib/components/default-components/DefaultBrush.tsx +0 -35
- package/src/lib/components/default-components/DefaultCollaboratorHint.tsx +0 -52
- package/src/lib/components/default-components/DefaultCursor.tsx +0 -59
- package/src/lib/components/default-components/DefaultHandle.tsx +0 -42
- package/src/lib/components/default-components/DefaultHandles.tsx +0 -15
- package/src/lib/components/default-components/DefaultScribble.tsx +0 -31
- package/src/lib/components/default-components/DefaultSelectionForeground.tsx +0 -50
- package/src/lib/components/default-components/DefaultShapeIndicator.tsx +0 -104
- package/src/lib/components/default-components/DefaultShapeIndicatorErrorFallback.tsx +0 -9
- package/src/lib/components/default-components/DefaultShapeIndicators.tsx +0 -118
- package/src/lib/components/default-components/DefaultSnapIndictor.tsx +0 -174
- package/src/lib/hooks/useHandleEvents.ts +0 -88
- package/src/lib/hooks/useSelectionEvents.ts +0 -97
package/src/lib/editor/Editor.ts
CHANGED
|
@@ -89,7 +89,6 @@ import {
|
|
|
89
89
|
hasOwnProperty,
|
|
90
90
|
last,
|
|
91
91
|
lerp,
|
|
92
|
-
maxBy,
|
|
93
92
|
minBy,
|
|
94
93
|
sortById,
|
|
95
94
|
sortByIndex,
|
|
@@ -152,6 +151,7 @@ import { notVisibleShapes } from './derivations/notVisibleShapes'
|
|
|
152
151
|
import { parentsToChildren } from './derivations/parentsToChildren'
|
|
153
152
|
import { deriveShapeIdsInCurrentPage } from './derivations/shapeIdsInCurrentPage'
|
|
154
153
|
import { ClickManager } from './managers/ClickManager/ClickManager'
|
|
154
|
+
import { CollaboratorsManager } from './managers/CollaboratorsManager/CollaboratorsManager'
|
|
155
155
|
import { EdgeScrollManager } from './managers/EdgeScrollManager/EdgeScrollManager'
|
|
156
156
|
import { FocusManager } from './managers/FocusManager/FocusManager'
|
|
157
157
|
import { FontManager } from './managers/FontManager/FontManager'
|
|
@@ -165,6 +165,8 @@ import { TextManager } from './managers/TextManager/TextManager'
|
|
|
165
165
|
import { ThemeManager, resolveThemes } from './managers/ThemeManager/ThemeManager'
|
|
166
166
|
import { TickManager } from './managers/TickManager/TickManager'
|
|
167
167
|
import { UserPreferencesManager } from './managers/UserPreferencesManager/UserPreferencesManager'
|
|
168
|
+
import { OverlayManager } from './overlays/OverlayManager'
|
|
169
|
+
import { TLAnyOverlayUtilConstructor } from './overlays/OverlayUtil'
|
|
168
170
|
import {
|
|
169
171
|
ShapeUtil,
|
|
170
172
|
TLEditStartInfo,
|
|
@@ -224,6 +226,11 @@ export interface TLEditorOptions {
|
|
|
224
226
|
* An array of asset utils to use in the editor. These will be used to handle asset-type-specific behavior.
|
|
225
227
|
*/
|
|
226
228
|
assetUtils?: readonly TLAnyAssetUtilConstructor[]
|
|
229
|
+
/**
|
|
230
|
+
* An array of overlay utils to use in the editor. These define canvas overlay UI elements
|
|
231
|
+
* like selection handles, rotation corners, shape handles, etc.
|
|
232
|
+
*/
|
|
233
|
+
overlayUtils?: readonly TLAnyOverlayUtilConstructor[]
|
|
227
234
|
/**
|
|
228
235
|
* An array of tools to use in the editor. These will be used to handle events and manage user interactions in the editor.
|
|
229
236
|
*/
|
|
@@ -330,6 +337,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
330
337
|
shapeUtils,
|
|
331
338
|
bindingUtils,
|
|
332
339
|
assetUtils: assetUtilConstructors,
|
|
340
|
+
overlayUtils: overlayUtilConstructors,
|
|
333
341
|
tools,
|
|
334
342
|
getContainer,
|
|
335
343
|
// needs to be here for backwards compatibility with TldrawEditor
|
|
@@ -410,6 +418,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
410
418
|
this.inputs = new InputsManager(this)
|
|
411
419
|
this.performance = new PerformanceManager(this)
|
|
412
420
|
this.disposables.add(() => this.performance.dispose())
|
|
421
|
+
this.collaborators = new CollaboratorsManager(this)
|
|
413
422
|
|
|
414
423
|
class NewRoot extends RootState {
|
|
415
424
|
static override initial = initialState ?? ''
|
|
@@ -489,6 +498,15 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
489
498
|
|
|
490
499
|
this.scribbles = new ScribbleManager(this)
|
|
491
500
|
|
|
501
|
+
// Overlay utils
|
|
502
|
+
this.overlays = new OverlayManager(this)
|
|
503
|
+
if (overlayUtilConstructors) {
|
|
504
|
+
for (const Util of overlayUtilConstructors) {
|
|
505
|
+
const util = new Util(this)
|
|
506
|
+
this.overlays.registerUtil(util)
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
492
510
|
// Cleanup
|
|
493
511
|
|
|
494
512
|
const cleanupInstancePageState = (
|
|
@@ -1033,6 +1051,13 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1033
1051
|
*/
|
|
1034
1052
|
readonly timers = tltime.forContext(this.contextId)
|
|
1035
1053
|
|
|
1054
|
+
/**
|
|
1055
|
+
* A manager for remote peer collaborators connected to this editor.
|
|
1056
|
+
*
|
|
1057
|
+
* @public
|
|
1058
|
+
*/
|
|
1059
|
+
readonly collaborators: CollaboratorsManager
|
|
1060
|
+
|
|
1036
1061
|
/**
|
|
1037
1062
|
* A manager for the user and their preferences.
|
|
1038
1063
|
*
|
|
@@ -1068,6 +1093,13 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1068
1093
|
*/
|
|
1069
1094
|
readonly scribbles: ScribbleManager
|
|
1070
1095
|
|
|
1096
|
+
/**
|
|
1097
|
+
* A manager for canvas overlay UI elements (selection handles, shape handles, etc.).
|
|
1098
|
+
*
|
|
1099
|
+
* @public
|
|
1100
|
+
*/
|
|
1101
|
+
readonly overlays: OverlayManager
|
|
1102
|
+
|
|
1071
1103
|
/**
|
|
1072
1104
|
* A manager for side effects and correct state enforcement. See {@link @tldraw/store#StoreSideEffects} for details.
|
|
1073
1105
|
*
|
|
@@ -1912,11 +1944,23 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1912
1944
|
/**
|
|
1913
1945
|
* Set the cursor.
|
|
1914
1946
|
*
|
|
1947
|
+
* No-op when the partial wouldn't change the current cursor — `setCursor`
|
|
1948
|
+
* is called from pointer-move hot paths (see `updateHoveredOverlayId`,
|
|
1949
|
+
* various tool states) and skipping redundant writes avoids needlessly
|
|
1950
|
+
* dirtying instance state.
|
|
1951
|
+
*
|
|
1915
1952
|
* @param cursor - The cursor to set.
|
|
1916
1953
|
* @public
|
|
1917
1954
|
*/
|
|
1918
1955
|
setCursor(cursor: Partial<TLCursor>) {
|
|
1919
|
-
|
|
1956
|
+
const current = this.getInstanceState().cursor
|
|
1957
|
+
if (
|
|
1958
|
+
(cursor.type === undefined || cursor.type === current.type) &&
|
|
1959
|
+
(cursor.rotation === undefined || cursor.rotation === current.rotation)
|
|
1960
|
+
) {
|
|
1961
|
+
return this
|
|
1962
|
+
}
|
|
1963
|
+
this.updateInstanceState({ cursor: { ...current, ...cursor } })
|
|
1920
1964
|
return this
|
|
1921
1965
|
}
|
|
1922
1966
|
|
|
@@ -4222,43 +4266,55 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
4222
4266
|
}
|
|
4223
4267
|
// Collaborators
|
|
4224
4268
|
|
|
4225
|
-
@computed
|
|
4226
|
-
private _getCollaboratorsQuery() {
|
|
4227
|
-
return this.store.query.records('instance_presence', () => ({
|
|
4228
|
-
userId: { neq: this.user.getId() },
|
|
4229
|
-
}))
|
|
4230
|
-
}
|
|
4231
|
-
|
|
4232
4269
|
/**
|
|
4233
4270
|
* Returns a list of presence records for all peer collaborators.
|
|
4234
4271
|
* This will return the latest presence record for each connected user.
|
|
4235
4272
|
*
|
|
4273
|
+
* Convenience wrapper for {@link CollaboratorsManager.getCollaborators}.
|
|
4274
|
+
*
|
|
4236
4275
|
* @public
|
|
4237
4276
|
*/
|
|
4238
|
-
@computed
|
|
4239
4277
|
getCollaborators() {
|
|
4240
|
-
|
|
4241
|
-
if (!allPresenceRecords.length) return EMPTY_ARRAY
|
|
4242
|
-
const userIds = [...new Set(allPresenceRecords.map((c) => c.userId))].sort()
|
|
4243
|
-
return userIds.map((id) => {
|
|
4244
|
-
const latestPresence = maxBy(
|
|
4245
|
-
allPresenceRecords.filter((c) => c.userId === id),
|
|
4246
|
-
(p) => p.lastActivityTimestamp ?? 0
|
|
4247
|
-
)
|
|
4248
|
-
return latestPresence!
|
|
4249
|
-
})
|
|
4278
|
+
return this.collaborators.getCollaborators()
|
|
4250
4279
|
}
|
|
4251
4280
|
|
|
4252
4281
|
/**
|
|
4253
4282
|
* Returns a list of presence records for all peer collaborators on the current page.
|
|
4254
4283
|
* This will return the latest presence record for each connected user.
|
|
4255
4284
|
*
|
|
4285
|
+
* Convenience wrapper for {@link CollaboratorsManager.getCollaboratorsOnCurrentPage}.
|
|
4286
|
+
*
|
|
4256
4287
|
* @public
|
|
4257
4288
|
*/
|
|
4258
|
-
@computed
|
|
4259
4289
|
getCollaboratorsOnCurrentPage() {
|
|
4260
|
-
|
|
4261
|
-
|
|
4290
|
+
return this.collaborators.getCollaboratorsOnCurrentPage()
|
|
4291
|
+
}
|
|
4292
|
+
|
|
4293
|
+
/**
|
|
4294
|
+
* Returns a list of presence records for peer collaborators who should currently be
|
|
4295
|
+
* shown in the UI. Filters {@link Editor.getCollaborators} by activity state
|
|
4296
|
+
* (active / idle / inactive) and visibility rules such as following and highlighted
|
|
4297
|
+
* users. Re-evaluates on the collaborator visibility clock, so callers don't need to
|
|
4298
|
+
* drive their own activity timer.
|
|
4299
|
+
*
|
|
4300
|
+
* Convenience wrapper for {@link CollaboratorsManager.getVisibleCollaborators}.
|
|
4301
|
+
*
|
|
4302
|
+
* @public
|
|
4303
|
+
*/
|
|
4304
|
+
getVisibleCollaborators() {
|
|
4305
|
+
return this.collaborators.getVisibleCollaborators()
|
|
4306
|
+
}
|
|
4307
|
+
|
|
4308
|
+
/**
|
|
4309
|
+
* Returns a list of presence records for peer collaborators who should currently be
|
|
4310
|
+
* shown in the UI, filtered to those on the current page.
|
|
4311
|
+
*
|
|
4312
|
+
* Convenience wrapper for {@link CollaboratorsManager.getVisibleCollaboratorsOnCurrentPage}.
|
|
4313
|
+
*
|
|
4314
|
+
* @public
|
|
4315
|
+
*/
|
|
4316
|
+
getVisibleCollaboratorsOnCurrentPage() {
|
|
4317
|
+
return this.collaborators.getVisibleCollaboratorsOnCurrentPage()
|
|
4262
4318
|
}
|
|
4263
4319
|
|
|
4264
4320
|
// Attribution
|
|
@@ -5112,7 +5168,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5112
5168
|
const zoomStepFunction = (zoom: number) => Math.pow(2, Math.ceil(Math.log2(zoom)))
|
|
5113
5169
|
const steppedScreenScale = zoomStepFunction(screenScale)
|
|
5114
5170
|
const networkEffectiveType: string | null =
|
|
5115
|
-
'connection' in navigator ? (navigator as any).connection
|
|
5171
|
+
'connection' in navigator ? ((navigator as any).connection?.effectiveType ?? null) : null
|
|
5116
5172
|
|
|
5117
5173
|
return await this.store.props.assets.resolve(asset, {
|
|
5118
5174
|
screenScale: screenScale || 1,
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { EMPTY_ARRAY, atom, computed } from '@tldraw/state'
|
|
2
|
+
import { TLInstancePresence } from '@tldraw/tlschema'
|
|
3
|
+
import { maxBy } from '@tldraw/utils'
|
|
4
|
+
import {
|
|
5
|
+
getCollaboratorStateFromElapsedTime,
|
|
6
|
+
shouldShowCollaborator,
|
|
7
|
+
} from '../../../utils/collaboratorState'
|
|
8
|
+
import type { Editor } from '../../Editor'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Tracks remote peers and exposes the collaborator-related queries used by the
|
|
12
|
+
* editor and its overlays. Encapsulates the visibility clock that periodically
|
|
13
|
+
* re-evaluates which collaborators should be visible based on activity.
|
|
14
|
+
*
|
|
15
|
+
* Accessed via {@link Editor.collaborators}.
|
|
16
|
+
*
|
|
17
|
+
* @public
|
|
18
|
+
*/
|
|
19
|
+
export class CollaboratorsManager {
|
|
20
|
+
constructor(private readonly editor: Editor) {
|
|
21
|
+
// Editor disposes `editor.timers` on its own teardown, so the interval is
|
|
22
|
+
// automatically cleared when the editor is disposed.
|
|
23
|
+
editor.timers.setInterval(() => {
|
|
24
|
+
this._visibilityClock.set(Date.now())
|
|
25
|
+
}, editor.options.collaboratorCheckIntervalMs)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Drives reactive re-evaluation of {@link CollaboratorsManager.getVisibleCollaborators}.
|
|
30
|
+
* Ticked on a fixed interval so callers don't need to manage their own activity timers.
|
|
31
|
+
*/
|
|
32
|
+
private readonly _visibilityClock = atom('collaboratorVisibilityClock', Date.now())
|
|
33
|
+
|
|
34
|
+
@computed
|
|
35
|
+
private _getCollaboratorsQuery() {
|
|
36
|
+
return this.editor.store.query.records('instance_presence', () => ({
|
|
37
|
+
userId: { neq: this.editor.user.getId() },
|
|
38
|
+
}))
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Returns a list of presence records for all peer collaborators.
|
|
43
|
+
* This will return the latest presence record for each connected user.
|
|
44
|
+
*/
|
|
45
|
+
@computed
|
|
46
|
+
getCollaborators(): TLInstancePresence[] {
|
|
47
|
+
const allPresenceRecords = this._getCollaboratorsQuery().get()
|
|
48
|
+
if (!allPresenceRecords.length) return EMPTY_ARRAY
|
|
49
|
+
const userIds = [...new Set(allPresenceRecords.map((c) => c.userId))].sort()
|
|
50
|
+
return userIds.map((id) => {
|
|
51
|
+
const latestPresence = maxBy(
|
|
52
|
+
allPresenceRecords.filter((c) => c.userId === id),
|
|
53
|
+
(p) => p.lastActivityTimestamp ?? 0
|
|
54
|
+
)
|
|
55
|
+
return latestPresence!
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Returns a list of presence records for all peer collaborators on the current page.
|
|
61
|
+
* This will return the latest presence record for each connected user.
|
|
62
|
+
*/
|
|
63
|
+
@computed
|
|
64
|
+
getCollaboratorsOnCurrentPage(): TLInstancePresence[] {
|
|
65
|
+
const currentPageId = this.editor.getCurrentPageId()
|
|
66
|
+
return this.getCollaborators().filter((c) => c.currentPageId === currentPageId)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Returns a list of presence records for peer collaborators who should currently be
|
|
71
|
+
* shown in the UI. Filters {@link CollaboratorsManager.getCollaborators} by activity
|
|
72
|
+
* state (active / idle / inactive) and visibility rules such as following and
|
|
73
|
+
* highlighted users. Re-evaluates on the visibility clock, so callers don't need to
|
|
74
|
+
* drive their own activity timer.
|
|
75
|
+
*/
|
|
76
|
+
@computed
|
|
77
|
+
getVisibleCollaborators(): TLInstancePresence[] {
|
|
78
|
+
this._visibilityClock.get()
|
|
79
|
+
const now = Date.now()
|
|
80
|
+
return this.getCollaborators().filter((presence) => {
|
|
81
|
+
// Treat a missing `lastActivityTimestamp` as "active right now" (elapsed = 0)
|
|
82
|
+
// so newly-joined peers aren't immediately classified as idle/inactive.
|
|
83
|
+
const elapsed = Math.max(0, now - (presence.lastActivityTimestamp ?? now))
|
|
84
|
+
const state = getCollaboratorStateFromElapsedTime(this.editor, elapsed)
|
|
85
|
+
return shouldShowCollaborator(this.editor, presence, state)
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Returns a list of presence records for peer collaborators who should currently be
|
|
91
|
+
* shown in the UI, filtered to those on the current page.
|
|
92
|
+
*/
|
|
93
|
+
@computed
|
|
94
|
+
getVisibleCollaboratorsOnCurrentPage(): TLInstancePresence[] {
|
|
95
|
+
const currentPageId = this.editor.getCurrentPageId()
|
|
96
|
+
return this.getVisibleCollaborators().filter((c) => c.currentPageId === currentPageId)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -136,6 +136,13 @@ export const DEFAULT_THEME: TLTheme = {
|
|
|
136
136
|
solid: '#fcfffe',
|
|
137
137
|
cursor: 'black',
|
|
138
138
|
noteBorder: 'rgb(144, 144, 144)',
|
|
139
|
+
snap: 'hsl(0, 76%, 60%)',
|
|
140
|
+
selectionStroke: 'hsl(214, 84%, 56%)',
|
|
141
|
+
selectionFill: 'hsl(210, 100%, 56%, 24%)',
|
|
142
|
+
brushFill: 'hsl(0, 0%, 56%, 10.2%)',
|
|
143
|
+
brushStroke: 'hsl(0, 0%, 56%, 25.1%)',
|
|
144
|
+
selectedContrast: '#ffffff',
|
|
145
|
+
laser: 'hsl(0, 100%, 50%)',
|
|
139
146
|
black: {
|
|
140
147
|
solid: '#1d1d1d',
|
|
141
148
|
fill: '#1d1d1d',
|
|
@@ -351,6 +358,13 @@ export const DEFAULT_THEME: TLTheme = {
|
|
|
351
358
|
negativeSpace: 'hsl(240, 5%, 6.5%)',
|
|
352
359
|
solid: '#010403',
|
|
353
360
|
cursor: 'white',
|
|
361
|
+
snap: 'hsl(0, 76%, 60%)',
|
|
362
|
+
selectionStroke: 'hsl(214, 84%, 56%)',
|
|
363
|
+
selectionFill: 'hsl(209, 100%, 57%, 20%)',
|
|
364
|
+
brushFill: 'hsl(0, 0%, 56%, 10.2%)',
|
|
365
|
+
brushStroke: 'hsl(0, 0%, 56%, 25.1%)',
|
|
366
|
+
selectedContrast: '#ffffff',
|
|
367
|
+
laser: 'hsl(0, 100%, 50%)',
|
|
354
368
|
noteBorder: 'rgb(20, 20, 20)',
|
|
355
369
|
|
|
356
370
|
black: {
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { atom, computed } from '@tldraw/state'
|
|
2
|
+
import { Geometry2d } from '../../primitives/geometry/Geometry2d'
|
|
3
|
+
import { VecLike } from '../../primitives/Vec'
|
|
4
|
+
import type { Editor } from '../Editor'
|
|
5
|
+
import { OverlayUtil, TLOverlay } from './OverlayUtil'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* An active overlay util paired with the overlays it produced for the current
|
|
9
|
+
* editor state. Returned by {@link OverlayManager.getActiveOverlayEntries} so
|
|
10
|
+
* hit-test, render, and debug paths share a single scan per reactive tick.
|
|
11
|
+
*
|
|
12
|
+
* @public
|
|
13
|
+
*/
|
|
14
|
+
export interface TLOverlayEntry {
|
|
15
|
+
util: OverlayUtil
|
|
16
|
+
overlays: TLOverlay[]
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** @public */
|
|
20
|
+
export class OverlayManager {
|
|
21
|
+
constructor(public readonly editor: Editor) {}
|
|
22
|
+
|
|
23
|
+
/** @internal */
|
|
24
|
+
readonly _overlayUtils = new Map<string, OverlayUtil>()
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Register an overlay util instance. Called during editor construction.
|
|
28
|
+
* @internal
|
|
29
|
+
*/
|
|
30
|
+
registerUtil(util: OverlayUtil) {
|
|
31
|
+
const type = (util.constructor as typeof OverlayUtil).type
|
|
32
|
+
if (!type) {
|
|
33
|
+
throw new Error(`Overlay util ${util.constructor.name} is missing a static 'type' property.`)
|
|
34
|
+
}
|
|
35
|
+
if (this._overlayUtils.has(type)) {
|
|
36
|
+
throw new Error(`Duplicate overlay util type: "${type}"`)
|
|
37
|
+
}
|
|
38
|
+
this._overlayUtils.set(type, util)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Get an overlay util by type string, overlay instance, or by passing
|
|
43
|
+
* a util class as a generic parameter for type-safe lookup.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* const util = editor.overlays.getOverlayUtil('brush')
|
|
48
|
+
* const util = editor.overlays.getOverlayUtil<BrushOverlayUtil>('brush')
|
|
49
|
+
* const util = editor.overlays.getOverlayUtil(myOverlay)
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* @public
|
|
53
|
+
*/
|
|
54
|
+
getOverlayUtil<T extends OverlayUtil>(
|
|
55
|
+
type: T extends OverlayUtil<infer O> ? O['type'] : string
|
|
56
|
+
): T
|
|
57
|
+
getOverlayUtil<O extends TLOverlay>(overlay: O): OverlayUtil<O>
|
|
58
|
+
getOverlayUtil(arg: string | TLOverlay): OverlayUtil {
|
|
59
|
+
const type = typeof arg === 'string' ? arg : arg.type
|
|
60
|
+
const util = this._overlayUtils.get(type)
|
|
61
|
+
if (!util) throw new Error(`No overlay util found for type: "${type}"`)
|
|
62
|
+
return util
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Returns all registered overlay utils in paint order (ascending zIndex).
|
|
67
|
+
* Utils with the same zIndex preserve their registration order.
|
|
68
|
+
*
|
|
69
|
+
* @public
|
|
70
|
+
*/
|
|
71
|
+
@computed getOverlayUtilsInZOrder(): OverlayUtil[] {
|
|
72
|
+
const utils = Array.from(this._overlayUtils.values())
|
|
73
|
+
// Stable sort by zIndex (registration order breaks ties).
|
|
74
|
+
return utils
|
|
75
|
+
.map((util, i) => ({ util, i, z: util.options.zIndex ?? 0 }))
|
|
76
|
+
.sort((a, b) => a.z - b.z || a.i - b.i)
|
|
77
|
+
.map((entry) => entry.util)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Reactive list of active overlay utils paired with the overlays they
|
|
82
|
+
* produced for the current editor state, in paint order (ascending
|
|
83
|
+
* zIndex). Both the hit-test and render paths read from this single
|
|
84
|
+
* cached scan instead of each re-deriving the active set. Active utils
|
|
85
|
+
* are included even when their `getOverlays()` returns an empty array,
|
|
86
|
+
* since `render()` may still draw non-interactive UI (e.g. the selection
|
|
87
|
+
* bounding box during brushing).
|
|
88
|
+
*
|
|
89
|
+
* @public
|
|
90
|
+
*/
|
|
91
|
+
@computed getActiveOverlayEntries(): TLOverlayEntry[] {
|
|
92
|
+
const entries: TLOverlayEntry[] = []
|
|
93
|
+
for (const util of this.getOverlayUtilsInZOrder()) {
|
|
94
|
+
if (!util.isActive()) continue
|
|
95
|
+
entries.push({ util, overlays: util.getOverlays() })
|
|
96
|
+
}
|
|
97
|
+
return entries
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Reactively computed list of all currently active overlays, in paint order.
|
|
102
|
+
* @public
|
|
103
|
+
*/
|
|
104
|
+
@computed getCurrentOverlays(): TLOverlay[] {
|
|
105
|
+
const all: TLOverlay[] = []
|
|
106
|
+
for (const { overlays } of this.getActiveOverlayEntries()) {
|
|
107
|
+
all.push(...overlays)
|
|
108
|
+
}
|
|
109
|
+
return all
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Hit-test geometry cache keyed by overlay identity. Entries remain valid
|
|
113
|
+
// while getActiveOverlayEntries() keeps returning the same overlay
|
|
114
|
+
// instances; when its reactive deps change, getOverlays() emits fresh
|
|
115
|
+
// objects and stale entries fall out by GC.
|
|
116
|
+
private _geometryCache = new WeakMap<TLOverlay, Geometry2d | null>()
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Get hit-test geometry for an overlay, cached by overlay identity. Lets
|
|
120
|
+
* hit-testing on a pointermove storm skip the per-overlay geometry
|
|
121
|
+
* allocation that {@link OverlayUtil.getGeometry} would otherwise do on
|
|
122
|
+
* every call.
|
|
123
|
+
*
|
|
124
|
+
* @public
|
|
125
|
+
*/
|
|
126
|
+
getOverlayGeometry(overlay: TLOverlay): Geometry2d | null {
|
|
127
|
+
const cached = this._geometryCache.get(overlay)
|
|
128
|
+
if (cached !== undefined) return cached
|
|
129
|
+
const util = this.getOverlayUtil(overlay)
|
|
130
|
+
const geometry = util.getGeometry(overlay)
|
|
131
|
+
this._geometryCache.set(overlay, geometry)
|
|
132
|
+
return geometry
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* The currently hovered overlay id.
|
|
137
|
+
* @public
|
|
138
|
+
*/
|
|
139
|
+
private _hoveredOverlayId = atom<string | null>('hoveredOverlayId', null)
|
|
140
|
+
|
|
141
|
+
getHoveredOverlayId(): string | null {
|
|
142
|
+
return this._hoveredOverlayId.get()
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
getHoveredOverlay(): TLOverlay | null {
|
|
146
|
+
const id = this._hoveredOverlayId.get()
|
|
147
|
+
if (!id) return null
|
|
148
|
+
return this.getCurrentOverlays().find((o) => o.id === id) ?? null
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
setHoveredOverlay(id: string | null) {
|
|
152
|
+
if (id === this._hoveredOverlayId.get()) return
|
|
153
|
+
this._hoveredOverlayId.set(id)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Hit test all active overlays at a given page point.
|
|
158
|
+
* Returns the topmost overlay whose geometry contains the point, or null.
|
|
159
|
+
* Utils are walked from highest zIndex to lowest so the overlay painted on
|
|
160
|
+
* top also wins the hit test. Within a util, overlays are walked in
|
|
161
|
+
* array order: the first overlay whose geometry contains the point wins,
|
|
162
|
+
* so utils should place highest-priority overlays first in `getOverlays`.
|
|
163
|
+
* Interactive overlays (those with geometry) are checked; non-interactive are skipped.
|
|
164
|
+
*
|
|
165
|
+
* @param point - Point in page coordinates
|
|
166
|
+
* @param margin - Hit test margin
|
|
167
|
+
* @public
|
|
168
|
+
*/
|
|
169
|
+
getOverlayAtPoint(point: VecLike, margin = 0): TLOverlay | null {
|
|
170
|
+
const entries = this.getActiveOverlayEntries()
|
|
171
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
172
|
+
const { overlays } = entries[i]
|
|
173
|
+
for (const overlay of overlays) {
|
|
174
|
+
const geometry = this.getOverlayGeometry(overlay)
|
|
175
|
+
if (!geometry) continue
|
|
176
|
+
if (geometry.hitTestPoint(point, geometry.isFilled ? 0 : margin, true)) {
|
|
177
|
+
return overlay
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return null
|
|
182
|
+
}
|
|
183
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { TLCursorType } from '@tldraw/tlschema'
|
|
2
|
+
import { Geometry2d } from '../../primitives/geometry/Geometry2d'
|
|
3
|
+
import type { Editor } from '../Editor'
|
|
4
|
+
import { TLPointerEventInfo } from '../types/event-types'
|
|
5
|
+
|
|
6
|
+
/** @public */
|
|
7
|
+
export interface TLOverlay<Props = Record<string, unknown>> {
|
|
8
|
+
/**
|
|
9
|
+
* Globally unique id for this overlay instance across all overlay utils.
|
|
10
|
+
* Hit-test and hover lookup key on `id` alone, so utils must namespace their
|
|
11
|
+
* ids (e.g. `'selection_fg:top_left'`, `'handle:<shapeId>:<handleId>'`) to
|
|
12
|
+
* avoid colliding with overlays from other utils.
|
|
13
|
+
*/
|
|
14
|
+
id: string
|
|
15
|
+
/** The overlay util type that owns this instance */
|
|
16
|
+
type: string
|
|
17
|
+
/** Arbitrary props for the overlay (handle id, corner name, etc.) */
|
|
18
|
+
props: Props
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** @public */
|
|
22
|
+
export interface TLOverlayUtilConstructor<U extends OverlayUtil = OverlayUtil> {
|
|
23
|
+
new (editor: Editor): U
|
|
24
|
+
type: string
|
|
25
|
+
configure<T extends TLOverlayUtilConstructor<any>>(
|
|
26
|
+
this: T,
|
|
27
|
+
options: T extends new (...args: any[]) => { options: infer Options } ? Partial<Options> : never
|
|
28
|
+
): T
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** @public */
|
|
32
|
+
export type TLAnyOverlayUtilConstructor = TLOverlayUtilConstructor<any>
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Base class for overlay utilities. Overlays are ephemeral UI elements rendered
|
|
36
|
+
* on top of the canvas (selection handles, rotation corners, shape handles, etc.).
|
|
37
|
+
*
|
|
38
|
+
* Each OverlayUtil defines a type of overlay and knows how to:
|
|
39
|
+
* - Determine when its overlays should be active (predicate)
|
|
40
|
+
* - Produce overlay instances from current editor state
|
|
41
|
+
* - Provide hit-test geometry for interactive overlays
|
|
42
|
+
* - Provide cursor style on hover
|
|
43
|
+
* - Render into a canvas 2D context
|
|
44
|
+
*
|
|
45
|
+
* @public
|
|
46
|
+
*/
|
|
47
|
+
export abstract class OverlayUtil<T extends TLOverlay = TLOverlay> {
|
|
48
|
+
constructor(public editor: Editor) {}
|
|
49
|
+
static type: string
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Options for this overlay util. Override this to provide customization options.
|
|
53
|
+
* Use {@link OverlayUtil.configure} to customize existing overlay utils.
|
|
54
|
+
*
|
|
55
|
+
* `zIndex` controls paint and hit-test order across utils — higher numbers
|
|
56
|
+
* paint on top and are hit-tested first. Ties resolve by registration order.
|
|
57
|
+
* Defaults to `0`; built-in utils use larger integers (100, 200, …) with
|
|
58
|
+
* gaps so custom utils can slot between.
|
|
59
|
+
*
|
|
60
|
+
* @public
|
|
61
|
+
*/
|
|
62
|
+
options: { zIndex?: number } = {}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Create a new overlay util class with the given options merged in.
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```ts
|
|
69
|
+
* const MyBrush = BrushOverlayUtil.configure({ fill: 'rgba(0,0,255,0.1)' })
|
|
70
|
+
* ```
|
|
71
|
+
*
|
|
72
|
+
* @public
|
|
73
|
+
*/
|
|
74
|
+
static configure<T extends TLOverlayUtilConstructor<any>>(
|
|
75
|
+
this: T,
|
|
76
|
+
options: T extends new (...args: any[]) => { options: infer Options } ? Partial<Options> : never
|
|
77
|
+
): T {
|
|
78
|
+
// @ts-expect-error -- typescript has no idea what's going on here but it's fine
|
|
79
|
+
return class extends this {
|
|
80
|
+
// @ts-expect-error
|
|
81
|
+
options = { ...this.options, ...options }
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Whether this overlay util's overlays should currently be active.
|
|
87
|
+
* Checked reactively to determine which overlays exist at any given time.
|
|
88
|
+
*/
|
|
89
|
+
abstract isActive(): boolean
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Returns the overlay instances that currently exist.
|
|
93
|
+
* Called only when `isActive()` returns true.
|
|
94
|
+
*/
|
|
95
|
+
abstract getOverlays(): T[]
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Returns hit-test geometry for an overlay instance, in page coordinates.
|
|
99
|
+
* Return null for non-interactive overlays (e.g. snap indicators, scribbles).
|
|
100
|
+
*/
|
|
101
|
+
getGeometry(_overlay: T): Geometry2d | null {
|
|
102
|
+
return null
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Returns the cursor type to show when hovering this overlay.
|
|
107
|
+
*/
|
|
108
|
+
getCursor(_overlay: T): TLCursorType | undefined {
|
|
109
|
+
return undefined
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Called when the user points down on this overlay, before the default
|
|
114
|
+
* routing runs. Acts as an interrupt: define it to take over the event.
|
|
115
|
+
*
|
|
116
|
+
* Return `false` to continue with the default behavior (e.g. the
|
|
117
|
+
* built-in rotate/resize handle transitions or shape-handle dispatch).
|
|
118
|
+
* Return `true` — or nothing at all — to skip the default. In other
|
|
119
|
+
* words, once you override this method you own the event unless you
|
|
120
|
+
* explicitly opt back in by returning `false`.
|
|
121
|
+
*/
|
|
122
|
+
onPointerDown?(overlay: T, info: TLPointerEventInfo): boolean | void
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Render all active overlays into the canvas context.
|
|
126
|
+
* The context is already transformed to page space (camera transform applied).
|
|
127
|
+
* Called reactively when overlays or editor state changes.
|
|
128
|
+
*/
|
|
129
|
+
render(_ctx: CanvasRenderingContext2D, _overlays: T[]): void {}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Optional: render all active overlays into the minimap canvas.
|
|
133
|
+
* The context is already transformed to page space (minimap camera applied),
|
|
134
|
+
* so overlays can use the same page-space coordinates as in {@link OverlayUtil.render}.
|
|
135
|
+
*
|
|
136
|
+
* `zoom` is the minimap's screen-pixels-per-page-unit, analogous to
|
|
137
|
+
* `editor.getCamera().z`; use `1 / zoom` for one-minimap-pixel line widths.
|
|
138
|
+
*
|
|
139
|
+
* Most overlays should leave this blank — only overlays that are meaningful
|
|
140
|
+
* at minimap scale (e.g. brushes, collaborator cursors) should opt in.
|
|
141
|
+
*/
|
|
142
|
+
renderMinimap(_ctx: CanvasRenderingContext2D, _overlays: T[], _zoom: number): void {}
|
|
143
|
+
}
|