@tldraw/editor 3.7.0-internal.acaf9fbbd3cc → 3.7.1
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/CHANGELOG.md +74 -0
- package/dist-cjs/index.d.ts +11 -2
- package/dist-cjs/index.js +5 -1
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/TldrawEditor.js +5 -3
- package/dist-cjs/lib/TldrawEditor.js.map +2 -2
- package/dist-cjs/lib/components/LiveCollaborators.js +2 -1
- package/dist-cjs/lib/components/LiveCollaborators.js.map +2 -2
- package/dist-cjs/lib/components/Shape.js +6 -4
- package/dist-cjs/lib/components/Shape.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultSelectionForeground.js +2 -1
- package/dist-cjs/lib/components/default-components/DefaultSelectionForeground.js.map +2 -2
- package/dist-cjs/lib/editor/Editor.js +10 -4
- package/dist-cjs/lib/editor/Editor.js.map +2 -2
- package/dist-cjs/lib/editor/managers/TextManager.js +9 -15
- package/dist-cjs/lib/editor/managers/TextManager.js.map +2 -2
- package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
- package/dist-cjs/lib/exports/FontEmbedder.js +2 -1
- package/dist-cjs/lib/exports/FontEmbedder.js.map +2 -2
- package/dist-cjs/lib/hooks/useEvent.js +18 -1
- package/dist-cjs/lib/hooks/useEvent.js.map +2 -2
- package/dist-cjs/lib/utils/sync/LocalIndexedDb.js +1 -0
- package/dist-cjs/lib/utils/sync/LocalIndexedDb.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 +11 -2
- package/dist-esm/index.mjs +6 -2
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/TldrawEditor.mjs +5 -3
- package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
- package/dist-esm/lib/components/LiveCollaborators.mjs +2 -1
- package/dist-esm/lib/components/LiveCollaborators.mjs.map +2 -2
- package/dist-esm/lib/components/Shape.mjs +6 -4
- package/dist-esm/lib/components/Shape.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultSelectionForeground.mjs +2 -1
- package/dist-esm/lib/components/default-components/DefaultSelectionForeground.mjs.map +2 -2
- package/dist-esm/lib/editor/Editor.mjs +11 -4
- package/dist-esm/lib/editor/Editor.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/TextManager.mjs +9 -15
- package/dist-esm/lib/editor/managers/TextManager.mjs.map +2 -2
- package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/exports/FontEmbedder.mjs +2 -1
- package/dist-esm/lib/exports/FontEmbedder.mjs.map +2 -2
- package/dist-esm/lib/hooks/useEvent.mjs +18 -1
- package/dist-esm/lib/hooks/useEvent.mjs.map +2 -2
- package/dist-esm/lib/utils/sync/LocalIndexedDb.mjs +1 -0
- package/dist-esm/lib/utils/sync/LocalIndexedDb.mjs.map +2 -2
- package/dist-esm/version.mjs +3 -3
- package/dist-esm/version.mjs.map +1 -1
- package/package.json +7 -7
- package/src/index.ts +2 -1
- package/src/lib/TldrawEditor.tsx +3 -1
- package/src/lib/components/LiveCollaborators.tsx +3 -1
- package/src/lib/components/Shape.tsx +22 -10
- package/src/lib/components/default-components/DefaultSelectionForeground.tsx +4 -1
- package/src/lib/editor/Editor.ts +14 -7
- package/src/lib/editor/managers/TextManager.ts +9 -17
- package/src/lib/editor/shapes/ShapeUtil.ts +1 -1
- package/src/lib/exports/FontEmbedder.ts +2 -1
- package/src/lib/hooks/useEvent.tsx +29 -0
- package/src/lib/utils/sync/LocalIndexedDb.ts +7 -5
- package/src/version.ts +3 -3
package/src/lib/editor/Editor.ts
CHANGED
|
@@ -77,6 +77,7 @@ import {
|
|
|
77
77
|
hasOwnProperty,
|
|
78
78
|
last,
|
|
79
79
|
lerp,
|
|
80
|
+
maxBy,
|
|
80
81
|
sortById,
|
|
81
82
|
sortByIndex,
|
|
82
83
|
structuredClone,
|
|
@@ -2264,6 +2265,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2264
2265
|
const leaderPresence = this.getCollaborators().find((c) => c.userId === followingUserId)
|
|
2265
2266
|
if (!leaderPresence) return null
|
|
2266
2267
|
|
|
2268
|
+
if (!leaderPresence.camera || !leaderPresence.screenBounds) return null
|
|
2269
|
+
|
|
2267
2270
|
// Fit their viewport inside of our screen bounds
|
|
2268
2271
|
// 1. calculate their viewport in page space
|
|
2269
2272
|
const { w: lw, h: lh } = leaderPresence.screenBounds
|
|
@@ -3161,6 +3164,9 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3161
3164
|
|
|
3162
3165
|
if (!presence) return this
|
|
3163
3166
|
|
|
3167
|
+
const cursor = presence.cursor
|
|
3168
|
+
if (!cursor) return this
|
|
3169
|
+
|
|
3164
3170
|
this.run(() => {
|
|
3165
3171
|
// If we're following someone, stop following them
|
|
3166
3172
|
if (this.getInstanceState().followingUserId !== null) {
|
|
@@ -3178,7 +3184,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3178
3184
|
opts.animation = undefined
|
|
3179
3185
|
}
|
|
3180
3186
|
|
|
3181
|
-
this.centerOnPoint(
|
|
3187
|
+
this.centerOnPoint(cursor, opts)
|
|
3182
3188
|
|
|
3183
3189
|
// Highlight the user's cursor
|
|
3184
3190
|
const { highlightedUserIds } = this.getInstanceState()
|
|
@@ -3389,10 +3395,11 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3389
3395
|
if (!allPresenceRecords.length) return EMPTY_ARRAY
|
|
3390
3396
|
const userIds = [...new Set(allPresenceRecords.map((c) => c.userId))].sort()
|
|
3391
3397
|
return userIds.map((id) => {
|
|
3392
|
-
const latestPresence =
|
|
3393
|
-
.filter((c) => c.userId === id)
|
|
3394
|
-
|
|
3395
|
-
|
|
3398
|
+
const latestPresence = maxBy(
|
|
3399
|
+
allPresenceRecords.filter((c) => c.userId === id),
|
|
3400
|
+
(p) => p.lastActivityTimestamp ?? 0
|
|
3401
|
+
)
|
|
3402
|
+
return latestPresence!
|
|
3396
3403
|
})
|
|
3397
3404
|
}
|
|
3398
3405
|
|
|
@@ -4165,8 +4172,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
4165
4172
|
* Upload an asset to the store's asset service, returning a URL that can be used to resolve the
|
|
4166
4173
|
* asset.
|
|
4167
4174
|
*/
|
|
4168
|
-
async uploadAsset(asset: TLAsset, file: File): Promise<string> {
|
|
4169
|
-
return await this.store.props.assets.upload(asset, file)
|
|
4175
|
+
async uploadAsset(asset: TLAsset, file: File, abortSignal?: AbortSignal): Promise<string> {
|
|
4176
|
+
return await this.store.props.assets.upload(asset, file, abortSignal)
|
|
4170
4177
|
}
|
|
4171
4178
|
|
|
4172
4179
|
/* --------------------- Shapes --------------------- */
|
|
@@ -38,21 +38,13 @@ const spaceCharacterRegex = /\s/
|
|
|
38
38
|
|
|
39
39
|
/** @public */
|
|
40
40
|
export class TextManager {
|
|
41
|
-
|
|
41
|
+
private baseElem: HTMLDivElement
|
|
42
42
|
|
|
43
43
|
constructor(public editor: Editor) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
elm.classList.add('tl-text-measure')
|
|
49
|
-
elm.tabIndex = -1
|
|
50
|
-
container.appendChild(elm)
|
|
51
|
-
|
|
52
|
-
this.baseElm = elm
|
|
53
|
-
editor.disposables.add(() => {
|
|
54
|
-
elm.remove()
|
|
55
|
-
})
|
|
44
|
+
this.baseElem = document.createElement('div')
|
|
45
|
+
this.baseElem.classList.add('tl-text')
|
|
46
|
+
this.baseElem.classList.add('tl-text-measure')
|
|
47
|
+
this.baseElem.tabIndex = -1
|
|
56
48
|
}
|
|
57
49
|
|
|
58
50
|
measureText(
|
|
@@ -75,8 +67,8 @@ export class TextManager {
|
|
|
75
67
|
}
|
|
76
68
|
): BoxModel & { scrollWidth: number } {
|
|
77
69
|
// Duplicate our base element; we don't need to clone deep
|
|
78
|
-
const elm = this.
|
|
79
|
-
this.
|
|
70
|
+
const elm = this.baseElem.cloneNode() as HTMLDivElement
|
|
71
|
+
this.editor.getContainer().appendChild(elm)
|
|
80
72
|
|
|
81
73
|
elm.setAttribute('dir', 'auto')
|
|
82
74
|
// N.B. This property, while discouraged ("intended for Document Type Definition (DTD) designers")
|
|
@@ -223,8 +215,8 @@ export class TextManager {
|
|
|
223
215
|
): { text: string; box: BoxModel }[] {
|
|
224
216
|
if (textToMeasure === '') return []
|
|
225
217
|
|
|
226
|
-
const elm = this.
|
|
227
|
-
this.
|
|
218
|
+
const elm = this.baseElem.cloneNode() as HTMLDivElement
|
|
219
|
+
this.editor.getContainer().appendChild(elm)
|
|
228
220
|
|
|
229
221
|
const elementWidth = Math.ceil(opts.width - opts.padding * 2)
|
|
230
222
|
elm.setAttribute('dir', 'auto')
|
|
@@ -342,7 +342,7 @@ export abstract class ShapeUtil<Shape extends TLUnknownShape = TLUnknownShape> {
|
|
|
342
342
|
): ReactElement | null | Promise<ReactElement | null>
|
|
343
343
|
|
|
344
344
|
/** @internal */
|
|
345
|
-
expandSelectionOutlinePx(shape: Shape): number {
|
|
345
|
+
expandSelectionOutlinePx(shape: Shape): number | Box {
|
|
346
346
|
return 0
|
|
347
347
|
}
|
|
348
348
|
|
|
@@ -94,7 +94,8 @@ async function getCurrentDocumentFontFaces() {
|
|
|
94
94
|
if (rule instanceof CSSFontFaceRule) {
|
|
95
95
|
fontFaces.push(parseCssFontFaces(rule.cssText, styleSheet.href ?? document.baseURI))
|
|
96
96
|
} else if (rule instanceof CSSImportRule) {
|
|
97
|
-
|
|
97
|
+
const absoluteUrl = new URL(rule.href, rule.parentStyleSheet?.href ?? document.baseURI)
|
|
98
|
+
fontFaces.push(fetchCssFontFaces(absoluteUrl.href))
|
|
98
99
|
}
|
|
99
100
|
}
|
|
100
101
|
} else if (styleSheet.href) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useAtom } from '@tldraw/state-react'
|
|
1
2
|
import { assert } from '@tldraw/utils'
|
|
2
3
|
import { useCallback, useDebugValue, useLayoutEffect, useRef } from 'react'
|
|
3
4
|
|
|
@@ -42,3 +43,31 @@ export function useEvent<Args extends Array<unknown>, Result>(
|
|
|
42
43
|
return fn(...args)
|
|
43
44
|
}, [])
|
|
44
45
|
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* like {@link useEvent}, but for use in reactive contexts - when the handler function changes, it
|
|
49
|
+
* will invalidate any reactive contexts that call it.
|
|
50
|
+
* @internal
|
|
51
|
+
*/
|
|
52
|
+
export function useReactiveEvent<Args extends Array<unknown>, Result>(
|
|
53
|
+
handler: (...args: Args) => Result
|
|
54
|
+
): (...args: Args) => Result {
|
|
55
|
+
const handlerAtom = useAtom<(...args: Args) => Result>('useReactiveEvent', () => handler)
|
|
56
|
+
|
|
57
|
+
// In a real implementation, this would run before layout effects
|
|
58
|
+
useLayoutEffect(() => {
|
|
59
|
+
handlerAtom.set(handler)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
useDebugValue(handler)
|
|
63
|
+
|
|
64
|
+
return useCallback(
|
|
65
|
+
(...args: Args) => {
|
|
66
|
+
// In a real implementation, this would throw if called during render
|
|
67
|
+
const fn = handlerAtom.get()
|
|
68
|
+
assert(fn, 'fn does not exist')
|
|
69
|
+
return fn(...args)
|
|
70
|
+
},
|
|
71
|
+
[handlerAtom]
|
|
72
|
+
)
|
|
73
|
+
}
|
|
@@ -9,14 +9,16 @@ const STORE_PREFIX = 'TLDRAW_DOCUMENT_v2'
|
|
|
9
9
|
const LEGACY_ASSET_STORE_PREFIX = 'TLDRAW_ASSET_STORE_v1'
|
|
10
10
|
const dbNameIndexKey = 'TLDRAW_DB_NAME_INDEX_v2'
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
/** @internal */
|
|
13
|
+
export const Table = {
|
|
13
14
|
Records: 'records',
|
|
14
15
|
Schema: 'schema',
|
|
15
16
|
SessionState: 'session_state',
|
|
16
17
|
Assets: 'assets',
|
|
17
18
|
} as const
|
|
18
19
|
|
|
19
|
-
|
|
20
|
+
/** @internal */
|
|
21
|
+
export type StoreName = (typeof Table)[keyof typeof Table]
|
|
20
22
|
|
|
21
23
|
async function openLocalDb(persistenceKey: string) {
|
|
22
24
|
const storeId = STORE_PREFIX + persistenceKey
|
|
@@ -109,7 +111,7 @@ export class LocalIndexedDb {
|
|
|
109
111
|
})()
|
|
110
112
|
}
|
|
111
113
|
|
|
112
|
-
getDb() {
|
|
114
|
+
private getDb() {
|
|
113
115
|
return this.getDbPromise
|
|
114
116
|
}
|
|
115
117
|
|
|
@@ -288,14 +290,14 @@ export class LocalIndexedDb {
|
|
|
288
290
|
})
|
|
289
291
|
}
|
|
290
292
|
|
|
291
|
-
async getAsset(assetId: string): Promise<
|
|
293
|
+
async getAsset(assetId: string): Promise<File | undefined> {
|
|
292
294
|
return await this.tx('readonly', [Table.Assets], async (tx) => {
|
|
293
295
|
const assetsStore = tx.objectStore(Table.Assets)
|
|
294
296
|
return await assetsStore.get(assetId)
|
|
295
297
|
})
|
|
296
298
|
}
|
|
297
299
|
|
|
298
|
-
async storeAsset(assetId: string, blob:
|
|
300
|
+
async storeAsset(assetId: string, blob: File) {
|
|
299
301
|
await this.tx('readwrite', [Table.Assets], async (tx) => {
|
|
300
302
|
const assetsStore = tx.objectStore(Table.Assets)
|
|
301
303
|
await assetsStore.put(blob, assetId)
|
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.7.
|
|
4
|
+
export const version = '3.7.1'
|
|
5
5
|
export const publishDates = {
|
|
6
6
|
major: '2024-09-13T14:36:29.063Z',
|
|
7
|
-
minor: '
|
|
8
|
-
patch: '
|
|
7
|
+
minor: '2025-01-07T15:59:09.453Z',
|
|
8
|
+
patch: '2025-01-13T12:08:07.173Z',
|
|
9
9
|
}
|