@tldraw/editor 3.14.0-canary.ea2c4acfa022 → 3.14.0-canary.eab0b2f83117
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 +99 -14
- package/dist-cjs/index.js +4 -1
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/editor/Editor.js +59 -25
- package/dist-cjs/lib/editor/Editor.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 +3 -2
- 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/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/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/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/version.js +3 -3
- package/dist-cjs/version.js.map +1 -1
- package/dist-esm/index.d.mts +99 -14
- package/dist-esm/index.mjs +4 -1
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/editor/Editor.mjs +59 -25
- package/dist-esm/lib/editor/Editor.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 +3 -2
- 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/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/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/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/version.mjs +3 -3
- package/dist-esm/version.mjs.map +1 -1
- package/editor.css +17 -11
- package/package.json +7 -7
- package/src/index.ts +5 -0
- package/src/lib/editor/Editor.ts +76 -35
- package/src/lib/editor/managers/HistoryManager/HistoryManager.ts +3 -1
- package/src/lib/editor/managers/TextManager/TextManager.ts +4 -2
- package/src/lib/editor/shapes/ShapeUtil.ts +47 -15
- 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/primitives/geometry/Geometry2d.ts +7 -2
- package/src/lib/primitives/geometry/Group2d.ts +11 -5
- package/src/lib/utils/dom.ts +1 -1
- package/src/lib/utils/reparenting.ts +383 -0
- package/src/version.ts +3 -3
package/editor.css
CHANGED
|
@@ -176,8 +176,7 @@
|
|
|
176
176
|
--color-success: hsl(123, 46%, 34%);
|
|
177
177
|
--color-info: hsl(201, 98%, 41%);
|
|
178
178
|
--color-warning: hsl(27, 98%, 47%);
|
|
179
|
-
--color-
|
|
180
|
-
--color-warn: hsl(0, 90%, 43%);
|
|
179
|
+
--color-danger: hsl(0, 90%, 43%);
|
|
181
180
|
--color-laser: hsl(0, 100%, 50%);
|
|
182
181
|
/* Shadows */
|
|
183
182
|
--shadow-1: 0px 1px 2px hsl(0, 0%, 0%, 25%), 0px 1px 3px hsl(0, 0%, 0%, 9%);
|
|
@@ -232,8 +231,7 @@
|
|
|
232
231
|
--color-success: hsl(123, 38%, 57%);
|
|
233
232
|
--color-info: hsl(199, 92%, 56%);
|
|
234
233
|
--color-warning: hsl(36, 100%, 57%);
|
|
235
|
-
--color-
|
|
236
|
-
--color-warn: hsl(0, 81%, 66%);
|
|
234
|
+
--color-danger: hsl(0, 82%, 66%);
|
|
237
235
|
--color-laser: hsl(0, 100%, 50%);
|
|
238
236
|
/* Shadows */
|
|
239
237
|
--shadow-1:
|
|
@@ -247,6 +245,13 @@
|
|
|
247
245
|
inset 0px 0px 0px 1px var(--color-panel-contrast);
|
|
248
246
|
}
|
|
249
247
|
|
|
248
|
+
.tl-counter-scaled {
|
|
249
|
+
transform: scale(var(--tl-scale));
|
|
250
|
+
transform-origin: top left;
|
|
251
|
+
width: calc(100% * var(--tl-zoom));
|
|
252
|
+
height: calc(100% * var(--tl-zoom));
|
|
253
|
+
}
|
|
254
|
+
|
|
250
255
|
.tl-container,
|
|
251
256
|
.tl-container * {
|
|
252
257
|
-webkit-touch-callout: none;
|
|
@@ -1057,8 +1062,8 @@ input,
|
|
|
1057
1062
|
}
|
|
1058
1063
|
|
|
1059
1064
|
.tl-hyperlink__icon {
|
|
1060
|
-
width:
|
|
1061
|
-
height:
|
|
1065
|
+
width: 15px;
|
|
1066
|
+
height: 15px;
|
|
1062
1067
|
background-color: currentColor;
|
|
1063
1068
|
pointer-events: none;
|
|
1064
1069
|
}
|
|
@@ -1157,7 +1162,7 @@ input,
|
|
|
1157
1162
|
stroke-linejoin: round;
|
|
1158
1163
|
/* content-visibility: auto; */
|
|
1159
1164
|
transform-origin: top left;
|
|
1160
|
-
color:
|
|
1165
|
+
color: var(--color-text-1);
|
|
1161
1166
|
}
|
|
1162
1167
|
|
|
1163
1168
|
/* -------------------- Group shape ------------------ */
|
|
@@ -1272,6 +1277,7 @@ input,
|
|
|
1272
1277
|
display: flex;
|
|
1273
1278
|
justify-content: flex-end;
|
|
1274
1279
|
align-items: flex-start;
|
|
1280
|
+
box-shadow: inset 0px 0px 0px 1px var(--color-divider);
|
|
1275
1281
|
}
|
|
1276
1282
|
|
|
1277
1283
|
.tl-bookmark__image_container > .tl-hyperlink-button::after {
|
|
@@ -1294,7 +1300,7 @@ input,
|
|
|
1294
1300
|
}
|
|
1295
1301
|
|
|
1296
1302
|
.tl-bookmark__copy_container {
|
|
1297
|
-
background-color: var(--color-muted);
|
|
1303
|
+
background-color: var(--color-muted-0);
|
|
1298
1304
|
padding: var(--space-4);
|
|
1299
1305
|
pointer-events: all;
|
|
1300
1306
|
display: flex;
|
|
@@ -1313,11 +1319,11 @@ input,
|
|
|
1313
1319
|
|
|
1314
1320
|
.tl-bookmark__heading {
|
|
1315
1321
|
font-size: 16px;
|
|
1316
|
-
line-height: 1.
|
|
1322
|
+
line-height: 1.6;
|
|
1317
1323
|
font-weight: bold;
|
|
1318
1324
|
padding-bottom: var(--space-2);
|
|
1319
1325
|
overflow: hidden;
|
|
1320
|
-
max-height: calc((16px * 1.
|
|
1326
|
+
max-height: calc((16px * 1.6) * 2);
|
|
1321
1327
|
-webkit-box-orient: vertical;
|
|
1322
1328
|
-webkit-line-clamp: 2;
|
|
1323
1329
|
line-clamp: 2;
|
|
@@ -1706,7 +1712,7 @@ it from receiving any pointer events or affecting the cursor. */
|
|
|
1706
1712
|
gap: var(--space-4);
|
|
1707
1713
|
}
|
|
1708
1714
|
.tl-error-boundary__content .tl-error-boundary__reset {
|
|
1709
|
-
color: var(--color-
|
|
1715
|
+
color: var(--color-danger);
|
|
1710
1716
|
}
|
|
1711
1717
|
.tl-error-boundary__content .tl-error-boundary__refresh {
|
|
1712
1718
|
background-color: var(--color-primary);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tldraw/editor",
|
|
3
3
|
"description": "A tiny little drawing app (editor).",
|
|
4
|
-
"version": "3.14.0-canary.
|
|
4
|
+
"version": "3.14.0-canary.eab0b2f83117",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "tldraw Inc.",
|
|
7
7
|
"email": "hello@tldraw.com"
|
|
@@ -48,12 +48,12 @@
|
|
|
48
48
|
"@tiptap/core": "^2.9.1",
|
|
49
49
|
"@tiptap/pm": "^2.9.1",
|
|
50
50
|
"@tiptap/react": "^2.9.1",
|
|
51
|
-
"@tldraw/state": "3.14.0-canary.
|
|
52
|
-
"@tldraw/state-react": "3.14.0-canary.
|
|
53
|
-
"@tldraw/store": "3.14.0-canary.
|
|
54
|
-
"@tldraw/tlschema": "3.14.0-canary.
|
|
55
|
-
"@tldraw/utils": "3.14.0-canary.
|
|
56
|
-
"@tldraw/validate": "3.14.0-canary.
|
|
51
|
+
"@tldraw/state": "3.14.0-canary.eab0b2f83117",
|
|
52
|
+
"@tldraw/state-react": "3.14.0-canary.eab0b2f83117",
|
|
53
|
+
"@tldraw/store": "3.14.0-canary.eab0b2f83117",
|
|
54
|
+
"@tldraw/tlschema": "3.14.0-canary.eab0b2f83117",
|
|
55
|
+
"@tldraw/utils": "3.14.0-canary.eab0b2f83117",
|
|
56
|
+
"@tldraw/validate": "3.14.0-canary.eab0b2f83117",
|
|
57
57
|
"@types/core-js": "^2.5.8",
|
|
58
58
|
"@use-gesture/react": "^10.3.1",
|
|
59
59
|
"classnames": "^2.5.1",
|
package/src/index.ts
CHANGED
|
@@ -182,6 +182,10 @@ export { BaseBoxShapeUtil, type TLBaseBoxShape } from './lib/editor/shapes/BaseB
|
|
|
182
182
|
export {
|
|
183
183
|
ShapeUtil,
|
|
184
184
|
type TLCropInfo,
|
|
185
|
+
type TLDragShapesInInfo,
|
|
186
|
+
type TLDragShapesOutInfo,
|
|
187
|
+
type TLDragShapesOverInfo,
|
|
188
|
+
type TLDropShapesOverInfo,
|
|
185
189
|
type TLGeometryOpts,
|
|
186
190
|
type TLHandleDragInfo,
|
|
187
191
|
type TLResizeInfo,
|
|
@@ -446,6 +450,7 @@ export { hardResetEditor } from './lib/utils/hardResetEditor'
|
|
|
446
450
|
export { isAccelKey } from './lib/utils/keyboard'
|
|
447
451
|
export { normalizeWheel } from './lib/utils/normalizeWheel'
|
|
448
452
|
export { refreshPage } from './lib/utils/refreshPage'
|
|
453
|
+
export { getDroppedShapesToNewParents, kickoutOccludedShapes } from './lib/utils/reparenting'
|
|
449
454
|
export {
|
|
450
455
|
getFontsFromRichText,
|
|
451
456
|
type RichTextFontVisitor,
|
package/src/lib/editor/Editor.ts
CHANGED
|
@@ -3662,7 +3662,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3662
3662
|
* @public
|
|
3663
3663
|
*/
|
|
3664
3664
|
updateViewportScreenBounds(screenBounds: Box | HTMLElement, center = false): this {
|
|
3665
|
-
if (screenBounds instanceof
|
|
3665
|
+
if (!(screenBounds instanceof Box)) {
|
|
3666
3666
|
const rect = screenBounds.getBoundingClientRect()
|
|
3667
3667
|
screenBounds = new Box(
|
|
3668
3668
|
rect.left || rect.x,
|
|
@@ -5528,7 +5528,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5528
5528
|
if (!id) return undefined
|
|
5529
5529
|
const freshShape = this.getShape(id)
|
|
5530
5530
|
if (freshShape === undefined || !isShapeId(freshShape.parentId)) return undefined
|
|
5531
|
-
return this.
|
|
5531
|
+
return this.getShape(freshShape.parentId)
|
|
5532
5532
|
}
|
|
5533
5533
|
|
|
5534
5534
|
/**
|
|
@@ -5711,6 +5711,10 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5711
5711
|
const newPoint = invertedParentTransform.applyToPoint(pagePoint)
|
|
5712
5712
|
const newRotation = pageTransform.rotation() - parentPageRotation
|
|
5713
5713
|
|
|
5714
|
+
if (shape.id === parentId) {
|
|
5715
|
+
throw Error('Attempted to reparent a shape to itself!')
|
|
5716
|
+
}
|
|
5717
|
+
|
|
5714
5718
|
changes.push({
|
|
5715
5719
|
id: shape.id,
|
|
5716
5720
|
type: shape.type,
|
|
@@ -5814,6 +5818,11 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5814
5818
|
return shapeIds
|
|
5815
5819
|
}
|
|
5816
5820
|
|
|
5821
|
+
/** @deprecated Use {@link Editor.getDraggingOverShape} instead */
|
|
5822
|
+
getDroppingOverShape(point: Vec, droppingShapes: TLShape[]): TLShape | undefined {
|
|
5823
|
+
return this.getDraggingOverShape(point, droppingShapes)
|
|
5824
|
+
}
|
|
5825
|
+
|
|
5817
5826
|
/**
|
|
5818
5827
|
* Get the shape that some shapes should be dropped on at a given point.
|
|
5819
5828
|
*
|
|
@@ -5824,35 +5833,33 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5824
5833
|
*
|
|
5825
5834
|
* @public
|
|
5826
5835
|
*/
|
|
5827
|
-
|
|
5828
|
-
//
|
|
5829
|
-
const
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
if (
|
|
5834
|
-
// ignore hidden shapes
|
|
5835
|
-
this.isShapeHidden(shape) ||
|
|
5836
|
-
// don't allow dropping on selected shapes
|
|
5837
|
-
this.getSelectedShapeIds().includes(shape.id) ||
|
|
5838
|
-
// only allow shapes that can receive children
|
|
5839
|
-
!this.getShapeUtil(shape).canDropShapes(shape, droppingShapes) ||
|
|
5840
|
-
// don't allow dropping a shape on itself or one of it's children
|
|
5841
|
-
droppingShapes.find((s) => s.id === shape.id || this.hasAncestor(shape, s.id))
|
|
5842
|
-
) {
|
|
5843
|
-
continue
|
|
5844
|
-
}
|
|
5836
|
+
getDraggingOverShape(point: Vec, droppingShapes: TLShape[]): TLShape | undefined {
|
|
5837
|
+
// get fresh moving shapes
|
|
5838
|
+
const draggingShapes = compact(droppingShapes.map((s) => this.getShape(s))).filter(
|
|
5839
|
+
(s) => !s.isLocked && !this.isShapeHidden(s)
|
|
5840
|
+
)
|
|
5845
5841
|
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
5842
|
+
const maybeDraggingOverShapes = this.getShapesAtPoint(point, {
|
|
5843
|
+
hitInside: true,
|
|
5844
|
+
margin: 0,
|
|
5845
|
+
}).filter(
|
|
5846
|
+
(s) =>
|
|
5847
|
+
!droppingShapes.includes(s) &&
|
|
5848
|
+
!s.isLocked &&
|
|
5849
|
+
!this.isShapeHidden(s) &&
|
|
5850
|
+
!draggingShapes.includes(s)
|
|
5851
|
+
)
|
|
5849
5852
|
|
|
5853
|
+
for (const maybeDraggingOverShape of maybeDraggingOverShapes) {
|
|
5854
|
+
const shapeUtil = this.getShapeUtil(maybeDraggingOverShape)
|
|
5855
|
+
// Any shape that can handle any dragging interactions is a valid target
|
|
5850
5856
|
if (
|
|
5851
|
-
|
|
5852
|
-
|
|
5853
|
-
|
|
5857
|
+
shapeUtil.onDragShapesOver ||
|
|
5858
|
+
shapeUtil.onDragShapesIn ||
|
|
5859
|
+
shapeUtil.onDragShapesOut ||
|
|
5860
|
+
shapeUtil.onDropShapesOver
|
|
5854
5861
|
) {
|
|
5855
|
-
return
|
|
5862
|
+
return maybeDraggingOverShape
|
|
5856
5863
|
}
|
|
5857
5864
|
}
|
|
5858
5865
|
}
|
|
@@ -6211,11 +6218,12 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
6211
6218
|
*/
|
|
6212
6219
|
duplicateShapes(shapes: TLShapeId[] | TLShape[], offset?: VecLike): this {
|
|
6213
6220
|
this.run(() => {
|
|
6214
|
-
const
|
|
6221
|
+
const _ids =
|
|
6215
6222
|
typeof shapes[0] === 'string'
|
|
6216
6223
|
? (shapes as TLShapeId[])
|
|
6217
6224
|
: (shapes as TLShape[]).map((s) => s.id)
|
|
6218
6225
|
|
|
6226
|
+
const ids = this._shouldIgnoreShapeLock ? _ids : this._getUnlockedShapeIds(_ids)
|
|
6219
6227
|
if (ids.length <= 0) return this
|
|
6220
6228
|
|
|
6221
6229
|
const initialIds = new Set(ids)
|
|
@@ -6295,10 +6303,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
6295
6303
|
})
|
|
6296
6304
|
const shapesToCreate = shapesToCreateWithOriginals.map(({ shape }) => shape)
|
|
6297
6305
|
|
|
6298
|
-
|
|
6299
|
-
shapesToCreate.length + this.getCurrentPageShapeIds().size > this.options.maxShapesPerPage
|
|
6300
|
-
|
|
6301
|
-
if (maxShapesReached) {
|
|
6306
|
+
if (!this.canCreateShapes(shapesToCreate)) {
|
|
6302
6307
|
alertMaxShapes(this)
|
|
6303
6308
|
return
|
|
6304
6309
|
}
|
|
@@ -7727,6 +7732,32 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
7727
7732
|
return {}
|
|
7728
7733
|
}
|
|
7729
7734
|
|
|
7735
|
+
/**
|
|
7736
|
+
* Get whether the provided shape can be created.
|
|
7737
|
+
*
|
|
7738
|
+
* @param shape - The shape or shape IDs to check.
|
|
7739
|
+
*
|
|
7740
|
+
* @public
|
|
7741
|
+
*/
|
|
7742
|
+
canCreateShape<T extends TLUnknownShape>(
|
|
7743
|
+
shape: OptionalKeys<TLShapePartial<T>, 'id'> | T['id']
|
|
7744
|
+
): boolean {
|
|
7745
|
+
return this.canCreateShapes([shape])
|
|
7746
|
+
}
|
|
7747
|
+
|
|
7748
|
+
/**
|
|
7749
|
+
* Get whether the provided shapes can be created.
|
|
7750
|
+
*
|
|
7751
|
+
* @param shapes - The shapes or shape IDs to create.
|
|
7752
|
+
*
|
|
7753
|
+
* @public
|
|
7754
|
+
*/
|
|
7755
|
+
canCreateShapes<T extends TLUnknownShape>(
|
|
7756
|
+
shapes: (T['id'] | OptionalKeys<TLShapePartial<T>, 'id'>)[]
|
|
7757
|
+
): boolean {
|
|
7758
|
+
return shapes.length + this.getCurrentPageShapeIds().size <= this.options.maxShapesPerPage
|
|
7759
|
+
}
|
|
7760
|
+
|
|
7730
7761
|
/**
|
|
7731
7762
|
* Create a single shape.
|
|
7732
7763
|
*
|
|
@@ -7773,6 +7804,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
7773
7804
|
if (maxShapesReached) {
|
|
7774
7805
|
// can't create more shapes than fit on the page
|
|
7775
7806
|
alertMaxShapes(this)
|
|
7807
|
+
// todo: throw an error here? Otherwise we'll need to check every time whether the shapes were actually created
|
|
7776
7808
|
return this
|
|
7777
7809
|
}
|
|
7778
7810
|
|
|
@@ -7805,9 +7837,10 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
7805
7837
|
|
|
7806
7838
|
for (let i = currentPageShapesSorted.length - 1; i >= 0; i--) {
|
|
7807
7839
|
const parent = currentPageShapesSorted[i]
|
|
7840
|
+
const util = this.getShapeUtil(parent)
|
|
7808
7841
|
if (
|
|
7842
|
+
util.canReceiveNewChildrenOfType(parent, partial.type) &&
|
|
7809
7843
|
!this.isShapeHidden(parent) &&
|
|
7810
|
-
this.getShapeUtil(parent).canReceiveNewChildrenOfType(parent, partial.type) &&
|
|
7811
7844
|
this.isPointInShape(
|
|
7812
7845
|
parent,
|
|
7813
7846
|
// If no parent is provided, then we can treat the
|
|
@@ -7936,6 +7969,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
7936
7969
|
}
|
|
7937
7970
|
})
|
|
7938
7971
|
|
|
7972
|
+
this.emit('created-shapes', shapeRecordsToCreate)
|
|
7973
|
+
this.emit('edit')
|
|
7939
7974
|
this.store.put(shapeRecordsToCreate)
|
|
7940
7975
|
})
|
|
7941
7976
|
|
|
@@ -8330,6 +8365,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8330
8365
|
updates.push(updated)
|
|
8331
8366
|
}
|
|
8332
8367
|
|
|
8368
|
+
this.emit('edited-shapes', updates)
|
|
8369
|
+
this.emit('edit')
|
|
8333
8370
|
this.store.put(updates)
|
|
8334
8371
|
})
|
|
8335
8372
|
}
|
|
@@ -8379,6 +8416,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8379
8416
|
})
|
|
8380
8417
|
}
|
|
8381
8418
|
|
|
8419
|
+
this.emit('deleted-shapes', [...allShapeIdsToDelete])
|
|
8420
|
+
this.emit('edit')
|
|
8382
8421
|
return this.run(() => this.store.remove([...allShapeIdsToDelete]))
|
|
8383
8422
|
}
|
|
8384
8423
|
|
|
@@ -9505,6 +9544,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9505
9544
|
previousPagePoint,
|
|
9506
9545
|
currentScreenPoint,
|
|
9507
9546
|
currentPagePoint,
|
|
9547
|
+
originScreenPoint,
|
|
9548
|
+
originPagePoint,
|
|
9508
9549
|
} = this.inputs
|
|
9509
9550
|
|
|
9510
9551
|
const { screenBounds } = this.store.unsafeGetWithoutCapture(TLINSTANCE_ID)!
|
|
@@ -9533,8 +9574,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9533
9574
|
// Reset velocity on pointer down, or when a pinch starts or ends
|
|
9534
9575
|
if (info.name === 'pointer_down' || this.inputs.isPinching) {
|
|
9535
9576
|
pointerVelocity.set(0, 0)
|
|
9536
|
-
|
|
9537
|
-
|
|
9577
|
+
originScreenPoint.setTo(currentScreenPoint)
|
|
9578
|
+
originPagePoint.setTo(currentPagePoint)
|
|
9538
9579
|
}
|
|
9539
9580
|
|
|
9540
9581
|
// todo: We only have to do this if there are multiple users in the document
|
|
@@ -241,7 +241,9 @@ export class HistoryManager<R extends UnknownRecord> {
|
|
|
241
241
|
}
|
|
242
242
|
|
|
243
243
|
bailToMark(id: string) {
|
|
244
|
-
|
|
244
|
+
if (id) {
|
|
245
|
+
this._undo({ pushToRedoStack: false, toMark: id })
|
|
246
|
+
}
|
|
245
247
|
|
|
246
248
|
return this
|
|
247
249
|
}
|
|
@@ -26,6 +26,7 @@ export interface TLMeasureTextOpts {
|
|
|
26
26
|
fontWeight: string
|
|
27
27
|
fontFamily: string
|
|
28
28
|
fontSize: number
|
|
29
|
+
/** This must be a number, e.g. 1.35, not a pixel value. */
|
|
29
30
|
lineHeight: number
|
|
30
31
|
/**
|
|
31
32
|
* When maxWidth is a number, the text will be wrapped to that maxWidth. When maxWidth
|
|
@@ -75,6 +76,7 @@ export class TextManager {
|
|
|
75
76
|
// we need to save the default styles so that we can restore them when we're done
|
|
76
77
|
// these must be the css names, not the js names for the styles
|
|
77
78
|
this.defaultStyles = {
|
|
79
|
+
'overflow-wrap': 'break-word',
|
|
78
80
|
'word-break': 'auto',
|
|
79
81
|
width: null,
|
|
80
82
|
height: null,
|
|
@@ -123,7 +125,7 @@ export class TextManager {
|
|
|
123
125
|
elm.style.setProperty('font-style', opts.fontStyle)
|
|
124
126
|
elm.style.setProperty('font-weight', opts.fontWeight)
|
|
125
127
|
elm.style.setProperty('font-size', opts.fontSize + 'px')
|
|
126
|
-
elm.style.setProperty('line-height', opts.lineHeight
|
|
128
|
+
elm.style.setProperty('line-height', opts.lineHeight.toString())
|
|
127
129
|
elm.style.setProperty('padding', opts.padding)
|
|
128
130
|
|
|
129
131
|
if (opts.maxWidth) {
|
|
@@ -287,7 +289,7 @@ export class TextManager {
|
|
|
287
289
|
elm.style.setProperty('font-style', opts.fontStyle)
|
|
288
290
|
elm.style.setProperty('font-weight', opts.fontWeight)
|
|
289
291
|
elm.style.setProperty('font-size', opts.fontSize + 'px')
|
|
290
|
-
elm.style.setProperty('line-height', opts.lineHeight
|
|
292
|
+
elm.style.setProperty('line-height', opts.lineHeight.toString())
|
|
291
293
|
|
|
292
294
|
const elementWidth = Math.ceil(opts.width - opts.padding * 2)
|
|
293
295
|
elm.style.setProperty('width', `${elementWidth}px`)
|
|
@@ -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 */
|
|
@@ -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
|
|