@tldraw/editor 3.14.0-canary.8ea0ff8489db → 3.14.0-canary.8f303431285f
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 +166 -76
- package/dist-cjs/index.js +11 -10
- 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 +132 -101
- package/dist-cjs/lib/editor/Editor.js.map +2 -2
- package/dist-cjs/lib/editor/bindings/BindingUtil.js.map +2 -2
- package/dist-cjs/lib/editor/derivations/bindingsIndex.js +22 -22
- package/dist-cjs/lib/editor/derivations/bindingsIndex.js.map +2 -2
- package/dist-cjs/lib/editor/derivations/parentsToChildren.js +16 -16
- package/dist-cjs/lib/editor/derivations/parentsToChildren.js.map +2 -2
- package/dist-cjs/lib/editor/managers/{ClickManager.js → ClickManager/ClickManager.js} +1 -1
- package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js.map +7 -0
- package/dist-cjs/lib/editor/managers/{EdgeScrollManager.js → EdgeScrollManager/EdgeScrollManager.js} +2 -2
- package/dist-cjs/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.js.map +7 -0
- package/dist-cjs/lib/editor/managers/FocusManager/FocusManager.js.map +7 -0
- package/dist-cjs/lib/editor/managers/{FontManager.js → FontManager/FontManager.js} +4 -1
- package/dist-cjs/lib/editor/managers/FontManager/FontManager.js.map +7 -0
- package/dist-cjs/lib/editor/managers/{HistoryManager.js → HistoryManager/HistoryManager.js} +67 -7
- package/dist-cjs/lib/editor/managers/HistoryManager/HistoryManager.js.map +7 -0
- package/dist-cjs/lib/editor/managers/{ScribbleManager.js → ScribbleManager/ScribbleManager.js} +1 -1
- package/dist-cjs/lib/editor/managers/ScribbleManager/ScribbleManager.js.map +7 -0
- package/dist-cjs/lib/editor/managers/{TextManager.js → TextManager/TextManager.js} +73 -42
- package/dist-cjs/lib/editor/managers/TextManager/TextManager.js.map +7 -0
- package/dist-cjs/lib/editor/managers/{TickManager.js → TickManager/TickManager.js} +1 -1
- package/dist-cjs/lib/editor/managers/TickManager/TickManager.js.map +7 -0
- package/dist-cjs/lib/editor/managers/{UserPreferencesManager.js → UserPreferencesManager/UserPreferencesManager.js} +1 -1
- package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js.map +7 -0
- 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/exports/getSvgJsx.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 +33 -39
- 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/reorderShapes.js +11 -10
- package/dist-cjs/lib/utils/reorderShapes.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 +166 -76
- package/dist-esm/index.mjs +15 -10
- 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 +132 -101
- package/dist-esm/lib/editor/Editor.mjs.map +2 -2
- package/dist-esm/lib/editor/bindings/BindingUtil.mjs.map +2 -2
- package/dist-esm/lib/editor/derivations/bindingsIndex.mjs +22 -22
- package/dist-esm/lib/editor/derivations/bindingsIndex.mjs.map +2 -2
- package/dist-esm/lib/editor/derivations/parentsToChildren.mjs +16 -16
- package/dist-esm/lib/editor/derivations/parentsToChildren.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/{ClickManager.mjs → ClickManager/ClickManager.mjs} +1 -1
- package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs.map +7 -0
- package/dist-esm/lib/editor/managers/{EdgeScrollManager.mjs → EdgeScrollManager/EdgeScrollManager.mjs} +2 -2
- package/dist-esm/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.mjs.map +7 -0
- package/dist-esm/lib/editor/managers/FocusManager/FocusManager.mjs.map +7 -0
- package/dist-esm/lib/editor/managers/{FontManager.mjs → FontManager/FontManager.mjs} +4 -1
- package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs.map +7 -0
- package/dist-esm/lib/editor/managers/{HistoryManager.mjs → HistoryManager/HistoryManager.mjs} +63 -3
- package/dist-esm/lib/editor/managers/HistoryManager/HistoryManager.mjs.map +7 -0
- package/dist-esm/lib/editor/managers/{ScribbleManager.mjs → ScribbleManager/ScribbleManager.mjs} +1 -1
- package/dist-esm/lib/editor/managers/ScribbleManager/ScribbleManager.mjs.map +7 -0
- package/dist-esm/lib/editor/managers/{TextManager.mjs → TextManager/TextManager.mjs} +73 -42
- package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs.map +7 -0
- package/dist-esm/lib/editor/managers/{TickManager.mjs → TickManager/TickManager.mjs} +1 -1
- package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs.map +7 -0
- package/dist-esm/lib/editor/managers/{UserPreferencesManager.mjs → UserPreferencesManager/UserPreferencesManager.mjs} +1 -1
- package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs.map +7 -0
- 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/exports/getSvgJsx.mjs.map +1 -1
- 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 +33 -39
- 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/reorderShapes.mjs +11 -10
- package/dist-esm/lib/utils/reorderShapes.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 +20 -8
- package/src/lib/config/TLSessionStateSnapshot.ts +1 -1
- package/src/lib/editor/Editor.test.ts +252 -3
- package/src/lib/editor/Editor.ts +151 -111
- package/src/lib/editor/bindings/BindingUtil.ts +6 -0
- package/src/lib/editor/derivations/bindingsIndex.ts +27 -26
- package/src/lib/editor/derivations/parentsToChildren.ts +28 -25
- package/src/lib/editor/managers/ClickManager/ClickManager.test.ts +442 -0
- package/src/lib/editor/managers/{ClickManager.ts → ClickManager/ClickManager.ts} +3 -3
- package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.test.ts +374 -0
- package/src/lib/editor/managers/{EdgeScrollManager.ts → EdgeScrollManager/EdgeScrollManager.ts} +3 -3
- package/src/lib/editor/managers/FocusManager/FocusManager.test.ts +455 -0
- package/src/lib/editor/managers/{FocusManager.ts → FocusManager/FocusManager.ts} +1 -1
- package/src/lib/editor/managers/FontManager/FontManager.test.ts +263 -0
- package/src/lib/editor/managers/{FontManager.ts → FontManager/FontManager.ts} +5 -2
- package/src/lib/editor/managers/{HistoryManager.test.ts → HistoryManager/HistoryManager.test.ts} +388 -1
- package/src/lib/editor/managers/{HistoryManager.ts → HistoryManager/HistoryManager.ts} +76 -3
- package/src/lib/editor/managers/ScribbleManager/ScribbleManager.test.ts +624 -0
- package/src/lib/editor/managers/{ScribbleManager.ts → ScribbleManager/ScribbleManager.ts} +2 -2
- package/src/lib/editor/managers/SnapManager/SnapManager.test.ts +485 -0
- package/src/lib/editor/managers/TextManager/TextManager.test.ts +407 -0
- package/src/lib/editor/managers/{TextManager.ts → TextManager/TextManager.ts} +119 -87
- package/src/lib/editor/managers/TickManager/TickManager.test.ts +314 -0
- package/src/lib/editor/managers/{TickManager.ts → TickManager/TickManager.ts} +2 -2
- package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.test.ts +591 -0
- package/src/lib/editor/managers/{UserPreferencesManager.ts → UserPreferencesManager/UserPreferencesManager.ts} +2 -2
- package/src/lib/editor/shapes/ShapeUtil.ts +48 -16
- 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/exports/getSvgJsx.tsx +1 -1
- package/src/lib/hooks/useCanvasEvents.ts +0 -1
- package/src/lib/primitives/Box.test.ts +588 -7
- package/src/lib/primitives/Box.ts +33 -41
- 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/reorderShapes.ts +10 -13
- package/src/lib/utils/reparenting.ts +383 -0
- package/src/lib/utils/richText.ts +10 -4
- package/src/version.ts +3 -3
- package/dist-cjs/lib/editor/managers/ClickManager.js.map +0 -7
- package/dist-cjs/lib/editor/managers/EdgeScrollManager.js.map +0 -7
- package/dist-cjs/lib/editor/managers/FocusManager.js.map +0 -7
- package/dist-cjs/lib/editor/managers/FontManager.js.map +0 -7
- package/dist-cjs/lib/editor/managers/HistoryManager.js.map +0 -7
- package/dist-cjs/lib/editor/managers/ScribbleManager.js.map +0 -7
- package/dist-cjs/lib/editor/managers/Stack.js +0 -82
- package/dist-cjs/lib/editor/managers/Stack.js.map +0 -7
- package/dist-cjs/lib/editor/managers/TextManager.js.map +0 -7
- package/dist-cjs/lib/editor/managers/TickManager.js.map +0 -7
- package/dist-cjs/lib/editor/managers/UserPreferencesManager.js.map +0 -7
- package/dist-esm/lib/editor/managers/ClickManager.mjs.map +0 -7
- package/dist-esm/lib/editor/managers/EdgeScrollManager.mjs.map +0 -7
- package/dist-esm/lib/editor/managers/FocusManager.mjs.map +0 -7
- package/dist-esm/lib/editor/managers/FontManager.mjs.map +0 -7
- package/dist-esm/lib/editor/managers/HistoryManager.mjs.map +0 -7
- package/dist-esm/lib/editor/managers/ScribbleManager.mjs.map +0 -7
- package/dist-esm/lib/editor/managers/Stack.mjs +0 -62
- package/dist-esm/lib/editor/managers/Stack.mjs.map +0 -7
- package/dist-esm/lib/editor/managers/TextManager.mjs.map +0 -7
- package/dist-esm/lib/editor/managers/TickManager.mjs.map +0 -7
- package/dist-esm/lib/editor/managers/UserPreferencesManager.mjs.map +0 -7
- package/src/lib/editor/managers/ScribbleManager.test.ts +0 -32
- package/src/lib/editor/managers/Stack.ts +0 -71
- /package/dist-cjs/lib/editor/managers/{FocusManager.js → FocusManager/FocusManager.js} +0 -0
- /package/dist-esm/lib/editor/managers/{FocusManager.mjs → FocusManager/FocusManager.mjs} +0 -0
|
@@ -134,33 +134,33 @@ export class Box {
|
|
|
134
134
|
|
|
135
135
|
// eslint-disable-next-line no-restricted-syntax
|
|
136
136
|
get center() {
|
|
137
|
-
return new Vec(this.
|
|
137
|
+
return new Vec(this.x + this.w / 2, this.y + this.h / 2)
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
// eslint-disable-next-line no-restricted-syntax
|
|
141
141
|
set center(v: Vec) {
|
|
142
|
-
this.
|
|
143
|
-
this.
|
|
142
|
+
this.x = v.x - this.w / 2
|
|
143
|
+
this.y = v.y - this.h / 2
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
// eslint-disable-next-line no-restricted-syntax
|
|
147
147
|
get corners() {
|
|
148
148
|
return [
|
|
149
|
-
new Vec(this.
|
|
150
|
-
new Vec(this.
|
|
151
|
-
new Vec(this.
|
|
152
|
-
new Vec(this.
|
|
149
|
+
new Vec(this.x, this.y),
|
|
150
|
+
new Vec(this.x + this.w, this.y),
|
|
151
|
+
new Vec(this.x + this.w, this.y + this.h),
|
|
152
|
+
new Vec(this.x, this.y + this.h),
|
|
153
153
|
]
|
|
154
154
|
}
|
|
155
155
|
|
|
156
156
|
// eslint-disable-next-line no-restricted-syntax
|
|
157
157
|
get cornersAndCenter() {
|
|
158
158
|
return [
|
|
159
|
-
new Vec(this.
|
|
160
|
-
new Vec(this.
|
|
161
|
-
new Vec(this.
|
|
162
|
-
new Vec(this.
|
|
163
|
-
this.
|
|
159
|
+
new Vec(this.x, this.y),
|
|
160
|
+
new Vec(this.x + this.w, this.y),
|
|
161
|
+
new Vec(this.x + this.w, this.y + this.h),
|
|
162
|
+
new Vec(this.x, this.y + this.h),
|
|
163
|
+
new Vec(this.x + this.w / 2, this.y + this.h / 2),
|
|
164
164
|
]
|
|
165
165
|
}
|
|
166
166
|
|
|
@@ -205,10 +205,10 @@ export class Box {
|
|
|
205
205
|
}
|
|
206
206
|
|
|
207
207
|
expand(A: Box) {
|
|
208
|
-
const minX = Math.min(this.
|
|
209
|
-
const minY = Math.min(this.
|
|
210
|
-
const maxX = Math.max(this.
|
|
211
|
-
const maxY = Math.max(this.
|
|
208
|
+
const minX = Math.min(this.x, A.x)
|
|
209
|
+
const minY = Math.min(this.y, A.y)
|
|
210
|
+
const maxX = Math.max(this.x + this.w, A.x + A.w)
|
|
211
|
+
const maxY = Math.max(this.y + this.h, A.y + A.h)
|
|
212
212
|
|
|
213
213
|
this.x = minX
|
|
214
214
|
this.y = minY
|
|
@@ -245,10 +245,10 @@ export class Box {
|
|
|
245
245
|
}
|
|
246
246
|
|
|
247
247
|
snapToGrid(size: number) {
|
|
248
|
-
const minX = Math.round(this.
|
|
249
|
-
const minY = Math.round(this.
|
|
250
|
-
const maxX = Math.round(this.
|
|
251
|
-
const maxY = Math.round(this.
|
|
248
|
+
const minX = Math.round(this.x / size) * size
|
|
249
|
+
const minY = Math.round(this.y / size) * size
|
|
250
|
+
const maxX = Math.round((this.x + this.w) / size) * size
|
|
251
|
+
const maxY = Math.round((this.y + this.h) / size) * size
|
|
252
252
|
this.minX = minX
|
|
253
253
|
this.minY = minY
|
|
254
254
|
this.width = Math.max(1, maxX - minX)
|
|
@@ -274,26 +274,26 @@ export class Box {
|
|
|
274
274
|
getHandlePoint(handle: SelectionCorner | SelectionEdge) {
|
|
275
275
|
switch (handle) {
|
|
276
276
|
case 'top_left':
|
|
277
|
-
return new Vec(this.
|
|
277
|
+
return new Vec(this.x, this.y)
|
|
278
278
|
case 'top_right':
|
|
279
|
-
return new Vec(this.
|
|
279
|
+
return new Vec(this.x + this.w, this.y)
|
|
280
280
|
case 'bottom_left':
|
|
281
|
-
return new Vec(this.
|
|
281
|
+
return new Vec(this.x, this.y + this.h)
|
|
282
282
|
case 'bottom_right':
|
|
283
|
-
return new Vec(this.
|
|
283
|
+
return new Vec(this.x + this.w, this.y + this.h)
|
|
284
284
|
case 'top':
|
|
285
|
-
return new Vec(this.
|
|
285
|
+
return new Vec(this.x + this.w / 2, this.y)
|
|
286
286
|
case 'right':
|
|
287
|
-
return new Vec(this.
|
|
287
|
+
return new Vec(this.x + this.w, this.y + this.h / 2)
|
|
288
288
|
case 'bottom':
|
|
289
|
-
return new Vec(this.
|
|
289
|
+
return new Vec(this.x + this.w / 2, this.y + this.h)
|
|
290
290
|
case 'left':
|
|
291
|
-
return new Vec(this.
|
|
291
|
+
return new Vec(this.x, this.y + this.h / 2)
|
|
292
292
|
}
|
|
293
293
|
}
|
|
294
294
|
|
|
295
295
|
toJson(): BoxModel {
|
|
296
|
-
return { x: this.
|
|
296
|
+
return { x: this.x, y: this.y, w: this.w, h: this.h }
|
|
297
297
|
}
|
|
298
298
|
|
|
299
299
|
resize(handle: SelectionCorner | SelectionEdge | string, dx: number, dy: number) {
|
|
@@ -357,10 +357,10 @@ export class Box {
|
|
|
357
357
|
}
|
|
358
358
|
|
|
359
359
|
union(box: BoxModel) {
|
|
360
|
-
const minX = Math.min(this.
|
|
361
|
-
const minY = Math.min(this.
|
|
362
|
-
const maxX = Math.max(this.
|
|
363
|
-
const maxY = Math.max(this.
|
|
360
|
+
const minX = Math.min(this.x, box.x)
|
|
361
|
+
const minY = Math.min(this.y, box.y)
|
|
362
|
+
const maxX = Math.max(this.x + this.w, box.x + box.w)
|
|
363
|
+
const maxY = Math.max(this.y + this.h, box.y + box.h)
|
|
364
364
|
|
|
365
365
|
this.x = minX
|
|
366
366
|
this.y = minY
|
|
@@ -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
|
}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { TLParentId, TLShape, TLShapeId, TLShapePartial } from '@tldraw/tlschema'
|
|
2
2
|
import { IndexKey, compact, getIndicesBetween, sortByIndex } from '@tldraw/utils'
|
|
3
3
|
import { Editor } from '../editor/Editor'
|
|
4
|
-
import { Vec } from '../primitives/Vec'
|
|
5
|
-
import { polygonsIntersect } from '../primitives/intersect'
|
|
6
4
|
|
|
7
5
|
export function getReorderingShapesChanges(
|
|
8
6
|
editor: Editor,
|
|
@@ -155,19 +153,18 @@ function reorderToFront(moving: Set<TLShape>, children: TLShape[], changes: TLSh
|
|
|
155
153
|
}
|
|
156
154
|
|
|
157
155
|
function getOverlapChecker(editor: Editor, moving: Set<TLShape>) {
|
|
158
|
-
const
|
|
159
|
-
.map((shape) => {
|
|
160
|
-
const
|
|
161
|
-
if (!
|
|
162
|
-
return { shape,
|
|
156
|
+
const movingBounds = compact(
|
|
157
|
+
Array.from(moving).map((shape) => {
|
|
158
|
+
const bounds = editor.getShapePageBounds(shape)
|
|
159
|
+
if (!bounds) return null
|
|
160
|
+
return { shape, bounds }
|
|
163
161
|
})
|
|
164
|
-
|
|
165
|
-
|
|
162
|
+
)
|
|
166
163
|
const isOverlapping = (child: TLShape) => {
|
|
167
|
-
const
|
|
168
|
-
if (!
|
|
169
|
-
return
|
|
170
|
-
return
|
|
164
|
+
const bounds = editor.getShapePageBounds(child)
|
|
165
|
+
if (!bounds) return false
|
|
166
|
+
return movingBounds.some((other) => {
|
|
167
|
+
return other.bounds.includes(bounds)
|
|
171
168
|
})
|
|
172
169
|
}
|
|
173
170
|
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
import { EMPTY_ARRAY } from '@tldraw/state'
|
|
2
|
+
import { TLGroupShape, TLParentId, TLShape, TLShapeId } from '@tldraw/tlschema'
|
|
3
|
+
import { IndexKey, compact, getIndexAbove, getIndexBetween } from '@tldraw/utils'
|
|
4
|
+
import { Editor } from '../editor/Editor'
|
|
5
|
+
import { Vec } from '../primitives/Vec'
|
|
6
|
+
import { Geometry2d } from '../primitives/geometry/Geometry2d'
|
|
7
|
+
import { Group2d } from '../primitives/geometry/Group2d'
|
|
8
|
+
import {
|
|
9
|
+
intersectPolygonPolygon,
|
|
10
|
+
polygonIntersectsPolyline,
|
|
11
|
+
polygonsIntersect,
|
|
12
|
+
} from '../primitives/intersect'
|
|
13
|
+
import { pointInPolygon } from '../primitives/utils'
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Reparents shapes that are no longer contained within their parent shapes.
|
|
17
|
+
* todo: rename me to something more descriptive, like `reparentOccludedShapes` or `reparentAutoDroppedShapes`
|
|
18
|
+
*
|
|
19
|
+
* @param editor - The editor instance.
|
|
20
|
+
* @param shapeIds - The IDs of the shapes to reparent.
|
|
21
|
+
* @param opts - Optional options, including a callback to filter out certain parents, such as when removing a frame.
|
|
22
|
+
*
|
|
23
|
+
* @public
|
|
24
|
+
*/
|
|
25
|
+
export function kickoutOccludedShapes(
|
|
26
|
+
editor: Editor,
|
|
27
|
+
shapeIds: TLShapeId[],
|
|
28
|
+
opts?: { filter?(parent: TLShape): boolean }
|
|
29
|
+
) {
|
|
30
|
+
const parentsToCheck = new Set<TLShape>()
|
|
31
|
+
|
|
32
|
+
for (const id of shapeIds) {
|
|
33
|
+
const shape = editor.getShape(id)
|
|
34
|
+
|
|
35
|
+
if (!shape) continue
|
|
36
|
+
parentsToCheck.add(shape)
|
|
37
|
+
|
|
38
|
+
const parent = editor.getShape(shape.parentId)
|
|
39
|
+
if (!parent) continue
|
|
40
|
+
parentsToCheck.add(parent)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Check all of the parents and gather up parents who have lost children
|
|
44
|
+
const parentsToLostChildren = new Map<TLShape, TLShapeId[]>()
|
|
45
|
+
|
|
46
|
+
for (const parent of parentsToCheck) {
|
|
47
|
+
const childIds = editor.getSortedChildIdsForParent(parent)
|
|
48
|
+
if (opts?.filter && !opts.filter(parent)) {
|
|
49
|
+
// If the shape is filtered out, we kick out all of its children
|
|
50
|
+
parentsToLostChildren.set(parent, childIds)
|
|
51
|
+
} else {
|
|
52
|
+
const overlappingChildren = getOverlappingShapes(editor, parent.id, childIds)
|
|
53
|
+
if (overlappingChildren.length < childIds.length) {
|
|
54
|
+
parentsToLostChildren.set(
|
|
55
|
+
parent,
|
|
56
|
+
childIds.filter((id) => !overlappingChildren.includes(id))
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Get all of the shapes on the current page, sorted by their index
|
|
63
|
+
const sortedShapeIds = editor.getCurrentPageShapesSorted().map((s) => s.id)
|
|
64
|
+
|
|
65
|
+
const parentsToNewChildren: Record<
|
|
66
|
+
TLParentId,
|
|
67
|
+
{ parentId: TLParentId; shapeIds: TLShapeId[]; index?: IndexKey }
|
|
68
|
+
> = {}
|
|
69
|
+
|
|
70
|
+
for (const [prevParent, lostChildrenIds] of parentsToLostChildren) {
|
|
71
|
+
const lostChildren = compact(lostChildrenIds.map((id) => editor.getShape(id)))
|
|
72
|
+
|
|
73
|
+
// Don't fall "up" into frames in front of the shape
|
|
74
|
+
// if (pageShapes.indexOf(shape) < frameSortPosition) continue shapeCheck
|
|
75
|
+
|
|
76
|
+
// Otherwise, we have no next dropping shape under the cursor, so go find
|
|
77
|
+
// all the frames on the page where the moving shapes will fall into
|
|
78
|
+
const { reparenting, remainingShapesToReparent } = getDroppedShapesToNewParents(
|
|
79
|
+
editor,
|
|
80
|
+
lostChildren,
|
|
81
|
+
(shape, maybeNewParent) => {
|
|
82
|
+
// If we're filtering out a potential parent, don't reparent shapes to the filtered out shape
|
|
83
|
+
if (opts?.filter && !opts.filter(maybeNewParent)) return false
|
|
84
|
+
return (
|
|
85
|
+
maybeNewParent.id !== prevParent.id &&
|
|
86
|
+
sortedShapeIds.indexOf(maybeNewParent.id) < sortedShapeIds.indexOf(shape.id)
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
reparenting.forEach((childrenToReparent, newParentId) => {
|
|
92
|
+
if (childrenToReparent.length === 0) return
|
|
93
|
+
if (!parentsToNewChildren[newParentId]) {
|
|
94
|
+
parentsToNewChildren[newParentId] = {
|
|
95
|
+
parentId: newParentId,
|
|
96
|
+
shapeIds: [],
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
parentsToNewChildren[newParentId].shapeIds.push(...childrenToReparent.map((s) => s.id))
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
// Reparent the rest to the page (or containing group)
|
|
103
|
+
if (remainingShapesToReparent.size > 0) {
|
|
104
|
+
// The remaining shapes are going to be reparented to the old parent's containing group, if there was one, or else to the page
|
|
105
|
+
const newParentId =
|
|
106
|
+
editor.findShapeAncestor(prevParent, (s) => editor.isShapeOfType<TLGroupShape>(s, 'group'))
|
|
107
|
+
?.id ?? editor.getCurrentPageId()
|
|
108
|
+
|
|
109
|
+
remainingShapesToReparent.forEach((shape) => {
|
|
110
|
+
if (!parentsToNewChildren[newParentId]) {
|
|
111
|
+
let insertIndexKey: IndexKey | undefined
|
|
112
|
+
|
|
113
|
+
const oldParentSiblingIds = editor.getSortedChildIdsForParent(newParentId)
|
|
114
|
+
const oldParentIndex = oldParentSiblingIds.indexOf(prevParent.id)
|
|
115
|
+
if (oldParentIndex > -1) {
|
|
116
|
+
// If the old parent is a direct child of the new parent, then we'll add them above the old parent but below the next sibling.
|
|
117
|
+
const siblingsIndexAbove = oldParentSiblingIds[oldParentIndex + 1]
|
|
118
|
+
const indexKeyAbove = siblingsIndexAbove
|
|
119
|
+
? editor.getShape(siblingsIndexAbove)!.index
|
|
120
|
+
: getIndexAbove(prevParent.index)
|
|
121
|
+
insertIndexKey = getIndexBetween(prevParent.index, indexKeyAbove)
|
|
122
|
+
} else {
|
|
123
|
+
// If the old parent is not a direct child of the new parent, then we'll add them to the "top" of the new parent's children.
|
|
124
|
+
// This is done automatically if we leave the index undefined, so let's do that.
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
parentsToNewChildren[newParentId] = {
|
|
128
|
+
parentId: newParentId,
|
|
129
|
+
shapeIds: [],
|
|
130
|
+
index: insertIndexKey,
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
parentsToNewChildren[newParentId].shapeIds.push(shape.id)
|
|
135
|
+
})
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
editor.run(() => {
|
|
140
|
+
Object.values(parentsToNewChildren).forEach(({ parentId, shapeIds, index }) => {
|
|
141
|
+
if (shapeIds.length === 0) return
|
|
142
|
+
// Before we reparent, sort the new shape ids by their place in the original absolute order on the page
|
|
143
|
+
shapeIds.sort((a, b) => (sortedShapeIds.indexOf(a) < sortedShapeIds.indexOf(b) ? -1 : 1))
|
|
144
|
+
editor.reparentShapes(shapeIds, parentId, index)
|
|
145
|
+
})
|
|
146
|
+
})
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Get the shapes that overlap with a given shape.
|
|
151
|
+
*
|
|
152
|
+
* @param editor - The editor instance.
|
|
153
|
+
* @param shape - The shapes or shape IDs to check against.
|
|
154
|
+
* @param otherShapes - The shapes or shape IDs to check for overlap.
|
|
155
|
+
* @returns An array of shapes or shape IDs that overlap with the given shape.
|
|
156
|
+
*/
|
|
157
|
+
function getOverlappingShapes<T extends TLShape[] | TLShapeId[]>(
|
|
158
|
+
editor: Editor,
|
|
159
|
+
shape: T[number],
|
|
160
|
+
otherShapes: T
|
|
161
|
+
) {
|
|
162
|
+
if (otherShapes.length === 0) {
|
|
163
|
+
return EMPTY_ARRAY
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const parentPageBounds = editor.getShapePageBounds(shape)
|
|
167
|
+
if (!parentPageBounds) return EMPTY_ARRAY
|
|
168
|
+
|
|
169
|
+
const parentGeometry = editor.getShapeGeometry(shape)
|
|
170
|
+
const parentPageTransform = editor.getShapePageTransform(shape)
|
|
171
|
+
const parentPageCorners = parentPageTransform.applyToPoints(parentGeometry.vertices)
|
|
172
|
+
|
|
173
|
+
const parentPageMaskVertices = editor.getShapeMask(shape)
|
|
174
|
+
const parentPagePolygon = parentPageMaskVertices
|
|
175
|
+
? intersectPolygonPolygon(parentPageMaskVertices, parentPageCorners)
|
|
176
|
+
: parentPageCorners
|
|
177
|
+
|
|
178
|
+
if (!parentPagePolygon) return EMPTY_ARRAY
|
|
179
|
+
|
|
180
|
+
return otherShapes.filter((childId) => {
|
|
181
|
+
const shapePageBounds = editor.getShapePageBounds(childId)
|
|
182
|
+
if (!shapePageBounds || !parentPageBounds.includes(shapePageBounds)) return false
|
|
183
|
+
|
|
184
|
+
const parentPolygonInShapeShape = editor
|
|
185
|
+
.getShapePageTransform(childId)
|
|
186
|
+
.clone()
|
|
187
|
+
.invert()
|
|
188
|
+
.applyToPoints(parentPagePolygon)
|
|
189
|
+
|
|
190
|
+
const geometry = editor.getShapeGeometry(childId)
|
|
191
|
+
|
|
192
|
+
return doesGeometryOverlapPolygon(geometry, parentPolygonInShapeShape)
|
|
193
|
+
})
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* @public
|
|
198
|
+
*/
|
|
199
|
+
export function doesGeometryOverlapPolygon(
|
|
200
|
+
geometry: Geometry2d,
|
|
201
|
+
parentCornersInShapeSpace: Vec[]
|
|
202
|
+
): boolean {
|
|
203
|
+
// If the child is a group, check if any of its children overlap the box
|
|
204
|
+
if (geometry instanceof Group2d) {
|
|
205
|
+
return geometry.children.some((childGeometry) =>
|
|
206
|
+
doesGeometryOverlapPolygon(childGeometry, parentCornersInShapeSpace)
|
|
207
|
+
)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Otherwise, check if the geometry overlaps the box
|
|
211
|
+
const { vertices, center, isFilled, isEmptyLabel, isClosed } = geometry
|
|
212
|
+
|
|
213
|
+
// We'll do things in order of cheapest to most expensive checks
|
|
214
|
+
|
|
215
|
+
// Skip empty labels
|
|
216
|
+
if (isEmptyLabel) return false
|
|
217
|
+
|
|
218
|
+
// If any of the shape's vertices are inside the occluder, it's inside
|
|
219
|
+
if (vertices.some((v) => pointInPolygon(v, parentCornersInShapeSpace))) {
|
|
220
|
+
return true
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// If the shape is filled and closed and its center is inside the parent, it's inside
|
|
224
|
+
if (isClosed) {
|
|
225
|
+
if (isFilled) {
|
|
226
|
+
// If closed and filled, check if the center is inside the parent
|
|
227
|
+
if (pointInPolygon(center, parentCornersInShapeSpace)) {
|
|
228
|
+
return true
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// ..then, slightly more expensive check, see the shape covers the entire parent but not its center
|
|
232
|
+
if (parentCornersInShapeSpace.every((v) => pointInPolygon(v, vertices))) {
|
|
233
|
+
return true
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// If any the shape's vertices intersect the edge of the occluder, it's inside.
|
|
238
|
+
// for example when a rotated rectangle is moved over the corner of a parent rectangle
|
|
239
|
+
// If the child shape is closed, intersect as a polygon
|
|
240
|
+
if (polygonsIntersect(parentCornersInShapeSpace, vertices)) {
|
|
241
|
+
return true
|
|
242
|
+
}
|
|
243
|
+
} else {
|
|
244
|
+
// if the child shape is not closed, intersect as a polyline
|
|
245
|
+
if (polygonIntersectsPolyline(parentCornersInShapeSpace, vertices)) {
|
|
246
|
+
return true
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// If none of the above checks passed, the shape is outside the parent
|
|
251
|
+
return false
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Get the shapes that will be reparented to new parents when the shapes are dropped.
|
|
256
|
+
*
|
|
257
|
+
* @param editor - The editor instance.
|
|
258
|
+
* @param shapes - The shapes to check.
|
|
259
|
+
* @param cb - A callback to filter out certain shapes.
|
|
260
|
+
* @returns An object with the shapes that will be reparented to new parents and the shapes that will be reparented to the page or their ancestral group.
|
|
261
|
+
*
|
|
262
|
+
* @public
|
|
263
|
+
*/
|
|
264
|
+
export function getDroppedShapesToNewParents(
|
|
265
|
+
editor: Editor,
|
|
266
|
+
shapes: Set<TLShape> | TLShape[],
|
|
267
|
+
cb?: (shape: TLShape, parent: TLShape) => boolean
|
|
268
|
+
) {
|
|
269
|
+
const shapesToActuallyCheck = new Set<TLShape>(shapes)
|
|
270
|
+
const movingGroups = new Set<TLGroupShape>()
|
|
271
|
+
|
|
272
|
+
for (const shape of shapes) {
|
|
273
|
+
const parent = editor.getShapeParent(shape)
|
|
274
|
+
if (parent && editor.isShapeOfType<TLGroupShape>(parent, 'group')) {
|
|
275
|
+
if (!movingGroups.has(parent)) {
|
|
276
|
+
movingGroups.add(parent)
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// If all of a group's children are moving, then move the group instead
|
|
282
|
+
for (const movingGroup of movingGroups) {
|
|
283
|
+
const children = compact(
|
|
284
|
+
editor.getSortedChildIdsForParent(movingGroup).map((id) => editor.getShape(id))
|
|
285
|
+
)
|
|
286
|
+
for (const child of children) {
|
|
287
|
+
shapesToActuallyCheck.delete(child)
|
|
288
|
+
}
|
|
289
|
+
shapesToActuallyCheck.add(movingGroup)
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// this could be cached and passed in
|
|
293
|
+
const shapeGroupIds = new Map<TLShapeId, TLShapeId | undefined>()
|
|
294
|
+
|
|
295
|
+
const reparenting = new Map<TLShapeId, TLShape[]>()
|
|
296
|
+
|
|
297
|
+
const remainingShapesToReparent = new Set(shapesToActuallyCheck)
|
|
298
|
+
|
|
299
|
+
const potentialParentShapes = editor
|
|
300
|
+
.getCurrentPageShapesSorted()
|
|
301
|
+
// filter out any shapes that aren't frames or that are included among the provided shapes
|
|
302
|
+
.filter(
|
|
303
|
+
(s) =>
|
|
304
|
+
editor.getShapeUtil(s).canReceiveNewChildrenOfType?.(s, s.type) &&
|
|
305
|
+
!remainingShapesToReparent.has(s)
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
parentCheck: for (let i = potentialParentShapes.length - 1; i >= 0; i--) {
|
|
309
|
+
const parentShape = potentialParentShapes[i]
|
|
310
|
+
const parentShapeContainingGroupId = editor.findShapeAncestor(parentShape, (s) =>
|
|
311
|
+
editor.isShapeOfType<TLGroupShape>(s, 'group')
|
|
312
|
+
)?.id
|
|
313
|
+
|
|
314
|
+
const parentGeometry = editor.getShapeGeometry(parentShape)
|
|
315
|
+
const parentPageTransform = editor.getShapePageTransform(parentShape)
|
|
316
|
+
const parentPageMaskVertices = editor.getShapeMask(parentShape)
|
|
317
|
+
const parentPageCorners = parentPageTransform.applyToPoints(parentGeometry.vertices)
|
|
318
|
+
const parentPagePolygon = parentPageMaskVertices
|
|
319
|
+
? intersectPolygonPolygon(parentPageMaskVertices, parentPageCorners)
|
|
320
|
+
: parentPageCorners
|
|
321
|
+
|
|
322
|
+
if (!parentPagePolygon) continue parentCheck
|
|
323
|
+
|
|
324
|
+
const childrenToReparent = []
|
|
325
|
+
|
|
326
|
+
// For each of the dropping shapes...
|
|
327
|
+
shapeCheck: for (const shape of remainingShapesToReparent) {
|
|
328
|
+
// Don't reparent a frame to itself
|
|
329
|
+
if (parentShape.id === shape.id) continue shapeCheck
|
|
330
|
+
|
|
331
|
+
// Use the callback to filter out certain shapes
|
|
332
|
+
if (cb && !cb(shape, parentShape)) continue shapeCheck
|
|
333
|
+
|
|
334
|
+
if (!shapeGroupIds.has(shape.id)) {
|
|
335
|
+
shapeGroupIds.set(
|
|
336
|
+
shape.id,
|
|
337
|
+
editor.findShapeAncestor(shape, (s) => editor.isShapeOfType<TLGroupShape>(s, 'group'))?.id
|
|
338
|
+
)
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const shapeGroupId = shapeGroupIds.get(shape.id)
|
|
342
|
+
|
|
343
|
+
// Are the shape and the parent part of different groups?
|
|
344
|
+
if (shapeGroupId !== parentShapeContainingGroupId) continue shapeCheck
|
|
345
|
+
|
|
346
|
+
// Is the shape is actually the ancestor of the parent?
|
|
347
|
+
if (editor.findShapeAncestor(parentShape, (s) => shape.id === s.id)) continue shapeCheck
|
|
348
|
+
|
|
349
|
+
// Convert the parent polygon to the shape's space
|
|
350
|
+
const parentPolygonInShapeSpace = editor
|
|
351
|
+
.getShapePageTransform(shape)
|
|
352
|
+
.clone()
|
|
353
|
+
.invert()
|
|
354
|
+
.applyToPoints(parentPagePolygon)
|
|
355
|
+
|
|
356
|
+
// If the shape overlaps the parent polygon, reparent it to that parent
|
|
357
|
+
if (doesGeometryOverlapPolygon(editor.getShapeGeometry(shape), parentPolygonInShapeSpace)) {
|
|
358
|
+
// Use the util to check if the shape can be reparented to the parent
|
|
359
|
+
if (
|
|
360
|
+
!editor.getShapeUtil(parentShape).canReceiveNewChildrenOfType?.(parentShape, shape.type)
|
|
361
|
+
)
|
|
362
|
+
continue shapeCheck
|
|
363
|
+
|
|
364
|
+
if (shape.parentId !== parentShape.id) {
|
|
365
|
+
childrenToReparent.push(shape)
|
|
366
|
+
}
|
|
367
|
+
remainingShapesToReparent.delete(shape)
|
|
368
|
+
continue shapeCheck
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (childrenToReparent.length) {
|
|
373
|
+
reparenting.set(parentShape.id, childrenToReparent)
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return {
|
|
378
|
+
// these are the shapes that will be reparented to new parents
|
|
379
|
+
reparenting,
|
|
380
|
+
// these are the shapes that will be reparented to the page or their ancestral group
|
|
381
|
+
remainingShapesToReparent,
|
|
382
|
+
}
|
|
383
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { getSchema, JSONContent, Editor as TTEditor } from '@tiptap/core'
|
|
2
|
-
import { Node } from '@tiptap/pm/model'
|
|
2
|
+
import { Node, Schema } from '@tiptap/pm/model'
|
|
3
3
|
import { EditorProviderProps } from '@tiptap/react'
|
|
4
4
|
import { TLRichText } from '@tldraw/tlschema'
|
|
5
|
-
import { assert } from '@tldraw/utils'
|
|
5
|
+
import { assert, WeakCache } from '@tldraw/utils'
|
|
6
6
|
import { Editor } from '../editor/Editor'
|
|
7
|
-
import { TLFontFace } from '../editor/managers/FontManager'
|
|
7
|
+
import { TLFontFace } from '../editor/managers/FontManager/FontManager'
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* This is the TipTap editor! Docs are {@link https://tiptap.dev/docs}.
|
|
@@ -39,6 +39,11 @@ export type RichTextFontVisitor = (
|
|
|
39
39
|
addFont: (font: TLFontFace) => void
|
|
40
40
|
) => RichTextFontVisitorState
|
|
41
41
|
|
|
42
|
+
const schemaCache = new WeakCache<EditorProviderProps, Schema>()
|
|
43
|
+
export function getTipTapSchema(tipTapConfig: EditorProviderProps) {
|
|
44
|
+
return schemaCache.get(tipTapConfig, () => getSchema(tipTapConfig.extensions ?? []))
|
|
45
|
+
}
|
|
46
|
+
|
|
42
47
|
/** @public */
|
|
43
48
|
export function getFontsFromRichText(
|
|
44
49
|
editor: Editor,
|
|
@@ -49,7 +54,8 @@ export function getFontsFromRichText(
|
|
|
49
54
|
assert(tipTapConfig, 'textOptions.tipTapConfig must be set to use rich text')
|
|
50
55
|
assert(addFontsFromNode, 'textOptions.addFontsFromNode must be set to use rich text')
|
|
51
56
|
|
|
52
|
-
const schema =
|
|
57
|
+
const schema = getTipTapSchema(tipTapConfig)
|
|
58
|
+
|
|
53
59
|
const rootNode = Node.fromJSON(schema, richText as JSONContent)
|
|
54
60
|
|
|
55
61
|
const fonts = new Set<TLFontFace>()
|