@tldraw/editor 4.1.0-next.0df13eab91e1 → 4.1.0-next.2c81540f049b
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 +16 -1
- package/dist-cjs/index.js +1 -1
- package/dist-cjs/lib/editor/Editor.js +4 -2
- package/dist-cjs/lib/editor/Editor.js.map +2 -2
- package/dist-cjs/lib/editor/managers/SnapManager/HandleSnaps.js +67 -2
- package/dist-cjs/lib/editor/managers/SnapManager/HandleSnaps.js.map +2 -2
- package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
- package/dist-cjs/lib/license/Watermark.js +4 -4
- package/dist-cjs/lib/license/Watermark.js.map +1 -1
- package/dist-cjs/lib/primitives/geometry/Geometry2d.js +5 -0
- package/dist-cjs/lib/primitives/geometry/Geometry2d.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 +16 -1
- package/dist-esm/index.mjs +1 -1
- package/dist-esm/lib/editor/Editor.mjs +5 -2
- package/dist-esm/lib/editor/Editor.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/SnapManager/HandleSnaps.mjs +67 -2
- package/dist-esm/lib/editor/managers/SnapManager/HandleSnaps.mjs.map +2 -2
- package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/license/Watermark.mjs +4 -4
- package/dist-esm/lib/license/Watermark.mjs.map +1 -1
- package/dist-esm/lib/primitives/geometry/Geometry2d.mjs +5 -0
- package/dist-esm/lib/primitives/geometry/Geometry2d.mjs.map +2 -2
- package/dist-esm/version.mjs +3 -3
- package/dist-esm/version.mjs.map +1 -1
- package/editor.css +7 -2
- package/package.json +7 -7
- package/src/lib/editor/Editor.ts +5 -2
- package/src/lib/editor/managers/SnapManager/HandleSnaps.ts +91 -4
- package/src/lib/editor/shapes/ShapeUtil.ts +10 -0
- package/src/lib/license/Watermark.tsx +4 -4
- package/src/lib/primitives/geometry/Geometry2d.ts +6 -0
- package/src/version.ts +3 -3
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tldraw/editor",
|
|
3
3
|
"description": "tldraw infinite canvas SDK (editor).",
|
|
4
|
-
"version": "4.1.0-next.
|
|
4
|
+
"version": "4.1.0-next.2c81540f049b",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "tldraw Inc.",
|
|
7
7
|
"email": "hello@tldraw.com"
|
|
@@ -50,12 +50,12 @@
|
|
|
50
50
|
"@tiptap/core": "^2.9.1",
|
|
51
51
|
"@tiptap/pm": "^2.9.1",
|
|
52
52
|
"@tiptap/react": "^2.9.1",
|
|
53
|
-
"@tldraw/state": "4.1.0-next.
|
|
54
|
-
"@tldraw/state-react": "4.1.0-next.
|
|
55
|
-
"@tldraw/store": "4.1.0-next.
|
|
56
|
-
"@tldraw/tlschema": "4.1.0-next.
|
|
57
|
-
"@tldraw/utils": "4.1.0-next.
|
|
58
|
-
"@tldraw/validate": "4.1.0-next.
|
|
53
|
+
"@tldraw/state": "4.1.0-next.2c81540f049b",
|
|
54
|
+
"@tldraw/state-react": "4.1.0-next.2c81540f049b",
|
|
55
|
+
"@tldraw/store": "4.1.0-next.2c81540f049b",
|
|
56
|
+
"@tldraw/tlschema": "4.1.0-next.2c81540f049b",
|
|
57
|
+
"@tldraw/utils": "4.1.0-next.2c81540f049b",
|
|
58
|
+
"@tldraw/validate": "4.1.0-next.2c81540f049b",
|
|
59
59
|
"@types/core-js": "^2.5.8",
|
|
60
60
|
"@use-gesture/react": "^10.3.1",
|
|
61
61
|
"classnames": "^2.5.1",
|
package/src/lib/editor/Editor.ts
CHANGED
|
@@ -69,6 +69,7 @@ import {
|
|
|
69
69
|
JsonObject,
|
|
70
70
|
PerformanceTracker,
|
|
71
71
|
Result,
|
|
72
|
+
ZERO_INDEX_KEY,
|
|
72
73
|
annotateError,
|
|
73
74
|
assert,
|
|
74
75
|
assertExists,
|
|
@@ -2019,7 +2020,9 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2019
2020
|
}
|
|
2020
2021
|
|
|
2021
2022
|
/**
|
|
2022
|
-
*
|
|
2023
|
+
* Get the page bounds of all the provided shapes.
|
|
2024
|
+
*
|
|
2025
|
+
* @public
|
|
2023
2026
|
*/
|
|
2024
2027
|
getShapesPageBounds(shapeIds: TLShapeId[]): Box | null {
|
|
2025
2028
|
const bounds = compact(shapeIds.map((id) => this.getShapePageBounds(id)))
|
|
@@ -5661,7 +5664,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5661
5664
|
const children = this._parentIdsToChildIds.get()[parentId]
|
|
5662
5665
|
|
|
5663
5666
|
if (!children || children.length === 0) {
|
|
5664
|
-
return
|
|
5667
|
+
return getIndexAbove(ZERO_INDEX_KEY)
|
|
5665
5668
|
}
|
|
5666
5669
|
const shape = this.getShape(children[children.length - 1])!
|
|
5667
5670
|
return getIndexAbove(shape.index)
|
|
@@ -4,7 +4,7 @@ import { assertExists, uniqueId } from '@tldraw/utils'
|
|
|
4
4
|
import { Vec } from '../../../primitives/Vec'
|
|
5
5
|
import { Geometry2d } from '../../../primitives/geometry/Geometry2d'
|
|
6
6
|
import { Editor } from '../../Editor'
|
|
7
|
-
import { SnapData, SnapManager } from './SnapManager'
|
|
7
|
+
import { PointsSnapIndicator, SnapData, SnapManager } from './SnapManager'
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* When dragging a handle, users can snap the handle to key geometry on other nearby shapes.
|
|
@@ -43,6 +43,11 @@ export interface HandleSnapGeometry {
|
|
|
43
43
|
getSelfSnapPoints?(handle: TLHandle): VecModel[]
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
interface AlignPointsSnap {
|
|
47
|
+
snaps: PointsSnapIndicator[]
|
|
48
|
+
nudge: Vec
|
|
49
|
+
}
|
|
50
|
+
|
|
46
51
|
const defaultGetSelfSnapOutline = () => null
|
|
47
52
|
const defaultGetSelfSnapPoints = () => []
|
|
48
53
|
/** @public */
|
|
@@ -171,6 +176,67 @@ export class HandleSnaps {
|
|
|
171
176
|
return null
|
|
172
177
|
}
|
|
173
178
|
|
|
179
|
+
private getHandleSnapData({
|
|
180
|
+
handle,
|
|
181
|
+
currentShapeId,
|
|
182
|
+
}: {
|
|
183
|
+
handle: TLHandle
|
|
184
|
+
currentShapeId: TLShapeId
|
|
185
|
+
}): AlignPointsSnap | null {
|
|
186
|
+
const snapThreshold = this.manager.getSnapThreshold()
|
|
187
|
+
const currentShapeTransform = assertExists(this.editor.getShapePageTransform(currentShapeId))
|
|
188
|
+
const handleInPageSpace = currentShapeTransform.applyToPoint(handle)
|
|
189
|
+
|
|
190
|
+
let nearestXSnap: Vec | null = null
|
|
191
|
+
let nearestYSnap: Vec | null = null
|
|
192
|
+
let minOffsetX = snapThreshold
|
|
193
|
+
let minOffsetY = snapThreshold
|
|
194
|
+
|
|
195
|
+
for (const snapPoint of this.iterateSnapPointsInPageSpace(currentShapeId, handle)) {
|
|
196
|
+
const offsetX = Math.abs(handleInPageSpace.x - snapPoint.x)
|
|
197
|
+
const offsetY = Math.abs(handleInPageSpace.y - snapPoint.y)
|
|
198
|
+
if (offsetX < minOffsetX) {
|
|
199
|
+
minOffsetX = offsetX
|
|
200
|
+
nearestXSnap = snapPoint
|
|
201
|
+
}
|
|
202
|
+
if (offsetY < minOffsetY) {
|
|
203
|
+
minOffsetY = offsetY
|
|
204
|
+
nearestYSnap = snapPoint
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (!nearestXSnap && !nearestYSnap) {
|
|
209
|
+
return null
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const nudge = new Vec(
|
|
213
|
+
nearestXSnap ? nearestXSnap.x - handleInPageSpace.x : 0,
|
|
214
|
+
nearestYSnap ? nearestYSnap.y - handleInPageSpace.y : 0
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
const snappedHandle = Vec.Add(handleInPageSpace, nudge)
|
|
218
|
+
const snaps: PointsSnapIndicator[] = []
|
|
219
|
+
|
|
220
|
+
if (nearestXSnap) {
|
|
221
|
+
const snappedHandleOnX = new Vec(nearestXSnap.x, snappedHandle.y)
|
|
222
|
+
snaps.push({
|
|
223
|
+
id: uniqueId(),
|
|
224
|
+
type: 'points',
|
|
225
|
+
points: [nearestXSnap, snappedHandleOnX],
|
|
226
|
+
})
|
|
227
|
+
}
|
|
228
|
+
if (nearestYSnap) {
|
|
229
|
+
const snappedHandleOnY = new Vec(snappedHandle.x, nearestYSnap.y)
|
|
230
|
+
snaps.push({
|
|
231
|
+
id: uniqueId(),
|
|
232
|
+
type: 'points',
|
|
233
|
+
points: [nearestYSnap, snappedHandleOnY],
|
|
234
|
+
})
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return { snaps, nudge }
|
|
238
|
+
}
|
|
239
|
+
|
|
174
240
|
snapHandle({
|
|
175
241
|
currentShapeId,
|
|
176
242
|
handle,
|
|
@@ -180,10 +246,16 @@ export class HandleSnaps {
|
|
|
180
246
|
}): SnapData | null {
|
|
181
247
|
const currentShapeTransform = assertExists(this.editor.getShapePageTransform(currentShapeId))
|
|
182
248
|
const handleInPageSpace = currentShapeTransform.applyToPoint(handle)
|
|
183
|
-
|
|
249
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
250
|
+
const snapType = handle.canSnap ? 'point' : handle.snapType
|
|
251
|
+
|
|
252
|
+
if (snapType === 'point') {
|
|
253
|
+
const snapPosition = this.getHandleSnapPosition({ currentShapeId, handle, handleInPageSpace })
|
|
254
|
+
|
|
255
|
+
if (!snapPosition) {
|
|
256
|
+
return null
|
|
257
|
+
}
|
|
184
258
|
|
|
185
|
-
// If we found a point, display snap lines, and return the nudge
|
|
186
|
-
if (snapPosition) {
|
|
187
259
|
this.manager.setIndicators([
|
|
188
260
|
{
|
|
189
261
|
id: uniqueId(),
|
|
@@ -195,6 +267,21 @@ export class HandleSnaps {
|
|
|
195
267
|
return { nudge: Vec.Sub(snapPosition, handleInPageSpace) }
|
|
196
268
|
}
|
|
197
269
|
|
|
270
|
+
if (snapType === 'align') {
|
|
271
|
+
const snapData = this.getHandleSnapData({
|
|
272
|
+
handle,
|
|
273
|
+
currentShapeId,
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
if (!snapData) {
|
|
277
|
+
return null
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
this.manager.setIndicators(snapData.snaps)
|
|
281
|
+
|
|
282
|
+
return { nudge: snapData.nudge }
|
|
283
|
+
}
|
|
284
|
+
|
|
198
285
|
return null
|
|
199
286
|
}
|
|
200
287
|
}
|
|
@@ -328,6 +328,16 @@ export abstract class ShapeUtil<Shape extends TLUnknownShape = TLUnknownShape> {
|
|
|
328
328
|
*/
|
|
329
329
|
shouldClipChild?(child: TLShape): boolean
|
|
330
330
|
|
|
331
|
+
/**
|
|
332
|
+
* Whether a specific shape should hide in the minimap.
|
|
333
|
+
*
|
|
334
|
+
* If not defined, the default behavior is to show all shapes in the minimap.
|
|
335
|
+
*
|
|
336
|
+
* @returns boolean indicating if this shape should hide in the minimap
|
|
337
|
+
* @public
|
|
338
|
+
*/
|
|
339
|
+
hideInMinimap?(_shape: Shape): boolean
|
|
340
|
+
|
|
331
341
|
/**
|
|
332
342
|
* Whether the shape should hide its resize handles when selected.
|
|
333
343
|
*
|
|
@@ -208,22 +208,22 @@ To remove the watermark, please purchase a license at tldraw.dev.
|
|
|
208
208
|
}
|
|
209
209
|
|
|
210
210
|
@media (hover: hover) {
|
|
211
|
-
.${className}
|
|
211
|
+
.${className} > button {
|
|
212
212
|
pointer-events: none;
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
-
.${className}
|
|
215
|
+
.${className}:hover {
|
|
216
216
|
background-color: var(--tl-color-background);
|
|
217
217
|
transition: background-color 0.2s ease-in-out;
|
|
218
218
|
transition-delay: 0.32s;
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
-
.${className}
|
|
221
|
+
.${className}:hover > button {
|
|
222
222
|
animation: ${className}_delayed_link 0.2s forwards ease-in-out;
|
|
223
223
|
animation-delay: 0.32s;
|
|
224
224
|
}
|
|
225
225
|
|
|
226
|
-
.${className}
|
|
226
|
+
.${className} > button:focus-visible {
|
|
227
227
|
opacity: 1;
|
|
228
228
|
}
|
|
229
229
|
}
|
|
@@ -120,6 +120,8 @@ export abstract class Geometry2d {
|
|
|
120
120
|
distanceToLineSegment(A: VecLike, B: VecLike, filters?: Geometry2dFilters) {
|
|
121
121
|
if (Vec.Equals(A, B)) return this.distanceToPoint(A, false, filters)
|
|
122
122
|
const { vertices } = this
|
|
123
|
+
if (vertices.length === 0) throw Error('nearest point not found')
|
|
124
|
+
if (vertices.length === 1) return Vec.Dist(A, vertices[0])
|
|
123
125
|
let nearest: Vec | undefined
|
|
124
126
|
let dist = Infinity
|
|
125
127
|
let d: number, p: Vec, q: Vec
|
|
@@ -175,6 +177,8 @@ export abstract class Geometry2d {
|
|
|
175
177
|
interpolateAlongEdge(t: number, _filters?: Geometry2dFilters): Vec {
|
|
176
178
|
const { vertices } = this
|
|
177
179
|
|
|
180
|
+
if (vertices.length === 0) return new Vec(0, 0)
|
|
181
|
+
if (vertices.length === 1) return vertices[0]
|
|
178
182
|
if (t <= 0) return vertices[0]
|
|
179
183
|
|
|
180
184
|
const distanceToTravel = t * this.length
|
|
@@ -209,6 +213,8 @@ export abstract class Geometry2d {
|
|
|
209
213
|
let closestDistance = Infinity
|
|
210
214
|
let distanceTraveled = 0
|
|
211
215
|
|
|
216
|
+
if (vertices.length === 0 || vertices.length === 1) return 0
|
|
217
|
+
|
|
212
218
|
for (let i = 0; i < (this.isClosed ? vertices.length : vertices.length - 1); i++) {
|
|
213
219
|
const curr = vertices[i]
|
|
214
220
|
const next = vertices[(i + 1) % vertices.length]
|
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 = '4.1.0-next.
|
|
4
|
+
export const version = '4.1.0-next.2c81540f049b'
|
|
5
5
|
export const publishDates = {
|
|
6
6
|
major: '2025-09-18T14:39:22.803Z',
|
|
7
|
-
minor: '2025-10-
|
|
8
|
-
patch: '2025-10-
|
|
7
|
+
minor: '2025-10-15T10:06:47.170Z',
|
|
8
|
+
patch: '2025-10-15T10:06:47.170Z',
|
|
9
9
|
}
|