@tldraw/editor 3.14.0-canary.4c533b76dc35 → 3.14.0-canary.4f9d90070add
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 +17 -9
- package/dist-cjs/index.js +1 -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 +31 -20
- 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/shapes/group/GroupShapeUtil.js +1 -1
- package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js.map +1 -1
- package/dist-cjs/lib/primitives/Box.js +0 -6
- package/dist-cjs/lib/primitives/Box.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/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 +17 -9
- package/dist-esm/index.mjs +1 -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 +31 -20
- 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/shapes/group/GroupShapeUtil.mjs +1 -1
- package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs.map +1 -1
- package/dist-esm/lib/primitives/Box.mjs +0 -6
- package/dist-esm/lib/primitives/Box.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/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 +455 -523
- package/package.json +8 -9
- package/src/index.ts +0 -1
- package/src/lib/config/TLSessionStateSnapshot.ts +1 -1
- package/src/lib/editor/Editor.test.ts +11 -11
- package/src/lib/editor/Editor.ts +26 -17
- package/src/lib/editor/bindings/BindingUtil.ts +6 -0
- package/src/lib/editor/managers/FontManager/FontManager.ts +1 -2
- package/src/lib/editor/shapes/group/GroupShapeUtil.tsx +1 -1
- package/src/lib/primitives/Box.ts +0 -8
- package/src/lib/utils/areShapesContentEqual.ts +1 -2
- package/src/lib/utils/richText.ts +9 -3
- package/src/version.ts +3 -3
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.4f9d90070add",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "tldraw Inc.",
|
|
7
7
|
"email": "hello@tldraw.com"
|
|
@@ -48,20 +48,19 @@
|
|
|
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.4f9d90070add",
|
|
52
|
+
"@tldraw/state-react": "3.14.0-canary.4f9d90070add",
|
|
53
|
+
"@tldraw/store": "3.14.0-canary.4f9d90070add",
|
|
54
|
+
"@tldraw/tlschema": "3.14.0-canary.4f9d90070add",
|
|
55
|
+
"@tldraw/utils": "3.14.0-canary.4f9d90070add",
|
|
56
|
+
"@tldraw/validate": "3.14.0-canary.4f9d90070add",
|
|
57
57
|
"@types/core-js": "^2.5.8",
|
|
58
58
|
"@use-gesture/react": "^10.3.1",
|
|
59
59
|
"classnames": "^2.5.1",
|
|
60
60
|
"core-js": "^3.40.0",
|
|
61
61
|
"eventemitter3": "^4.0.7",
|
|
62
62
|
"idb": "^7.1.1",
|
|
63
|
-
"is-plain-object": "^5.0.0"
|
|
64
|
-
"lodash.isequal": "^4.5.0"
|
|
63
|
+
"is-plain-object": "^5.0.0"
|
|
65
64
|
},
|
|
66
65
|
"peerDependencies": {
|
|
67
66
|
"react": "^18.2.0 || ^19.0.0",
|
package/src/index.ts
CHANGED
|
@@ -4,7 +4,6 @@ import 'core-js/stable/array/flat-map.js'
|
|
|
4
4
|
import 'core-js/stable/array/flat.js'
|
|
5
5
|
import 'core-js/stable/string/at.js'
|
|
6
6
|
import 'core-js/stable/string/replace-all.js'
|
|
7
|
-
export { areShapesContentEqual } from './lib/utils/areShapesContentEqual'
|
|
8
7
|
|
|
9
8
|
// eslint-disable-next-line local/no-export-star
|
|
10
9
|
export * from '@tldraw/state'
|
|
@@ -14,12 +14,12 @@ import {
|
|
|
14
14
|
import {
|
|
15
15
|
deleteFromSessionStorage,
|
|
16
16
|
getFromSessionStorage,
|
|
17
|
+
isEqual,
|
|
17
18
|
setInSessionStorage,
|
|
18
19
|
structuredClone,
|
|
19
20
|
uniqueId,
|
|
20
21
|
} from '@tldraw/utils'
|
|
21
22
|
import { T } from '@tldraw/validate'
|
|
22
|
-
import isEqual from 'lodash.isequal'
|
|
23
23
|
import { tlenv } from '../globals/environment'
|
|
24
24
|
|
|
25
25
|
const tabIdKey = 'TLDRAW_TAB_ID_v2' as const
|
|
@@ -233,7 +233,7 @@ describe('getShapesAtPoint', () => {
|
|
|
233
233
|
})
|
|
234
234
|
})
|
|
235
235
|
|
|
236
|
-
it('returns shapes at a point in
|
|
236
|
+
it('returns shapes at a point in reverse z-index order', () => {
|
|
237
237
|
// Point at (50, 50) should hit shape3's edge (since it's at 50,50 with size 100x100)
|
|
238
238
|
// This point is exactly at the top-left corner of shape3
|
|
239
239
|
const shapes = editor.getShapesAtPoint({ x: 50, y: 50 })
|
|
@@ -313,12 +313,12 @@ describe('getShapesAtPoint', () => {
|
|
|
313
313
|
const shapes = editor.getShapesAtPoint({ x: 100, y: 0 })
|
|
314
314
|
const shapeIds = shapes.map((s) => s.id)
|
|
315
315
|
|
|
316
|
-
// Both shapes should be detected at this overlapping point
|
|
317
|
-
expect(shapeIds).toEqual([ids.
|
|
316
|
+
// Both shapes should be detected at this overlapping point (reversed order - top-most first)
|
|
317
|
+
expect(shapeIds).toEqual([ids.shape2, ids.shape1])
|
|
318
318
|
expect(shapes).toHaveLength(2)
|
|
319
319
|
})
|
|
320
320
|
|
|
321
|
-
it('maintains shape order
|
|
321
|
+
it('maintains reverse shape order and responds to z-index changes', () => {
|
|
322
322
|
// Create filled shape that overlaps with shape2
|
|
323
323
|
editor.createShape({
|
|
324
324
|
id: ids.shape5,
|
|
@@ -333,14 +333,14 @@ describe('getShapesAtPoint', () => {
|
|
|
333
333
|
const shapes = editor.getShapesAtPoint({ x: 120, y: 120 }, { hitInside: true })
|
|
334
334
|
const shapeIds = shapes.map((s) => s.id)
|
|
335
335
|
|
|
336
|
-
// All shapes that contain this point should be returned in z-index order
|
|
337
|
-
expect(shapeIds).toEqual([ids.
|
|
336
|
+
// All shapes that contain this point should be returned in reverse z-index order (top-most first)
|
|
337
|
+
expect(shapeIds).toEqual([ids.shape5, ids.shape4, ids.shape3, ids.shape2, ids.shape1])
|
|
338
338
|
|
|
339
|
-
// After bringing shape2 to front, order should change
|
|
339
|
+
// After bringing shape2 to front, order should change (shape2 becomes top-most)
|
|
340
340
|
editor.bringToFront([ids.shape2])
|
|
341
341
|
const shapes2 = editor.getShapesAtPoint({ x: 120, y: 120 }, { hitInside: true })
|
|
342
342
|
const shapeIds2 = shapes2.map((s) => s.id)
|
|
343
|
-
expect(shapeIds2).toEqual([ids.
|
|
343
|
+
expect(shapeIds2).toEqual([ids.shape2, ids.shape5, ids.shape4, ids.shape3, ids.shape1])
|
|
344
344
|
})
|
|
345
345
|
|
|
346
346
|
it('combines hitInside and margin options', () => {
|
|
@@ -361,7 +361,7 @@ describe('getShapesAtPoint', () => {
|
|
|
361
361
|
isShapeHiddenSpy.mockRestore()
|
|
362
362
|
})
|
|
363
363
|
|
|
364
|
-
it('returns multiple shapes at same point in z-index order', () => {
|
|
364
|
+
it('returns multiple shapes at same point in reverse z-index order', () => {
|
|
365
365
|
// Create two shapes at exactly the same position (away from existing shapes)
|
|
366
366
|
editor.createShape({
|
|
367
367
|
id: ids.overlap1,
|
|
@@ -383,8 +383,8 @@ describe('getShapesAtPoint', () => {
|
|
|
383
383
|
const shapes = editor.getShapesAtPoint({ x: 600, y: 600 })
|
|
384
384
|
const shapeIds = shapes.map((s) => s.id)
|
|
385
385
|
|
|
386
|
-
// Should return both shapes in z-index order
|
|
387
|
-
expect(shapeIds).toEqual([ids.
|
|
386
|
+
// Should return both shapes in reverse z-index order (top-most first)
|
|
387
|
+
expect(shapeIds).toEqual([ids.overlap2, ids.overlap1])
|
|
388
388
|
expect(shapes).toHaveLength(2)
|
|
389
389
|
})
|
|
390
390
|
|
package/src/lib/editor/Editor.ts
CHANGED
|
@@ -506,14 +506,13 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
506
506
|
shape: {
|
|
507
507
|
afterChange: (shapeBefore, shapeAfter) => {
|
|
508
508
|
for (const binding of this.getBindingsInvolvingShape(shapeAfter)) {
|
|
509
|
-
if (areShapesContentEqual(shapeBefore, shapeAfter)) continue
|
|
510
|
-
|
|
511
509
|
invalidBindingTypes.add(binding.type)
|
|
512
510
|
if (binding.fromId === shapeAfter.id) {
|
|
513
511
|
this.getBindingUtil(binding).onAfterChangeFromShape?.({
|
|
514
512
|
binding,
|
|
515
513
|
shapeBefore,
|
|
516
514
|
shapeAfter,
|
|
515
|
+
reason: 'self',
|
|
517
516
|
})
|
|
518
517
|
}
|
|
519
518
|
if (binding.toId === shapeAfter.id) {
|
|
@@ -521,6 +520,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
521
520
|
binding,
|
|
522
521
|
shapeBefore,
|
|
523
522
|
shapeAfter,
|
|
523
|
+
reason: 'self',
|
|
524
524
|
})
|
|
525
525
|
}
|
|
526
526
|
}
|
|
@@ -539,6 +539,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
539
539
|
binding,
|
|
540
540
|
shapeBefore: descendantShape,
|
|
541
541
|
shapeAfter: descendantShape,
|
|
542
|
+
reason: 'ancestry',
|
|
542
543
|
})
|
|
543
544
|
}
|
|
544
545
|
if (binding.toId === descendantShape.id) {
|
|
@@ -546,6 +547,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
546
547
|
binding,
|
|
547
548
|
shapeBefore: descendantShape,
|
|
548
549
|
shapeAfter: descendantShape,
|
|
550
|
+
reason: 'ancestry',
|
|
549
551
|
})
|
|
550
552
|
}
|
|
551
553
|
}
|
|
@@ -5035,28 +5037,33 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5035
5037
|
*
|
|
5036
5038
|
* @public
|
|
5037
5039
|
*/
|
|
5038
|
-
isShapeOrAncestorLocked(shape?: TLShape): boolean
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
if (shape.isLocked) return true
|
|
5044
|
-
return this.isShapeOrAncestorLocked(this.getShapeParent(shape))
|
|
5040
|
+
isShapeOrAncestorLocked(shape?: TLShape | TLShapeId): boolean {
|
|
5041
|
+
const _shape = shape && this.getShape(shape)
|
|
5042
|
+
if (_shape === undefined) return false
|
|
5043
|
+
if (_shape.isLocked) return true
|
|
5044
|
+
return this.isShapeOrAncestorLocked(this.getShapeParent(_shape))
|
|
5045
5045
|
}
|
|
5046
5046
|
|
|
5047
|
+
/**
|
|
5048
|
+
* Get shapes that are outside of the viewport.
|
|
5049
|
+
*
|
|
5050
|
+
* @public
|
|
5051
|
+
*/
|
|
5047
5052
|
@computed
|
|
5048
|
-
|
|
5049
|
-
return
|
|
5053
|
+
getNotVisibleShapes() {
|
|
5054
|
+
return this._notVisibleShapes.get()
|
|
5050
5055
|
}
|
|
5051
5056
|
|
|
5057
|
+
private _notVisibleShapes = notVisibleShapes(this)
|
|
5058
|
+
|
|
5052
5059
|
/**
|
|
5053
|
-
* Get culled shapes.
|
|
5060
|
+
* Get culled shapes (those that should not render), taking into account which shapes are selected or editing.
|
|
5054
5061
|
*
|
|
5055
5062
|
* @public
|
|
5056
5063
|
*/
|
|
5057
5064
|
@computed
|
|
5058
5065
|
getCulledShapes() {
|
|
5059
|
-
const notVisibleShapes = this.
|
|
5066
|
+
const notVisibleShapes = this.getNotVisibleShapes()
|
|
5060
5067
|
const selectedShapeIds = this.getSelectedShapeIds()
|
|
5061
5068
|
const editingId = this.getEditingShapeId()
|
|
5062
5069
|
const culledShapes = new Set<TLShapeId>(notVisibleShapes)
|
|
@@ -5305,21 +5312,23 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5305
5312
|
* @example
|
|
5306
5313
|
* ```ts
|
|
5307
5314
|
* editor.getShapesAtPoint({ x: 100, y: 100 })
|
|
5308
|
-
* editor.getShapesAtPoint({ x: 100, y: 100 }, { hitInside: true,
|
|
5315
|
+
* editor.getShapesAtPoint({ x: 100, y: 100 }, { hitInside: true, margin: 8 })
|
|
5309
5316
|
* ```
|
|
5310
5317
|
*
|
|
5311
5318
|
* @param point - The page point to test.
|
|
5312
5319
|
* @param opts - The options for the hit point testing.
|
|
5313
5320
|
*
|
|
5321
|
+
* @returns An array of shapes at the given point, sorted in reverse order of their absolute z-index (top-most shape first).
|
|
5322
|
+
*
|
|
5314
5323
|
* @public
|
|
5315
5324
|
*/
|
|
5316
5325
|
getShapesAtPoint(
|
|
5317
5326
|
point: VecLike,
|
|
5318
5327
|
opts = {} as { margin?: number; hitInside?: boolean }
|
|
5319
5328
|
): TLShape[] {
|
|
5320
|
-
return this.getCurrentPageShapesSorted()
|
|
5321
|
-
(shape) => !this.isShapeHidden(shape) && this.isPointInShape(shape, point, opts)
|
|
5322
|
-
|
|
5329
|
+
return this.getCurrentPageShapesSorted()
|
|
5330
|
+
.filter((shape) => !this.isShapeHidden(shape) && this.isPointInShape(shape, point, opts))
|
|
5331
|
+
.reverse()
|
|
5323
5332
|
}
|
|
5324
5333
|
|
|
5325
5334
|
/**
|
|
@@ -62,6 +62,12 @@ export interface BindingOnShapeChangeOptions<Binding extends TLUnknownBinding> {
|
|
|
62
62
|
shapeBefore: TLShape
|
|
63
63
|
/** The shape record after the change is made. */
|
|
64
64
|
shapeAfter: TLShape
|
|
65
|
+
/**
|
|
66
|
+
* Why did this shape change?
|
|
67
|
+
* - 'self': the shape itself changed
|
|
68
|
+
* - 'ancestry': the ancestry of the shape changed, but the shape itself may not have done
|
|
69
|
+
*/
|
|
70
|
+
reason: 'self' | 'ancestry'
|
|
65
71
|
}
|
|
66
72
|
|
|
67
73
|
/**
|
|
@@ -96,8 +96,7 @@ export class FontManager {
|
|
|
96
96
|
},
|
|
97
97
|
{
|
|
98
98
|
areResultsEqual: areArraysShallowEqual,
|
|
99
|
-
|
|
100
|
-
areRecordsEqual: (a, b) => a.props.richText === b.props.richText,
|
|
99
|
+
areRecordsEqual: (a, b) => a.props === b.props && a.meta === b.meta,
|
|
101
100
|
}
|
|
102
101
|
)
|
|
103
102
|
|
|
@@ -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)
|
|
@@ -1,8 +1,8 @@
|
|
|
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
7
|
import { TLFontFace } from '../editor/managers/FontManager/FontManager'
|
|
8
8
|
|
|
@@ -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>()
|
package/src/version.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// This file is automatically generated by internal/scripts/refresh-assets.ts.
|
|
2
2
|
// Do not edit manually. Or do, I'm a comment, not a cop.
|
|
3
3
|
|
|
4
|
-
export const version = '3.14.0-canary.
|
|
4
|
+
export const version = '3.14.0-canary.4f9d90070add'
|
|
5
5
|
export const publishDates = {
|
|
6
6
|
major: '2024-09-13T14:36:29.063Z',
|
|
7
|
-
minor: '2025-06-
|
|
8
|
-
patch: '2025-06-
|
|
7
|
+
minor: '2025-06-15T10:30:26.984Z',
|
|
8
|
+
patch: '2025-06-15T10:30:26.984Z',
|
|
9
9
|
}
|