@tldraw/editor 3.14.0-canary.e95fdc82a46a → 3.14.0-canary.e989a4107be2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-cjs/index.d.ts +155 -55
- package/dist-cjs/index.js +4 -3
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/config/TLSessionStateSnapshot.js +1 -12
- package/dist-cjs/lib/config/TLSessionStateSnapshot.js.map +3 -3
- package/dist-cjs/lib/editor/Editor.js +90 -30
- package/dist-cjs/lib/editor/Editor.js.map +2 -2
- package/dist-cjs/lib/editor/bindings/BindingUtil.js.map +2 -2
- package/dist-cjs/lib/editor/managers/FontManager/FontManager.js +1 -2
- package/dist-cjs/lib/editor/managers/FontManager/FontManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/HistoryManager/HistoryManager.js +3 -1
- package/dist-cjs/lib/editor/managers/HistoryManager/HistoryManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/TextManager/TextManager.js +73 -42
- package/dist-cjs/lib/editor/managers/TextManager/TextManager.js.map +2 -2
- package/dist-cjs/lib/editor/shapes/ShapeUtil.js +0 -10
- package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
- package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js +1 -1
- package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js.map +1 -1
- package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js +10 -6
- package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js.map +3 -3
- package/dist-cjs/lib/editor/tools/StateNode.js +3 -3
- package/dist-cjs/lib/editor/tools/StateNode.js.map +2 -2
- package/dist-cjs/lib/editor/types/emit-types.js.map +1 -1
- package/dist-cjs/lib/editor/types/external-content.js.map +1 -1
- package/dist-cjs/lib/hooks/useCanvasEvents.js +1 -2
- package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
- package/dist-cjs/lib/primitives/Box.js +0 -6
- package/dist-cjs/lib/primitives/Box.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Geometry2d.js +6 -2
- package/dist-cjs/lib/primitives/geometry/Geometry2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Group2d.js +11 -6
- package/dist-cjs/lib/primitives/geometry/Group2d.js.map +2 -2
- package/dist-cjs/lib/utils/areShapesContentEqual.js +1 -1
- package/dist-cjs/lib/utils/areShapesContentEqual.js.map +2 -2
- package/dist-cjs/lib/utils/dom.js +1 -1
- package/dist-cjs/lib/utils/dom.js.map +2 -2
- package/dist-cjs/lib/utils/reparenting.js +232 -0
- package/dist-cjs/lib/utils/reparenting.js.map +7 -0
- package/dist-cjs/lib/utils/richText.js +7 -2
- package/dist-cjs/lib/utils/richText.js.map +2 -2
- package/dist-cjs/version.js +3 -3
- package/dist-cjs/version.js.map +1 -1
- package/dist-esm/index.d.mts +155 -55
- package/dist-esm/index.mjs +4 -3
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/config/TLSessionStateSnapshot.mjs +1 -1
- package/dist-esm/lib/config/TLSessionStateSnapshot.mjs.map +2 -2
- package/dist-esm/lib/editor/Editor.mjs +90 -30
- package/dist-esm/lib/editor/Editor.mjs.map +2 -2
- package/dist-esm/lib/editor/bindings/BindingUtil.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs +1 -2
- package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/HistoryManager/HistoryManager.mjs +3 -1
- package/dist-esm/lib/editor/managers/HistoryManager/HistoryManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs +73 -42
- package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs.map +2 -2
- package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +0 -10
- package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs +1 -1
- package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs.map +1 -1
- package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs +10 -6
- package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs.map +3 -3
- package/dist-esm/lib/editor/tools/StateNode.mjs +3 -3
- package/dist-esm/lib/editor/tools/StateNode.mjs.map +2 -2
- package/dist-esm/lib/hooks/useCanvasEvents.mjs +1 -2
- package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
- package/dist-esm/lib/primitives/Box.mjs +0 -6
- package/dist-esm/lib/primitives/Box.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Geometry2d.mjs +6 -2
- package/dist-esm/lib/primitives/geometry/Geometry2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Group2d.mjs +11 -6
- package/dist-esm/lib/primitives/geometry/Group2d.mjs.map +2 -2
- package/dist-esm/lib/utils/areShapesContentEqual.mjs +1 -1
- package/dist-esm/lib/utils/areShapesContentEqual.mjs.map +2 -2
- package/dist-esm/lib/utils/dom.mjs +1 -1
- package/dist-esm/lib/utils/dom.mjs.map +2 -2
- package/dist-esm/lib/utils/reparenting.mjs +216 -0
- package/dist-esm/lib/utils/reparenting.mjs.map +7 -0
- package/dist-esm/lib/utils/richText.mjs +8 -3
- package/dist-esm/lib/utils/richText.mjs.map +2 -2
- package/dist-esm/version.mjs +3 -3
- package/dist-esm/version.mjs.map +1 -1
- package/editor.css +446 -489
- package/package.json +8 -9
- package/src/index.ts +7 -1
- package/src/lib/config/TLSessionStateSnapshot.ts +1 -1
- package/src/lib/editor/Editor.ts +107 -38
- package/src/lib/editor/bindings/BindingUtil.ts +6 -0
- package/src/lib/editor/managers/FontManager/FontManager.ts +1 -2
- package/src/lib/editor/managers/HistoryManager/HistoryManager.ts +3 -1
- package/src/lib/editor/managers/TextManager/TextManager.test.ts +1 -5
- package/src/lib/editor/managers/TextManager/TextManager.ts +118 -86
- package/src/lib/editor/shapes/ShapeUtil.ts +47 -15
- package/src/lib/editor/shapes/group/GroupShapeUtil.tsx +1 -1
- package/src/lib/editor/tools/BaseBoxShapeTool/children/Pointing.ts +22 -17
- package/src/lib/editor/tools/StateNode.ts +3 -3
- package/src/lib/editor/types/emit-types.ts +4 -0
- package/src/lib/editor/types/external-content.ts +11 -2
- package/src/lib/hooks/useCanvasEvents.ts +0 -1
- package/src/lib/primitives/Box.ts +0 -8
- package/src/lib/primitives/geometry/Geometry2d.ts +7 -2
- package/src/lib/primitives/geometry/Group2d.ts +11 -5
- package/src/lib/utils/areShapesContentEqual.ts +1 -2
- package/src/lib/utils/dom.ts +1 -1
- package/src/lib/utils/reparenting.ts +383 -0
- package/src/lib/utils/richText.ts +9 -3
- package/src/version.ts +3 -3
|
@@ -20,6 +20,28 @@ const textAlignmentsForLtr = {
|
|
|
20
20
|
'end-legacy': 'right',
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
/** @public */
|
|
24
|
+
export interface TLMeasureTextOpts {
|
|
25
|
+
fontStyle: string
|
|
26
|
+
fontWeight: string
|
|
27
|
+
fontFamily: string
|
|
28
|
+
fontSize: number
|
|
29
|
+
/** This must be a number, e.g. 1.35, not a pixel value. */
|
|
30
|
+
lineHeight: number
|
|
31
|
+
/**
|
|
32
|
+
* When maxWidth is a number, the text will be wrapped to that maxWidth. When maxWidth
|
|
33
|
+
* is null, the text will be measured without wrapping, but explicit line breaks and
|
|
34
|
+
* space are preserved.
|
|
35
|
+
*/
|
|
36
|
+
maxWidth: null | number
|
|
37
|
+
minWidth?: null | number
|
|
38
|
+
// todo: make this a number so that it is consistent with other TLMeasureTextSpanOpts
|
|
39
|
+
padding: string
|
|
40
|
+
otherStyles?: Record<string, string>
|
|
41
|
+
disableOverflowWrapBreaking?: boolean
|
|
42
|
+
measureScrollWidth?: boolean
|
|
43
|
+
}
|
|
44
|
+
|
|
23
45
|
/** @public */
|
|
24
46
|
export interface TLMeasureTextSpanOpts {
|
|
25
47
|
overflow: 'wrap' | 'truncate-ellipsis' | 'truncate-clip'
|
|
@@ -33,96 +55,99 @@ export interface TLMeasureTextSpanOpts {
|
|
|
33
55
|
lineHeight: number
|
|
34
56
|
textAlign: TLDefaultHorizontalAlignStyle
|
|
35
57
|
otherStyles?: Record<string, string>
|
|
58
|
+
measureScrollWidth?: boolean
|
|
36
59
|
}
|
|
37
60
|
|
|
38
61
|
const spaceCharacterRegex = /\s/
|
|
39
62
|
|
|
40
63
|
/** @public */
|
|
41
64
|
export class TextManager {
|
|
42
|
-
private
|
|
65
|
+
private elm: HTMLDivElement
|
|
66
|
+
private defaultStyles: Record<string, string | null>
|
|
43
67
|
|
|
44
68
|
constructor(public editor: Editor) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
69
|
+
const elm = document.createElement('div')
|
|
70
|
+
elm.classList.add('tl-text')
|
|
71
|
+
elm.classList.add('tl-text-measure')
|
|
72
|
+
elm.setAttribute('dir', 'auto')
|
|
73
|
+
elm.tabIndex = -1
|
|
74
|
+
this.editor.getContainer().appendChild(elm)
|
|
75
|
+
|
|
76
|
+
// we need to save the default styles so that we can restore them when we're done
|
|
77
|
+
// these must be the css names, not the js names for the styles
|
|
78
|
+
this.defaultStyles = {
|
|
79
|
+
'overflow-wrap': 'break-word',
|
|
80
|
+
'word-break': 'auto',
|
|
81
|
+
width: null,
|
|
82
|
+
height: null,
|
|
83
|
+
'max-width': null,
|
|
84
|
+
'min-width': null,
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
this.elm = elm
|
|
49
88
|
}
|
|
50
89
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* When maxWidth is a number, the text will be wrapped to that maxWidth. When maxWidth
|
|
61
|
-
* is null, the text will be measured without wrapping, but explicit line breaks and
|
|
62
|
-
* space are preserved.
|
|
63
|
-
*/
|
|
64
|
-
maxWidth: null | number
|
|
65
|
-
minWidth?: null | number
|
|
66
|
-
padding: string
|
|
67
|
-
disableOverflowWrapBreaking?: boolean
|
|
90
|
+
dispose() {
|
|
91
|
+
return this.elm.remove()
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private resetElmStyles() {
|
|
95
|
+
const { elm, defaultStyles } = this
|
|
96
|
+
for (const key in defaultStyles) {
|
|
97
|
+
elm.style.setProperty(key, defaultStyles[key])
|
|
68
98
|
}
|
|
69
|
-
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
measureText(textToMeasure: string, opts: TLMeasureTextOpts): BoxModel & { scrollWidth: number } {
|
|
70
102
|
const div = document.createElement('div')
|
|
71
103
|
div.textContent = normalizeTextForDom(textToMeasure)
|
|
72
104
|
return this.measureHtml(div.innerHTML, opts)
|
|
73
105
|
}
|
|
74
106
|
|
|
75
|
-
measureHtml(
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
* is null, the text will be measured without wrapping, but explicit line breaks and
|
|
86
|
-
* space are preserved.
|
|
87
|
-
*/
|
|
88
|
-
maxWidth: null | number
|
|
89
|
-
minWidth?: null | number
|
|
90
|
-
otherStyles?: Record<string, string>
|
|
91
|
-
padding: string
|
|
92
|
-
disableOverflowWrapBreaking?: boolean
|
|
107
|
+
measureHtml(html: string, opts: TLMeasureTextOpts): BoxModel & { scrollWidth: number } {
|
|
108
|
+
const { elm } = this
|
|
109
|
+
|
|
110
|
+
if (opts.otherStyles) {
|
|
111
|
+
for (const key in opts.otherStyles) {
|
|
112
|
+
if (!this.defaultStyles[key]) {
|
|
113
|
+
// we need to save the original style so that we can restore it when we're done
|
|
114
|
+
this.defaultStyles[key] = elm.style.getPropertyValue(key)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
93
117
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
118
|
+
|
|
119
|
+
elm.innerHTML = html
|
|
120
|
+
|
|
121
|
+
// Apply the default styles to the element (for all styles here or that were ever seen in opts.otherStyles)
|
|
122
|
+
this.resetElmStyles()
|
|
123
|
+
|
|
124
|
+
elm.style.setProperty('font-family', opts.fontFamily)
|
|
125
|
+
elm.style.setProperty('font-style', opts.fontStyle)
|
|
126
|
+
elm.style.setProperty('font-weight', opts.fontWeight)
|
|
127
|
+
elm.style.setProperty('font-size', opts.fontSize + 'px')
|
|
128
|
+
elm.style.setProperty('line-height', opts.lineHeight.toString())
|
|
129
|
+
elm.style.setProperty('padding', opts.padding)
|
|
130
|
+
|
|
131
|
+
if (opts.maxWidth) {
|
|
132
|
+
elm.style.setProperty('max-width', opts.maxWidth + 'px')
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (opts.minWidth) {
|
|
136
|
+
elm.style.setProperty('min-width', opts.minWidth + 'px')
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (opts.disableOverflowWrapBreaking) {
|
|
140
|
+
elm.style.setProperty('overflow-wrap', 'normal')
|
|
141
|
+
}
|
|
142
|
+
|
|
117
143
|
if (opts.otherStyles) {
|
|
118
144
|
for (const [key, value] of Object.entries(opts.otherStyles)) {
|
|
119
|
-
|
|
145
|
+
elm.style.setProperty(key, value)
|
|
120
146
|
}
|
|
121
147
|
}
|
|
122
148
|
|
|
123
|
-
const scrollWidth =
|
|
124
|
-
const rect =
|
|
125
|
-
wrapperElm.remove()
|
|
149
|
+
const scrollWidth = opts.measureScrollWidth ? elm.scrollWidth : 0
|
|
150
|
+
const rect = elm.getBoundingClientRect()
|
|
126
151
|
|
|
127
152
|
return {
|
|
128
153
|
x: 0,
|
|
@@ -247,27 +272,29 @@ export class TextManager {
|
|
|
247
272
|
): { text: string; box: BoxModel }[] {
|
|
248
273
|
if (textToMeasure === '') return []
|
|
249
274
|
|
|
250
|
-
const elm = this
|
|
251
|
-
|
|
275
|
+
const { elm } = this
|
|
276
|
+
|
|
277
|
+
if (opts.otherStyles) {
|
|
278
|
+
for (const key in opts.otherStyles) {
|
|
279
|
+
if (!this.defaultStyles[key]) {
|
|
280
|
+
// we need to save the original style so that we can restore it when we're done
|
|
281
|
+
this.defaultStyles[key] = elm.style.getPropertyValue(key)
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
this.resetElmStyles()
|
|
287
|
+
|
|
288
|
+
elm.style.setProperty('font-family', opts.fontFamily)
|
|
289
|
+
elm.style.setProperty('font-style', opts.fontStyle)
|
|
290
|
+
elm.style.setProperty('font-weight', opts.fontWeight)
|
|
291
|
+
elm.style.setProperty('font-size', opts.fontSize + 'px')
|
|
292
|
+
elm.style.setProperty('line-height', opts.lineHeight.toString())
|
|
252
293
|
|
|
253
294
|
const elementWidth = Math.ceil(opts.width - opts.padding * 2)
|
|
254
|
-
elm.setAttribute('dir', 'auto')
|
|
255
|
-
// N.B. This property, while discouraged ("intended for Document Type Definition (DTD) designers")
|
|
256
|
-
// is necessary for ensuring correct mixed RTL/LTR behavior when exporting SVGs.
|
|
257
|
-
elm.style.setProperty('unicode-bidi', 'plaintext')
|
|
258
295
|
elm.style.setProperty('width', `${elementWidth}px`)
|
|
259
296
|
elm.style.setProperty('height', 'min-content')
|
|
260
|
-
elm.style.setProperty('font-size', `${opts.fontSize}px`)
|
|
261
|
-
elm.style.setProperty('font-family', opts.fontFamily)
|
|
262
|
-
elm.style.setProperty('font-weight', opts.fontWeight)
|
|
263
|
-
elm.style.setProperty('line-height', `${opts.lineHeight * opts.fontSize}px`)
|
|
264
297
|
elm.style.setProperty('text-align', textAlignmentsForLtr[opts.textAlign])
|
|
265
|
-
elm.style.setProperty('font-style', opts.fontStyle)
|
|
266
|
-
if (opts.otherStyles) {
|
|
267
|
-
for (const [key, value] of Object.entries(opts.otherStyles)) {
|
|
268
|
-
elm.style.setProperty(key, value)
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
298
|
|
|
272
299
|
const shouldTruncateToFirstLine =
|
|
273
300
|
opts.overflow === 'truncate-ellipsis' || opts.overflow === 'truncate-clip'
|
|
@@ -277,6 +304,12 @@ export class TextManager {
|
|
|
277
304
|
elm.style.setProperty('word-break', 'break-all')
|
|
278
305
|
}
|
|
279
306
|
|
|
307
|
+
if (opts.otherStyles) {
|
|
308
|
+
for (const [key, value] of Object.entries(opts.otherStyles)) {
|
|
309
|
+
elm.style.setProperty(key, value)
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
280
313
|
const normalizedText = normalizeTextForDom(textToMeasure)
|
|
281
314
|
|
|
282
315
|
// Render the text into the measurement element:
|
|
@@ -313,11 +346,10 @@ export class TextManager {
|
|
|
313
346
|
h: lastSpan.box.h,
|
|
314
347
|
},
|
|
315
348
|
})
|
|
349
|
+
|
|
316
350
|
return truncatedSpans
|
|
317
351
|
}
|
|
318
352
|
|
|
319
|
-
elm.remove()
|
|
320
|
-
|
|
321
353
|
return spans
|
|
322
354
|
}
|
|
323
355
|
}
|
|
@@ -4,12 +4,15 @@ import { LegacyMigrations, MigrationSequence } from '@tldraw/store'
|
|
|
4
4
|
import {
|
|
5
5
|
RecordProps,
|
|
6
6
|
TLHandle,
|
|
7
|
+
TLParentId,
|
|
7
8
|
TLPropsMigrations,
|
|
8
9
|
TLShape,
|
|
9
10
|
TLShapeCrop,
|
|
11
|
+
TLShapeId,
|
|
10
12
|
TLShapePartial,
|
|
11
13
|
TLUnknownShape,
|
|
12
14
|
} from '@tldraw/tlschema'
|
|
15
|
+
import { IndexKey } from '@tldraw/utils'
|
|
13
16
|
import { ReactElement } from 'react'
|
|
14
17
|
import { Box, SelectionHandle } from '../../primitives/Box'
|
|
15
18
|
import { Vec } from '../../primitives/Vec'
|
|
@@ -387,17 +390,6 @@ export abstract class ShapeUtil<Shape extends TLUnknownShape = TLUnknownShape> {
|
|
|
387
390
|
return false
|
|
388
391
|
}
|
|
389
392
|
|
|
390
|
-
/**
|
|
391
|
-
* Get whether the shape can receive children of a given type.
|
|
392
|
-
*
|
|
393
|
-
* @param shape - The shape type.
|
|
394
|
-
* @param shapes - The shapes that are being dropped.
|
|
395
|
-
* @public
|
|
396
|
-
*/
|
|
397
|
-
canDropShapes(_shape: Shape, _shapes: TLShape[]) {
|
|
398
|
-
return false
|
|
399
|
-
}
|
|
400
|
-
|
|
401
393
|
/**
|
|
402
394
|
* Get the shape as an SVG object.
|
|
403
395
|
*
|
|
@@ -517,7 +509,16 @@ export abstract class ShapeUtil<Shape extends TLUnknownShape = TLUnknownShape> {
|
|
|
517
509
|
): Omit<TLShapePartial<Shape>, 'id' | 'type'> | undefined | void
|
|
518
510
|
|
|
519
511
|
/**
|
|
520
|
-
* A callback called when some other shapes are dragged
|
|
512
|
+
* A callback called when some other shapes are dragged into this one. This fires when the shapes are dragged over the shape for the first time.
|
|
513
|
+
*
|
|
514
|
+
* @param shape - The shape.
|
|
515
|
+
* @param shapes - The shapes that are being dragged in.
|
|
516
|
+
* @public
|
|
517
|
+
*/
|
|
518
|
+
onDragShapesIn?(shape: Shape, shapes: TLShape[], info: TLDragShapesInInfo): void
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* A callback called when some other shapes are dragged over this one. This fires when the shapes are dragged over the shape for the first time (after the onDragShapesIn callback), and again on every update while the shapes are being dragged.
|
|
521
522
|
*
|
|
522
523
|
* @example
|
|
523
524
|
*
|
|
@@ -531,7 +532,7 @@ export abstract class ShapeUtil<Shape extends TLUnknownShape = TLUnknownShape> {
|
|
|
531
532
|
* @param shapes - The shapes that are being dragged over this one.
|
|
532
533
|
* @public
|
|
533
534
|
*/
|
|
534
|
-
onDragShapesOver?(shape: Shape, shapes: TLShape[]): void
|
|
535
|
+
onDragShapesOver?(shape: Shape, shapes: TLShape[], info: TLDragShapesOverInfo): void
|
|
535
536
|
|
|
536
537
|
/**
|
|
537
538
|
* A callback called when some other shapes are dragged out of this one.
|
|
@@ -540,7 +541,7 @@ export abstract class ShapeUtil<Shape extends TLUnknownShape = TLUnknownShape> {
|
|
|
540
541
|
* @param shapes - The shapes that are being dragged out.
|
|
541
542
|
* @public
|
|
542
543
|
*/
|
|
543
|
-
onDragShapesOut?(shape: Shape, shapes: TLShape[]): void
|
|
544
|
+
onDragShapesOut?(shape: Shape, shapes: TLShape[], info: TLDragShapesOutInfo): void
|
|
544
545
|
|
|
545
546
|
/**
|
|
546
547
|
* A callback called when some other shapes are dropped over this one.
|
|
@@ -549,7 +550,7 @@ export abstract class ShapeUtil<Shape extends TLUnknownShape = TLUnknownShape> {
|
|
|
549
550
|
* @param shapes - The shapes that are being dropped over this one.
|
|
550
551
|
* @public
|
|
551
552
|
*/
|
|
552
|
-
onDropShapesOver?(shape: Shape, shapes: TLShape[]): void
|
|
553
|
+
onDropShapesOver?(shape: Shape, shapes: TLShape[], info: TLDropShapesOverInfo): void
|
|
553
554
|
|
|
554
555
|
/**
|
|
555
556
|
* A callback called when a shape starts being resized.
|
|
@@ -745,6 +746,37 @@ export interface TLCropInfo<T extends TLShape> {
|
|
|
745
746
|
crop: TLShapeCrop
|
|
746
747
|
uncroppedSize: { w: number; h: number }
|
|
747
748
|
initialShape: T
|
|
749
|
+
aspectRatioLocked?: boolean
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
/** @public */
|
|
753
|
+
export interface TLDragShapesInInfo {
|
|
754
|
+
initialDraggingOverShapeId: TLShapeId | null
|
|
755
|
+
prevDraggingOverShapeId: TLShapeId | null
|
|
756
|
+
initialParentIds: Map<TLShapeId, TLParentId>
|
|
757
|
+
initialIndices: Map<TLShapeId, IndexKey>
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
/** @public */
|
|
761
|
+
export interface TLDragShapesOverInfo {
|
|
762
|
+
initialDraggingOverShapeId: TLShapeId | null
|
|
763
|
+
initialParentIds: Map<TLShapeId, TLParentId>
|
|
764
|
+
initialIndices: Map<TLShapeId, IndexKey>
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
/** @public */
|
|
768
|
+
export interface TLDragShapesOutInfo {
|
|
769
|
+
nextDraggingOverShapeId: TLShapeId | null
|
|
770
|
+
initialDraggingOverShapeId: TLShapeId | null
|
|
771
|
+
initialParentIds: Map<TLShapeId, TLParentId>
|
|
772
|
+
initialIndices: Map<TLShapeId, IndexKey>
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
/** @public */
|
|
776
|
+
export interface TLDropShapesOverInfo {
|
|
777
|
+
initialDraggingOverShapeId: TLShapeId | null
|
|
778
|
+
initialParentIds: Map<TLShapeId, TLParentId>
|
|
779
|
+
initialIndices: Map<TLShapeId, IndexKey>
|
|
748
780
|
}
|
|
749
781
|
|
|
750
782
|
/**
|
|
@@ -11,29 +11,33 @@ export class Pointing extends StateNode {
|
|
|
11
11
|
static override id = 'pointing'
|
|
12
12
|
|
|
13
13
|
override onPointerMove(info: TLPointerEventInfo) {
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
const { editor } = this
|
|
15
|
+
if (editor.inputs.isDragging) {
|
|
16
|
+
const { originPagePoint } = editor.inputs
|
|
16
17
|
|
|
17
18
|
const shapeType = (this.parent as BaseBoxShapeTool)!.shapeType
|
|
18
19
|
|
|
19
20
|
const id = createShapeId()
|
|
20
21
|
|
|
21
|
-
const creatingMarkId =
|
|
22
|
-
const newPoint = maybeSnapToGrid(originPagePoint,
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
22
|
+
const creatingMarkId = editor.markHistoryStoppingPoint(`creating_box:${id}`)
|
|
23
|
+
const newPoint = maybeSnapToGrid(originPagePoint, editor)
|
|
24
|
+
|
|
25
|
+
// Allow this to trigger the max shapes reached alert
|
|
26
|
+
this.editor.createShapes<TLBaseBoxShape>([
|
|
27
|
+
{
|
|
28
|
+
id,
|
|
29
|
+
type: shapeType,
|
|
30
|
+
x: newPoint.x,
|
|
31
|
+
y: newPoint.y,
|
|
32
|
+
props: {
|
|
33
|
+
w: 1,
|
|
34
|
+
h: 1,
|
|
34
35
|
},
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
},
|
|
37
|
+
])
|
|
38
|
+
const shape = editor.getShape(id)
|
|
39
|
+
if (!shape) this.cancel()
|
|
40
|
+
editor.select(id)
|
|
37
41
|
|
|
38
42
|
const parent = this.parent as BaseBoxShapeTool
|
|
39
43
|
this.editor.setCurrentTool(
|
|
@@ -79,6 +83,7 @@ export class Pointing extends StateNode {
|
|
|
79
83
|
|
|
80
84
|
this.editor.markHistoryStoppingPoint(`creating_box:${id}`)
|
|
81
85
|
|
|
86
|
+
// Allow this to trigger the max shapes reached alert
|
|
82
87
|
// todo: add scale here when dynamic size is enabled (is this still needed?)
|
|
83
88
|
this.editor.createShapes<TLBaseBoxShape>([
|
|
84
89
|
{
|
|
@@ -206,15 +206,15 @@ export abstract class StateNode implements Partial<TLEventHandlers> {
|
|
|
206
206
|
}
|
|
207
207
|
|
|
208
208
|
// todo: move this logic into transition
|
|
209
|
-
exit(info: any,
|
|
209
|
+
exit(info: any, to: string) {
|
|
210
210
|
if (debugFlags.measurePerformance.get() && this.performanceTracker.isStarted()) {
|
|
211
211
|
this.performanceTracker.stop()
|
|
212
212
|
}
|
|
213
213
|
this._isActive.set(false)
|
|
214
|
-
this.onExit?.(info,
|
|
214
|
+
this.onExit?.(info, to)
|
|
215
215
|
|
|
216
216
|
if (!this.getIsActive()) {
|
|
217
|
-
this.getCurrent()?.exit(info,
|
|
217
|
+
this.getCurrent()?.exit(info, to)
|
|
218
218
|
}
|
|
219
219
|
}
|
|
220
220
|
|
|
@@ -18,6 +18,10 @@ export interface TLEventMap {
|
|
|
18
18
|
frame: [number]
|
|
19
19
|
'select-all-text': [{ shapeId: TLShapeId }]
|
|
20
20
|
'place-caret': [{ shapeId: TLShapeId; point: { x: number; y: number } }]
|
|
21
|
+
'created-shapes': [TLRecord[]]
|
|
22
|
+
'edited-shapes': [TLRecord[]]
|
|
23
|
+
'deleted-shapes': [TLShapeId[]]
|
|
24
|
+
edit: []
|
|
21
25
|
}
|
|
22
26
|
|
|
23
27
|
/** @public */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TLAssetId } from '@tldraw/tlschema'
|
|
1
|
+
import { TLAssetId, TLShapeId } from '@tldraw/tlschema'
|
|
2
2
|
import { VecLike } from '../../primitives/Vec'
|
|
3
3
|
import { TLContent } from './clipboard-types'
|
|
4
4
|
|
|
@@ -52,7 +52,15 @@ export interface TLTextExternalContent extends TLBaseExternalContent {
|
|
|
52
52
|
export interface TLFilesExternalContent extends TLBaseExternalContent {
|
|
53
53
|
type: 'files'
|
|
54
54
|
files: File[]
|
|
55
|
-
ignoreParent
|
|
55
|
+
ignoreParent?: boolean
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** @public */
|
|
59
|
+
export interface TLFileReplaceExternalContent extends TLBaseExternalContent {
|
|
60
|
+
type: 'file-replace'
|
|
61
|
+
file: File
|
|
62
|
+
shapeId: TLShapeId
|
|
63
|
+
isImage: boolean
|
|
56
64
|
}
|
|
57
65
|
|
|
58
66
|
/** @public */
|
|
@@ -90,6 +98,7 @@ export interface TLExcalidrawExternalContent extends TLBaseExternalContent {
|
|
|
90
98
|
export type TLExternalContent<EmbedDefinition> =
|
|
91
99
|
| TLTextExternalContent
|
|
92
100
|
| TLFilesExternalContent
|
|
101
|
+
| TLFileReplaceExternalContent
|
|
93
102
|
| TLUrlExternalContent
|
|
94
103
|
| TLSvgTextExternalContent
|
|
95
104
|
| TLEmbedExternalContent<EmbedDefinition>
|
|
@@ -591,14 +591,6 @@ export class Box {
|
|
|
591
591
|
return b.x === a.x && b.y === a.y && b.w === a.w && b.h === a.h
|
|
592
592
|
}
|
|
593
593
|
|
|
594
|
-
prettyMuchEquals(other: Box | BoxModel) {
|
|
595
|
-
return this.clone().toFixed().equals(Box.From(other).toFixed())
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
static PrettyMuchEquals(a: Box | BoxModel, b: Box | BoxModel) {
|
|
599
|
-
return b.x === a.x && b.y === a.y && b.w === a.w && b.h === a.h
|
|
600
|
-
}
|
|
601
|
-
|
|
602
594
|
zeroFix() {
|
|
603
595
|
this.w = Math.max(1, this.w)
|
|
604
596
|
this.h = Math.max(1, this.h)
|
|
@@ -44,6 +44,7 @@ export const Geometry2dFilters: {
|
|
|
44
44
|
/** @public */
|
|
45
45
|
export interface TransformedGeometry2dOptions {
|
|
46
46
|
isLabel?: boolean
|
|
47
|
+
isEmptyLabel?: boolean
|
|
47
48
|
isInternal?: boolean
|
|
48
49
|
debugColor?: string
|
|
49
50
|
ignore?: boolean
|
|
@@ -57,20 +58,24 @@ export interface Geometry2dOptions extends TransformedGeometry2dOptions {
|
|
|
57
58
|
|
|
58
59
|
/** @public */
|
|
59
60
|
export abstract class Geometry2d {
|
|
61
|
+
// todo: consider making accessors for these too, so that they can be overridden in subclasses by geometries with more complex logic
|
|
60
62
|
isFilled = false
|
|
61
63
|
isClosed = true
|
|
62
64
|
isLabel = false
|
|
65
|
+
isEmptyLabel = false
|
|
63
66
|
isInternal = false
|
|
64
67
|
debugColor?: string
|
|
65
68
|
ignore?: boolean
|
|
66
69
|
|
|
67
70
|
constructor(opts: Geometry2dOptions) {
|
|
71
|
+
const { isLabel = false, isEmptyLabel = false, isInternal = false } = opts
|
|
68
72
|
this.isFilled = opts.isFilled
|
|
69
73
|
this.isClosed = opts.isClosed
|
|
70
|
-
this.isLabel = opts.isLabel ?? false
|
|
71
|
-
this.isInternal = opts.isInternal ?? false
|
|
72
74
|
this.debugColor = opts.debugColor
|
|
73
75
|
this.ignore = opts.ignore
|
|
76
|
+
this.isLabel = isLabel
|
|
77
|
+
this.isEmptyLabel = isEmptyLabel
|
|
78
|
+
this.isInternal = isInternal
|
|
74
79
|
}
|
|
75
80
|
|
|
76
81
|
isExcludedByFilter(filters?: Geometry2dFilters) {
|
|
@@ -17,14 +17,20 @@ export class Group2d extends Geometry2d {
|
|
|
17
17
|
) {
|
|
18
18
|
super({ ...config, isClosed: true, isFilled: false })
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
const addChildren = (children: Geometry2d[]) => {
|
|
21
|
+
for (const child of children) {
|
|
22
|
+
if (child instanceof Group2d) {
|
|
23
|
+
addChildren(child.children)
|
|
24
|
+
} else if (child.ignore) {
|
|
25
|
+
this.ignoredChildren.push(child)
|
|
26
|
+
} else {
|
|
27
|
+
this.children.push(child)
|
|
28
|
+
}
|
|
25
29
|
}
|
|
26
30
|
}
|
|
27
31
|
|
|
32
|
+
addChildren(config.children)
|
|
33
|
+
|
|
28
34
|
if (this.children.length === 0) throw Error('Group2d must have at least one child')
|
|
29
35
|
}
|
|
30
36
|
|
package/src/lib/utils/dom.ts
CHANGED
|
@@ -18,7 +18,7 @@ import { debugFlags, pointerCaptureTrackingObject } from './debug-flags'
|
|
|
18
18
|
|
|
19
19
|
/** @public */
|
|
20
20
|
export function loopToHtmlElement(elm: Element): HTMLElement {
|
|
21
|
-
if (elm
|
|
21
|
+
if (elm.nodeType === Node.ELEMENT_NODE) return elm as HTMLElement
|
|
22
22
|
if (elm.parentElement) return loopToHtmlElement(elm.parentElement)
|
|
23
23
|
else throw Error('Could not find a parent element of an HTML type!')
|
|
24
24
|
}
|