tldraw 3.16.0-canary.aceca4c951a7 → 3.16.0-canary.c1bcdabc9513
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 +73 -0
- package/dist-cjs/index.js +5 -1
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/ShapeFill.js +1 -1
- package/dist-cjs/lib/shapes/shared/ShapeFill.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/freehand/svg.js.map +2 -2
- package/dist-cjs/lib/tools/EraserTool/childStates/Erasing.js +25 -1
- package/dist-cjs/lib/tools/EraserTool/childStates/Erasing.js.map +2 -2
- package/dist-cjs/lib/tools/EraserTool/childStates/Pointing.js +12 -0
- package/dist-cjs/lib/tools/EraserTool/childStates/Pointing.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js +25 -10
- package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js +2 -1
- package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js.map +2 -2
- package/dist-cjs/lib/ui/hooks/useTools.js +5 -5
- package/dist-cjs/lib/ui/hooks/useTools.js.map +2 -2
- package/dist-cjs/lib/ui/version.js +3 -3
- package/dist-cjs/lib/ui/version.js.map +1 -1
- package/dist-esm/index.d.mts +73 -0
- package/dist-esm/index.mjs +5 -1
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/ShapeFill.mjs +1 -1
- package/dist-esm/lib/shapes/shared/ShapeFill.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/freehand/svg.mjs.map +2 -2
- package/dist-esm/lib/tools/EraserTool/childStates/Erasing.mjs +26 -1
- package/dist-esm/lib/tools/EraserTool/childStates/Erasing.mjs.map +2 -2
- package/dist-esm/lib/tools/EraserTool/childStates/Pointing.mjs +13 -0
- package/dist-esm/lib/tools/EraserTool/childStates/Pointing.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs +25 -10
- package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs +2 -1
- package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs.map +2 -2
- package/dist-esm/lib/ui/hooks/useTools.mjs +5 -5
- package/dist-esm/lib/ui/hooks/useTools.mjs.map +2 -2
- package/dist-esm/lib/ui/version.mjs +3 -3
- package/dist-esm/lib/ui/version.mjs.map +1 -1
- package/package.json +11 -34
- package/src/index.ts +3 -0
- package/src/lib/shapes/arrow/ArrowShapeOptions.test.ts +2 -1
- package/src/lib/shapes/arrow/ArrowShapeTool.test.ts +4 -3
- package/src/lib/shapes/arrow/ArrowShapeUtil.test.ts +7 -6
- package/src/lib/shapes/draw/DrawShapeTool.test.ts +0 -5
- package/src/lib/shapes/line/LineShapeUtil.test.tsx +4 -3
- package/src/lib/shapes/line/__snapshots__/LineShapeUtil.test.tsx.snap +2 -2
- package/src/lib/shapes/shared/ShapeFill.tsx +1 -1
- package/src/lib/shapes/shared/freehand/svg.ts +2 -0
- package/src/lib/shapes/text/TextShapeTool.test.ts +6 -5
- package/src/lib/tools/EraserTool/childStates/Erasing.ts +34 -1
- package/src/lib/tools/EraserTool/childStates/Pointing.ts +20 -0
- package/src/lib/ui/components/primitives/TldrawUiTooltip.tsx +36 -10
- package/src/lib/ui/components/primitives/menus/TldrawUiMenuItem.tsx +3 -2
- package/src/lib/ui/hooks/useTools.tsx +7 -5
- package/src/lib/ui/version.ts +3 -3
- package/src/lib/utils/excalidraw/__snapshots__/putExcalidrawContent.test.tsx.snap +5 -5
- package/src/lib/utils/tldr/__snapshots__/buildFromV1Document.test.ts.snap +4 -4
- package/src/test/A11y.test.tsx +3 -2
- package/src/test/ClickManager.test.ts +7 -6
- package/src/test/Editor.test.tsx +20 -19
- package/src/test/EraserTool.test.ts +184 -13
- package/src/test/HandTool.test.ts +10 -9
- package/src/test/HighlightShape.test.ts +2 -1
- package/src/test/SelectTool.test.ts +3 -2
- package/src/test/TLUserPreferences.test.ts +4 -3
- package/src/test/TestEditor.ts +13 -15
- package/src/test/TldrawEditor.test.tsx +11 -10
- package/src/test/ZoomTool.test.ts +7 -6
- package/src/test/__snapshots__/drawing.test.ts.snap +2 -2
- package/src/test/__snapshots__/groups.test.tsx.snap +6 -6
- package/src/test/__snapshots__/resizing.test.ts.snap +2 -2
- package/src/test/arrows-megabus.test.tsx +5 -4
- package/src/test/bindings.test.tsx +24 -37
- package/src/test/bookmark-shapes.test.ts +1 -8
- package/src/test/commands/__snapshots__/getSvgString.test.ts.snap +23 -7
- package/src/test/commands/__snapshots__/packShapes.test.ts.snap +8 -8
- package/src/test/commands/__snapshots__/zoomToFit.test.ts.snap +2 -2
- package/src/test/commands/alignShapes.test.tsx +25 -24
- package/src/test/commands/animationSpeed.test.ts +2 -1
- package/src/test/commands/centerOnPoint.test.ts +3 -2
- package/src/test/commands/clipboard.test.ts +3 -2
- package/src/test/commands/createShapes.test.ts +2 -1
- package/src/test/commands/deleteShapes.test.ts +2 -1
- package/src/test/commands/distributeShapes.test.tsx +11 -10
- package/src/test/commands/getSvgString.test.ts +2 -1
- package/src/test/commands/packShapes.test.ts +5 -4
- package/src/test/commands/resizeShape.test.ts +2 -1
- package/src/test/commands/rotateShapes.test.ts +7 -6
- package/src/test/commands/setCamera.test.ts +4 -3
- package/src/test/commands/setCurrentPage.test.ts +3 -2
- package/src/test/commands/stackShapes.test.ts +11 -10
- package/src/test/commands/stretch.test.tsx +13 -12
- package/src/test/createDeepLink.test.tsx +2 -1
- package/src/test/cropping.test.ts +3 -2
- package/src/test/drawing.test.ts +2 -1
- package/src/test/flipShapes.test.ts +4 -3
- package/src/test/frames.test.ts +25 -24
- package/src/test/getCulledShapes.test.tsx +3 -2
- package/src/test/groups.test.tsx +1 -1
- package/src/test/handleDeepLink.test.tsx +2 -1
- package/src/test/maxShapes.test.ts +3 -2
- package/src/test/modifiers.test.ts +5 -4
- package/src/test/navigation.test.ts +12 -11
- package/src/test/panning.test.ts +2 -1
- package/src/test/perf/perf.test.ts +2 -1
- package/src/test/registerDeepLinkListener.test.tsx +10 -9
- package/src/test/resizing.test.ts +39 -38
- package/src/test/select.test.tsx +4 -3
- package/src/test/selection-omnibus.test.ts +11 -10
- package/src/test/shapeutils.test.ts +4 -3
- package/src/test/translating.test.ts +9 -8
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
TLGroupShape,
|
|
5
5
|
TLPointerEventInfo,
|
|
6
6
|
TLShapeId,
|
|
7
|
+
isAccelKey,
|
|
7
8
|
pointInPolygon,
|
|
8
9
|
} from '@tldraw/editor'
|
|
9
10
|
|
|
@@ -15,7 +16,15 @@ export class Erasing extends StateNode {
|
|
|
15
16
|
private markId = ''
|
|
16
17
|
private excludedShapeIds = new Set<TLShapeId>()
|
|
17
18
|
|
|
19
|
+
_isHoldingAccelKey = false
|
|
20
|
+
_firstErasingShapeId: TLShapeId | null = null
|
|
21
|
+
_erasingShapeIds: TLShapeId[] = []
|
|
22
|
+
|
|
18
23
|
override onEnter(info: TLPointerEventInfo) {
|
|
24
|
+
this._isHoldingAccelKey = isAccelKey(this.editor.inputs)
|
|
25
|
+
this._firstErasingShapeId = this.editor.getErasingShapeIds()[0] // the first one should be the first one we hit... is it?
|
|
26
|
+
this._erasingShapeIds = this.editor.getErasingShapeIds()
|
|
27
|
+
|
|
19
28
|
this.markId = this.editor.markHistoryStoppingPoint('erase scribble begin')
|
|
20
29
|
this.info = info
|
|
21
30
|
|
|
@@ -76,6 +85,16 @@ export class Erasing extends StateNode {
|
|
|
76
85
|
this.complete()
|
|
77
86
|
}
|
|
78
87
|
|
|
88
|
+
override onKeyUp() {
|
|
89
|
+
this._isHoldingAccelKey = isAccelKey(this.editor.inputs)
|
|
90
|
+
this.update()
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
override onKeyDown() {
|
|
94
|
+
this._isHoldingAccelKey = isAccelKey(this.editor.inputs)
|
|
95
|
+
this.update()
|
|
96
|
+
}
|
|
97
|
+
|
|
79
98
|
update() {
|
|
80
99
|
const { editor, excludedShapeIds } = this
|
|
81
100
|
const erasingShapeIds = editor.getErasingShapeIds()
|
|
@@ -87,6 +106,7 @@ export class Erasing extends StateNode {
|
|
|
87
106
|
|
|
88
107
|
this.pushPointToScribble()
|
|
89
108
|
|
|
109
|
+
// Otherwise, erasing shapes are all the shapes that were hit before plus any new shapes that are hit
|
|
90
110
|
const erasing = new Set<TLShapeId>(erasingShapeIds)
|
|
91
111
|
const minDist = this.editor.options.hitTestMargin / zoomLevel
|
|
92
112
|
|
|
@@ -121,18 +141,31 @@ export class Erasing extends StateNode {
|
|
|
121
141
|
if (geometry.hitTestLineSegment(A, B, minDist)) {
|
|
122
142
|
erasing.add(editor.getOutermostSelectableShape(shape).id)
|
|
123
143
|
}
|
|
144
|
+
|
|
145
|
+
this._erasingShapeIds = [...erasing]
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// If the user is holding the meta / ctrl key, we should only erase the first shape we hit
|
|
149
|
+
if (this._isHoldingAccelKey && this._firstErasingShapeId) {
|
|
150
|
+
const erasingShapeId = this._firstErasingShapeId
|
|
151
|
+
if (erasingShapeId && this.editor.getShape(erasingShapeId)) {
|
|
152
|
+
editor.setErasingShapes([erasingShapeId])
|
|
153
|
+
}
|
|
154
|
+
return
|
|
124
155
|
}
|
|
125
156
|
|
|
126
157
|
// Remove the hit shapes, except if they're in the list of excluded shapes
|
|
127
158
|
// (these excluded shapes will be any frames or groups the pointer was inside of
|
|
128
159
|
// when the user started erasing)
|
|
129
|
-
this.editor.setErasingShapes(
|
|
160
|
+
this.editor.setErasingShapes(this._erasingShapeIds.filter((id) => !excludedShapeIds.has(id)))
|
|
130
161
|
}
|
|
131
162
|
|
|
132
163
|
complete() {
|
|
133
164
|
const { editor } = this
|
|
134
165
|
editor.deleteShapes(editor.getCurrentPageState().erasingShapeIds)
|
|
135
166
|
this.parent.transition('idle')
|
|
167
|
+
this._erasingShapeIds = []
|
|
168
|
+
this._firstErasingShapeId = null
|
|
136
169
|
}
|
|
137
170
|
|
|
138
171
|
cancel() {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
isAccelKey,
|
|
2
3
|
StateNode,
|
|
3
4
|
TLFrameShape,
|
|
4
5
|
TLGroupShape,
|
|
@@ -9,7 +10,11 @@ import {
|
|
|
9
10
|
export class Pointing extends StateNode {
|
|
10
11
|
static override id = 'pointing'
|
|
11
12
|
|
|
13
|
+
_isHoldingAccelKey = false
|
|
14
|
+
|
|
12
15
|
override onEnter() {
|
|
16
|
+
this._isHoldingAccelKey = isAccelKey(this.editor.inputs)
|
|
17
|
+
|
|
13
18
|
const zoomLevel = this.editor.getZoomLevel()
|
|
14
19
|
const currentPageShapesSorted = this.editor.getCurrentPageRenderingShapesSorted()
|
|
15
20
|
const {
|
|
@@ -45,12 +50,25 @@ export class Pointing extends StateNode {
|
|
|
45
50
|
}
|
|
46
51
|
|
|
47
52
|
erasing.add(hitShape.id)
|
|
53
|
+
|
|
54
|
+
// If the user is holding the meta / ctrl key, stop after the first shape
|
|
55
|
+
if (this._isHoldingAccelKey) {
|
|
56
|
+
break
|
|
57
|
+
}
|
|
48
58
|
}
|
|
49
59
|
}
|
|
50
60
|
|
|
51
61
|
this.editor.setErasingShapes([...erasing])
|
|
52
62
|
}
|
|
53
63
|
|
|
64
|
+
override onKeyUp() {
|
|
65
|
+
this._isHoldingAccelKey = isAccelKey(this.editor.inputs)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
override onKeyDown() {
|
|
69
|
+
this._isHoldingAccelKey = isAccelKey(this.editor.inputs)
|
|
70
|
+
}
|
|
71
|
+
|
|
54
72
|
override onLongPress(info: TLPointerEventInfo) {
|
|
55
73
|
this.startErasing(info)
|
|
56
74
|
}
|
|
@@ -62,6 +80,8 @@ export class Pointing extends StateNode {
|
|
|
62
80
|
}
|
|
63
81
|
|
|
64
82
|
override onPointerMove(info: TLPointerEventInfo) {
|
|
83
|
+
if (this._isHoldingAccelKey) return
|
|
84
|
+
|
|
65
85
|
if (this.editor.inputs.isDragging) {
|
|
66
86
|
this.startErasing(info)
|
|
67
87
|
}
|
|
@@ -20,6 +20,8 @@ export interface TldrawUiTooltipProps {
|
|
|
20
20
|
side?: 'top' | 'right' | 'bottom' | 'left'
|
|
21
21
|
sideOffset?: number
|
|
22
22
|
disabled?: boolean
|
|
23
|
+
showOnMobile?: boolean
|
|
24
|
+
delayDuration?: number
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
// Singleton tooltip manager
|
|
@@ -30,10 +32,11 @@ class TooltipManager {
|
|
|
30
32
|
content: ReactNode
|
|
31
33
|
side: 'top' | 'right' | 'bottom' | 'left'
|
|
32
34
|
sideOffset: number
|
|
35
|
+
showOnMobile: boolean
|
|
33
36
|
targetElement: HTMLElement
|
|
37
|
+
delayDuration: number | undefined
|
|
34
38
|
} | null>('current tooltip', null)
|
|
35
39
|
private destroyTimeoutId: number | null = null
|
|
36
|
-
private subscribers: Set<() => void> = new Set()
|
|
37
40
|
|
|
38
41
|
static getInstance(): TooltipManager {
|
|
39
42
|
if (!TooltipManager.instance) {
|
|
@@ -46,8 +49,10 @@ class TooltipManager {
|
|
|
46
49
|
tooltipId: string,
|
|
47
50
|
content: string | React.ReactNode,
|
|
48
51
|
targetElement: HTMLElement,
|
|
49
|
-
side: 'top' | 'right' | 'bottom' | 'left'
|
|
50
|
-
sideOffset: number
|
|
52
|
+
side: 'top' | 'right' | 'bottom' | 'left',
|
|
53
|
+
sideOffset: number,
|
|
54
|
+
showOnMobile: boolean,
|
|
55
|
+
delayDuration: number | undefined
|
|
51
56
|
) {
|
|
52
57
|
// Clear any existing destroy timeout
|
|
53
58
|
if (this.destroyTimeoutId) {
|
|
@@ -61,7 +66,9 @@ class TooltipManager {
|
|
|
61
66
|
content,
|
|
62
67
|
side,
|
|
63
68
|
sideOffset,
|
|
69
|
+
showOnMobile,
|
|
64
70
|
targetElement,
|
|
71
|
+
delayDuration,
|
|
65
72
|
})
|
|
66
73
|
}
|
|
67
74
|
|
|
@@ -88,8 +95,10 @@ class TooltipManager {
|
|
|
88
95
|
}
|
|
89
96
|
|
|
90
97
|
getCurrentTooltipData() {
|
|
91
|
-
|
|
92
|
-
|
|
98
|
+
const currentTooltip = this.currentTooltip.get()
|
|
99
|
+
if (!currentTooltip) return null
|
|
100
|
+
if (!this.supportsHover() && !currentTooltip.showOnMobile) return null
|
|
101
|
+
return currentTooltip
|
|
93
102
|
}
|
|
94
103
|
|
|
95
104
|
private supportsHoverAtom: Atom<boolean> | null = null
|
|
@@ -168,7 +177,7 @@ function TooltipSingleton() {
|
|
|
168
177
|
showTimeoutRef.current = editor.timers.setTimeout(() => {
|
|
169
178
|
setIsOpen(true)
|
|
170
179
|
isFirstShowRef.current = false
|
|
171
|
-
}, editor.options.tooltipDelayMs)
|
|
180
|
+
}, currentTooltip.delayDuration ?? editor.options.tooltipDelayMs)
|
|
172
181
|
} else {
|
|
173
182
|
// Subsequent tooltips show immediately
|
|
174
183
|
setIsOpen(true)
|
|
@@ -207,7 +216,18 @@ function TooltipSingleton() {
|
|
|
207
216
|
|
|
208
217
|
/** @public @react */
|
|
209
218
|
export const TldrawUiTooltip = forwardRef<HTMLButtonElement, TldrawUiTooltipProps>(
|
|
210
|
-
(
|
|
219
|
+
(
|
|
220
|
+
{
|
|
221
|
+
children,
|
|
222
|
+
content,
|
|
223
|
+
side,
|
|
224
|
+
sideOffset = 5,
|
|
225
|
+
disabled = false,
|
|
226
|
+
showOnMobile = false,
|
|
227
|
+
delayDuration,
|
|
228
|
+
},
|
|
229
|
+
ref
|
|
230
|
+
) => {
|
|
211
231
|
const editor = useMaybeEditor()
|
|
212
232
|
const tooltipId = useRef<string>(uniqueId())
|
|
213
233
|
const hasProvider = useContext(TooltipSingletonContext)
|
|
@@ -233,7 +253,9 @@ export const TldrawUiTooltip = forwardRef<HTMLButtonElement, TldrawUiTooltipProp
|
|
|
233
253
|
if (!hasProvider) {
|
|
234
254
|
return (
|
|
235
255
|
<_Tooltip.Root
|
|
236
|
-
delayDuration={
|
|
256
|
+
delayDuration={
|
|
257
|
+
delayDuration ?? (editor?.options.tooltipDelayMs || DEFAULT_TOOLTIP_DELAY_MS)
|
|
258
|
+
}
|
|
237
259
|
disableHoverableContent
|
|
238
260
|
>
|
|
239
261
|
<_Tooltip.Trigger asChild ref={ref}>
|
|
@@ -264,7 +286,9 @@ export const TldrawUiTooltip = forwardRef<HTMLButtonElement, TldrawUiTooltipProp
|
|
|
264
286
|
content,
|
|
265
287
|
event.currentTarget as HTMLElement,
|
|
266
288
|
sideToUse,
|
|
267
|
-
sideOffset
|
|
289
|
+
sideOffset,
|
|
290
|
+
showOnMobile,
|
|
291
|
+
delayDuration
|
|
268
292
|
)
|
|
269
293
|
}
|
|
270
294
|
|
|
@@ -280,7 +304,9 @@ export const TldrawUiTooltip = forwardRef<HTMLButtonElement, TldrawUiTooltipProp
|
|
|
280
304
|
content,
|
|
281
305
|
event.currentTarget as HTMLElement,
|
|
282
306
|
sideToUse,
|
|
283
|
-
sideOffset
|
|
307
|
+
sideOffset,
|
|
308
|
+
showOnMobile,
|
|
309
|
+
delayDuration
|
|
284
310
|
)
|
|
285
311
|
}
|
|
286
312
|
|
|
@@ -316,8 +316,8 @@ function useDraggableEvents(
|
|
|
316
316
|
if (
|
|
317
317
|
distanceSq >
|
|
318
318
|
(editor.getInstanceState().isCoarsePointer
|
|
319
|
-
? editor.options.
|
|
320
|
-
: editor.options.
|
|
319
|
+
? editor.options.uiCoarseDragDistanceSquared
|
|
320
|
+
: editor.options.uiDragDistanceSquared)
|
|
321
321
|
) {
|
|
322
322
|
const screenSpaceStart = state.screenSpaceStart
|
|
323
323
|
state = {
|
|
@@ -350,6 +350,7 @@ function useDraggableEvents(
|
|
|
350
350
|
})
|
|
351
351
|
|
|
352
352
|
tooltipManager.hideAllTooltips()
|
|
353
|
+
editor.getContainer().focus()
|
|
353
354
|
})
|
|
354
355
|
}
|
|
355
356
|
}
|
|
@@ -155,7 +155,8 @@ export function ToolsProvider({ overrides, children }: TLUiToolsProviderProps) {
|
|
|
155
155
|
},
|
|
156
156
|
onDragStart(source: TLUiEventSource, info: TLPointerEventInfo) {
|
|
157
157
|
onDragFromToolbarToCreateShape(editor, info, {
|
|
158
|
-
createShape: (id) =>
|
|
158
|
+
createShape: (id) =>
|
|
159
|
+
editor.createShape({ id, type: 'geo', props: { w: 200, h: 200, geo } }),
|
|
159
160
|
})
|
|
160
161
|
trackEvent('drag-tool', { source, id: 'geo' })
|
|
161
162
|
},
|
|
@@ -199,8 +200,8 @@ export function ToolsProvider({ overrides, children }: TLUiToolsProviderProps) {
|
|
|
199
200
|
type: 'line',
|
|
200
201
|
props: {
|
|
201
202
|
points: {
|
|
202
|
-
[start]: { id: start, index: start, x: 0, y:
|
|
203
|
-
[end]: { id: end, index: end, x:
|
|
203
|
+
[start]: { id: start, index: start, x: 0, y: 200 },
|
|
204
|
+
[end]: { id: end, index: end, x: 200, y: 0 },
|
|
204
205
|
},
|
|
205
206
|
},
|
|
206
207
|
})
|
|
@@ -239,8 +240,8 @@ export function ToolsProvider({ overrides, children }: TLUiToolsProviderProps) {
|
|
|
239
240
|
createShape: (id) =>
|
|
240
241
|
editor.createShape({ id, type: 'text', props: { richText: toRichText('Text') } }),
|
|
241
242
|
onDragEnd: (id) => {
|
|
242
|
-
editor.emit('select-all-text', { shapeId: id })
|
|
243
243
|
editor.setEditingShape(id)
|
|
244
|
+
editor.emit('select-all-text', { shapeId: id })
|
|
244
245
|
},
|
|
245
246
|
})
|
|
246
247
|
trackEvent('drag-tool', { source, id: 'text' })
|
|
@@ -269,8 +270,8 @@ export function ToolsProvider({ overrides, children }: TLUiToolsProviderProps) {
|
|
|
269
270
|
onDragFromToolbarToCreateShape(editor, info, {
|
|
270
271
|
createShape: (id) => editor.createShape({ id, type: 'note' }),
|
|
271
272
|
onDragEnd: (id) => {
|
|
272
|
-
editor.emit('select-all-text', { shapeId: id })
|
|
273
273
|
editor.setEditingShape(id)
|
|
274
|
+
editor.emit('select-all-text', { shapeId: id })
|
|
274
275
|
},
|
|
275
276
|
})
|
|
276
277
|
trackEvent('drag-tool', { source, id: 'note' })
|
|
@@ -385,5 +386,6 @@ export function onDragFromToolbarToCreateShape(
|
|
|
385
386
|
opts.onDragEnd?.(id)
|
|
386
387
|
},
|
|
387
388
|
})
|
|
389
|
+
|
|
388
390
|
editor.getCurrentTool().setCurrentToolIdMask(shape.type)
|
|
389
391
|
}
|
package/src/lib/ui/version.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// This file is automatically generated by internal/scripts/refresh-assets.ts.
|
|
2
2
|
// Do not edit manually. Or do, I'm a comment, not a cop.
|
|
3
3
|
|
|
4
|
-
export const version = '3.16.0-canary.
|
|
4
|
+
export const version = '3.16.0-canary.c1bcdabc9513'
|
|
5
5
|
export const publishDates = {
|
|
6
6
|
major: '2024-09-13T14:36:29.063Z',
|
|
7
|
-
minor: '2025-08-
|
|
8
|
-
patch: '2025-08-
|
|
7
|
+
minor: '2025-08-17T08:02:22.032Z',
|
|
8
|
+
patch: '2025-08-17T08:02:22.032Z',
|
|
9
9
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
2
|
|
|
3
|
-
exports[`putExcalidrawContent test fixtures bound-arrows.json 1`] = `
|
|
3
|
+
exports[`putExcalidrawContent test fixtures > bound-arrows.json 1`] = `
|
|
4
4
|
{
|
|
5
5
|
"binding:8": {
|
|
6
6
|
"fromId": "shape:7",
|
|
@@ -173,7 +173,7 @@ exports[`putExcalidrawContent test fixtures bound-arrows.json 1`] = `
|
|
|
173
173
|
}
|
|
174
174
|
`;
|
|
175
175
|
|
|
176
|
-
exports[`putExcalidrawContent test fixtures bound-elbow-arrows.json 1`] = `
|
|
176
|
+
exports[`putExcalidrawContent test fixtures > bound-elbow-arrows.json 1`] = `
|
|
177
177
|
{
|
|
178
178
|
"binding:7": {
|
|
179
179
|
"fromId": "shape:6",
|
|
@@ -346,7 +346,7 @@ exports[`putExcalidrawContent test fixtures bound-elbow-arrows.json 1`] = `
|
|
|
346
346
|
}
|
|
347
347
|
`;
|
|
348
348
|
|
|
349
|
-
exports[`putExcalidrawContent test fixtures image.json 1`] = `
|
|
349
|
+
exports[`putExcalidrawContent test fixtures > image.json 1`] = `
|
|
350
350
|
{
|
|
351
351
|
"asset:5": {
|
|
352
352
|
"id": "asset:5",
|
|
@@ -404,7 +404,7 @@ exports[`putExcalidrawContent test fixtures image.json 1`] = `
|
|
|
404
404
|
}
|
|
405
405
|
`;
|
|
406
406
|
|
|
407
|
-
exports[`putExcalidrawContent test fixtures line-drawing.json 1`] = `
|
|
407
|
+
exports[`putExcalidrawContent test fixtures > line-drawing.json 1`] = `
|
|
408
408
|
{
|
|
409
409
|
"document:document": {
|
|
410
410
|
"gridSize": 10,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
2
|
|
|
3
|
-
exports[`buildFromV1Document test fixtures arrow-binding.tldr 1`] = `
|
|
3
|
+
exports[`buildFromV1Document test fixtures > arrow-binding.tldr 1`] = `
|
|
4
4
|
{
|
|
5
5
|
"binding:12": {
|
|
6
6
|
"fromId": "shape:11",
|
|
@@ -173,7 +173,7 @@ exports[`buildFromV1Document test fixtures arrow-binding.tldr 1`] = `
|
|
|
173
173
|
}
|
|
174
174
|
`;
|
|
175
175
|
|
|
176
|
-
exports[`buildFromV1Document test fixtures exact-arrow-binding.tldr 1`] = `
|
|
176
|
+
exports[`buildFromV1Document test fixtures > exact-arrow-binding.tldr 1`] = `
|
|
177
177
|
{
|
|
178
178
|
"binding:11": {
|
|
179
179
|
"fromId": "shape:10",
|
|
@@ -346,7 +346,7 @@ exports[`buildFromV1Document test fixtures exact-arrow-binding.tldr 1`] = `
|
|
|
346
346
|
}
|
|
347
347
|
`;
|
|
348
348
|
|
|
349
|
-
exports[`buildFromV1Document test fixtures incorrect-arrow-binding.tldr 1`] = `
|
|
349
|
+
exports[`buildFromV1Document test fixtures > incorrect-arrow-binding.tldr 1`] = `
|
|
350
350
|
{
|
|
351
351
|
"binding:11": {
|
|
352
352
|
"fromId": "shape:10",
|
package/src/test/A11y.test.tsx
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { createShapeId, toRichText } from '@tldraw/editor'
|
|
2
|
+
import { Mock, vi } from 'vitest'
|
|
2
3
|
import { generateShapeAnnouncementMessage } from '../lib/ui/components/A11y'
|
|
3
4
|
import { TestEditor } from './TestEditor'
|
|
4
5
|
|
|
5
6
|
describe('A11y Shape Announcements', () => {
|
|
6
7
|
let editor: TestEditor
|
|
7
|
-
let mockTranslate:
|
|
8
|
+
let mockTranslate: Mock
|
|
8
9
|
|
|
9
10
|
beforeEach(() => {
|
|
10
11
|
editor = new TestEditor()
|
|
11
12
|
|
|
12
13
|
// Create a simple translation mock
|
|
13
|
-
mockTranslate =
|
|
14
|
+
mockTranslate = vi.fn((key) => {
|
|
14
15
|
if (key === 'a11y.multiple-shapes') return '{num} shapes selected'
|
|
15
16
|
if (key === 'a11y.shape') return 'Shape'
|
|
16
17
|
if (key === 'a11y.text') return 'Text'
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { vi } from 'vitest'
|
|
1
2
|
import { TestEditor } from './TestEditor'
|
|
2
3
|
|
|
3
4
|
let editor: TestEditor
|
|
@@ -10,7 +11,7 @@ beforeEach(() => {
|
|
|
10
11
|
editor._transformPointerUpSpy.mockRestore()
|
|
11
12
|
})
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
vi.useFakeTimers()
|
|
14
15
|
|
|
15
16
|
describe('Handles events', () => {
|
|
16
17
|
it('Emits single click events', () => {
|
|
@@ -23,7 +24,7 @@ describe('Handles events', () => {
|
|
|
23
24
|
const eventsBeforeSettle = [{ name: 'pointer_down' }, { name: 'pointer_up' }]
|
|
24
25
|
|
|
25
26
|
// allow time for settle
|
|
26
|
-
|
|
27
|
+
vi.advanceTimersByTime(500)
|
|
27
28
|
|
|
28
29
|
// nothing should have settled
|
|
29
30
|
expect(events).toMatchObject(eventsBeforeSettle)
|
|
@@ -64,7 +65,7 @@ describe('Handles events', () => {
|
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
// allow double click to settle
|
|
67
|
-
|
|
68
|
+
vi.advanceTimersByTime(500)
|
|
68
69
|
|
|
69
70
|
expect(events).toMatchObject([
|
|
70
71
|
...eventsBeforeSettle,
|
|
@@ -110,7 +111,7 @@ describe('Handles events', () => {
|
|
|
110
111
|
expect(eventsBeforeSettle).toMatchObject(eventsBeforeSettle)
|
|
111
112
|
|
|
112
113
|
// allow double click to settle
|
|
113
|
-
|
|
114
|
+
vi.advanceTimersByTime(500)
|
|
114
115
|
|
|
115
116
|
expect(events).toMatchObject([
|
|
116
117
|
...eventsBeforeSettle,
|
|
@@ -162,7 +163,7 @@ describe('Handles events', () => {
|
|
|
162
163
|
expect(events).toMatchObject(eventsBeforeSettle)
|
|
163
164
|
|
|
164
165
|
// allow double click to settle
|
|
165
|
-
|
|
166
|
+
vi.advanceTimersByTime(500)
|
|
166
167
|
|
|
167
168
|
expect(events).toMatchObject([
|
|
168
169
|
...eventsBeforeSettle,
|
|
@@ -218,7 +219,7 @@ describe('Handles events', () => {
|
|
|
218
219
|
expect(events).toMatchObject(eventsBeforeSettle)
|
|
219
220
|
|
|
220
221
|
// allow double click to settle
|
|
221
|
-
|
|
222
|
+
vi.advanceTimersByTime(500)
|
|
222
223
|
|
|
223
224
|
expect(events).toMatchObject(eventsBeforeSettle)
|
|
224
225
|
|
package/src/test/Editor.test.tsx
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
loadSnapshot,
|
|
15
15
|
react,
|
|
16
16
|
} from '@tldraw/editor'
|
|
17
|
+
import { vi } from 'vitest'
|
|
17
18
|
import { TestEditor } from './TestEditor'
|
|
18
19
|
import { TL } from './test-jsx'
|
|
19
20
|
|
|
@@ -415,24 +416,24 @@ describe('isFocused', () => {
|
|
|
415
416
|
})
|
|
416
417
|
|
|
417
418
|
it('becomes true when the container div receives a focus event', () => {
|
|
418
|
-
|
|
419
|
+
vi.advanceTimersByTime(100)
|
|
419
420
|
expect(editor.getInstanceState().isFocused).toBe(false)
|
|
420
421
|
|
|
421
422
|
editor.elm.focus()
|
|
422
423
|
|
|
423
|
-
|
|
424
|
+
vi.advanceTimersByTime(100)
|
|
424
425
|
expect(editor.getInstanceState().isFocused).toBe(true)
|
|
425
426
|
})
|
|
426
427
|
|
|
427
428
|
it('becomes false when the container div receives a blur event', () => {
|
|
428
429
|
editor.elm.focus()
|
|
429
430
|
|
|
430
|
-
|
|
431
|
+
vi.advanceTimersByTime(100)
|
|
431
432
|
expect(editor.getInstanceState().isFocused).toBe(true)
|
|
432
433
|
|
|
433
434
|
editor.elm.blur()
|
|
434
435
|
|
|
435
|
-
|
|
436
|
+
vi.advanceTimersByTime(100)
|
|
436
437
|
expect(editor.getInstanceState().isFocused).toBe(false)
|
|
437
438
|
})
|
|
438
439
|
|
|
@@ -444,13 +445,13 @@ describe('isFocused', () => {
|
|
|
444
445
|
editor.elm.blur()
|
|
445
446
|
const child = document.createElement('div')
|
|
446
447
|
editor.elm.appendChild(child)
|
|
447
|
-
|
|
448
|
+
vi.advanceTimersByTime(100)
|
|
448
449
|
expect(editor.getInstanceState().isFocused).toBe(false)
|
|
449
450
|
child.dispatchEvent(new FocusEvent('focusin', { bubbles: true }))
|
|
450
|
-
|
|
451
|
+
vi.advanceTimersByTime(100)
|
|
451
452
|
expect(editor.getInstanceState().isFocused).toBe(true)
|
|
452
453
|
child.dispatchEvent(new FocusEvent('focusout', { bubbles: true }))
|
|
453
|
-
|
|
454
|
+
vi.advanceTimersByTime(100)
|
|
454
455
|
expect(editor.getInstanceState().isFocused).toBe(false)
|
|
455
456
|
})
|
|
456
457
|
|
|
@@ -466,7 +467,7 @@ describe('isFocused', () => {
|
|
|
466
467
|
|
|
467
468
|
child.dispatchEvent(new FocusEvent('focusout', { bubbles: true }))
|
|
468
469
|
|
|
469
|
-
|
|
470
|
+
vi.advanceTimersByTime(100)
|
|
470
471
|
expect(editor.getInstanceState().isFocused).toBe(false)
|
|
471
472
|
})
|
|
472
473
|
})
|
|
@@ -520,13 +521,13 @@ describe('getShapeUtil', () => {
|
|
|
520
521
|
it('throws if that shape type isnt registered', () => {
|
|
521
522
|
const myMissingShape = { type: 'missing' } as TLShape
|
|
522
523
|
expect(() => editor.getShapeUtil(myMissingShape)).toThrowErrorMatchingInlineSnapshot(
|
|
523
|
-
`
|
|
524
|
+
`[Error: No shape util found for type "missing"]`
|
|
524
525
|
)
|
|
525
526
|
})
|
|
526
527
|
|
|
527
528
|
it('throws if that type isnt registered', () => {
|
|
528
529
|
expect(() => editor.getShapeUtil('missing')).toThrowErrorMatchingInlineSnapshot(
|
|
529
|
-
`
|
|
530
|
+
`[Error: No shape util found for type "missing"]`
|
|
530
531
|
)
|
|
531
532
|
})
|
|
532
533
|
})
|
|
@@ -603,14 +604,14 @@ describe('snapshots', () => {
|
|
|
603
604
|
|
|
604
605
|
describe('when the user prefers dark UI', () => {
|
|
605
606
|
beforeEach(() => {
|
|
606
|
-
window.matchMedia =
|
|
607
|
+
window.matchMedia = vi.fn().mockImplementation((query) => {
|
|
607
608
|
return {
|
|
608
609
|
matches: query === '(prefers-color-scheme: dark)',
|
|
609
610
|
media: query,
|
|
610
611
|
onchange: null,
|
|
611
|
-
addEventListener:
|
|
612
|
-
removeEventListener:
|
|
613
|
-
dispatchEvent:
|
|
612
|
+
addEventListener: vi.fn(),
|
|
613
|
+
removeEventListener: vi.fn(),
|
|
614
|
+
dispatchEvent: vi.fn(),
|
|
614
615
|
}
|
|
615
616
|
})
|
|
616
617
|
})
|
|
@@ -630,14 +631,14 @@ describe('when the user prefers dark UI', () => {
|
|
|
630
631
|
|
|
631
632
|
describe('when the user prefers light UI', () => {
|
|
632
633
|
beforeEach(() => {
|
|
633
|
-
window.matchMedia =
|
|
634
|
+
window.matchMedia = vi.fn().mockImplementation((query) => {
|
|
634
635
|
return {
|
|
635
636
|
matches: false,
|
|
636
637
|
media: query,
|
|
637
638
|
onchange: null,
|
|
638
|
-
addEventListener:
|
|
639
|
-
removeEventListener:
|
|
640
|
-
dispatchEvent:
|
|
639
|
+
addEventListener: vi.fn(),
|
|
640
|
+
removeEventListener: vi.fn(),
|
|
641
|
+
dispatchEvent: vi.fn(),
|
|
641
642
|
}
|
|
642
643
|
})
|
|
643
644
|
})
|
|
@@ -722,7 +723,7 @@ describe('dragging', () => {
|
|
|
722
723
|
})
|
|
723
724
|
|
|
724
725
|
describe('getShapeVisibility', () => {
|
|
725
|
-
const getShapeVisibility =
|
|
726
|
+
const getShapeVisibility = vi.fn(((shape: TLShape) => {
|
|
726
727
|
return shape.meta.visibility as any
|
|
727
728
|
}) satisfies TldrawEditorProps['getShapeVisibility'])
|
|
728
729
|
|