@tldraw/editor 5.2.0-canary.2e7bbb68abd7 → 5.2.0-canary.2f9bfdac00de

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.
Files changed (72) hide show
  1. package/dist-cjs/index.d.ts +8 -13
  2. package/dist-cjs/index.js +1 -5
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/components/default-components/DefaultCanvas.js +1 -16
  5. package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
  6. package/dist-cjs/lib/editor/Editor.js +3 -1
  7. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  8. package/dist-cjs/lib/editor/managers/FontManager/FontManager.js +28 -4
  9. package/dist-cjs/lib/editor/managers/FontManager/FontManager.js.map +3 -3
  10. package/dist-cjs/lib/editor/types/event-types.js.map +2 -2
  11. package/dist-cjs/lib/globals/environment.js +6 -1
  12. package/dist-cjs/lib/globals/environment.js.map +2 -2
  13. package/dist-cjs/lib/hooks/EditorComponentsContext.js.map +2 -2
  14. package/dist-cjs/lib/hooks/useCanvasEvents.js +3 -1
  15. package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
  16. package/dist-cjs/lib/hooks/useEditorComponents.js +0 -1
  17. package/dist-cjs/lib/hooks/useEditorComponents.js.map +2 -2
  18. package/dist-cjs/lib/hooks/useScreenBounds.js +1 -0
  19. package/dist-cjs/lib/hooks/useScreenBounds.js.map +2 -2
  20. package/dist-cjs/lib/utils/pointer.js +5 -0
  21. package/dist-cjs/lib/utils/pointer.js.map +2 -2
  22. package/dist-cjs/version.js +3 -3
  23. package/dist-cjs/version.js.map +1 -1
  24. package/dist-esm/index.d.mts +8 -13
  25. package/dist-esm/index.mjs +1 -7
  26. package/dist-esm/index.mjs.map +2 -2
  27. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +1 -16
  28. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
  29. package/dist-esm/lib/editor/Editor.mjs +3 -1
  30. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  31. package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs +28 -4
  32. package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs.map +3 -3
  33. package/dist-esm/lib/editor/types/event-types.mjs.map +2 -2
  34. package/dist-esm/lib/globals/environment.mjs +6 -1
  35. package/dist-esm/lib/globals/environment.mjs.map +2 -2
  36. package/dist-esm/lib/hooks/EditorComponentsContext.mjs.map +2 -2
  37. package/dist-esm/lib/hooks/useCanvasEvents.mjs +4 -2
  38. package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
  39. package/dist-esm/lib/hooks/useEditorComponents.mjs +0 -1
  40. package/dist-esm/lib/hooks/useEditorComponents.mjs.map +2 -2
  41. package/dist-esm/lib/hooks/useScreenBounds.mjs +1 -0
  42. package/dist-esm/lib/hooks/useScreenBounds.mjs.map +2 -2
  43. package/dist-esm/lib/utils/pointer.mjs +5 -0
  44. package/dist-esm/lib/utils/pointer.mjs.map +2 -2
  45. package/dist-esm/version.mjs +3 -3
  46. package/dist-esm/version.mjs.map +1 -1
  47. package/package.json +10 -7
  48. package/src/index.ts +0 -5
  49. package/src/lib/components/default-components/DefaultCanvas.tsx +1 -17
  50. package/src/lib/editor/Editor.test.ts +36 -0
  51. package/src/lib/editor/Editor.ts +14 -2
  52. package/src/lib/editor/managers/FontManager/FontManager.test.ts +24 -1
  53. package/src/lib/editor/managers/FontManager/FontManager.ts +46 -1
  54. package/src/lib/editor/types/event-types.ts +7 -0
  55. package/src/lib/globals/environment.ts +7 -0
  56. package/src/lib/hooks/EditorComponentsContext.tsx +0 -2
  57. package/src/lib/hooks/useCanvasEvents.ts +6 -1
  58. package/src/lib/hooks/useEditorComponents.tsx +0 -1
  59. package/src/lib/hooks/useScreenBounds.ts +3 -0
  60. package/src/lib/utils/pointer.test.ts +29 -1
  61. package/src/lib/utils/pointer.ts +24 -0
  62. package/src/version.ts +3 -3
  63. package/dist-cjs/lib/components/default-components/DefaultSelectionBackground.js +0 -49
  64. package/dist-cjs/lib/components/default-components/DefaultSelectionBackground.js.map +0 -7
  65. package/dist-cjs/lib/hooks/useTransform.js +0 -43
  66. package/dist-cjs/lib/hooks/useTransform.js.map +0 -7
  67. package/dist-esm/lib/components/default-components/DefaultSelectionBackground.mjs +0 -19
  68. package/dist-esm/lib/components/default-components/DefaultSelectionBackground.mjs.map +0 -7
  69. package/dist-esm/lib/hooks/useTransform.mjs +0 -23
  70. package/dist-esm/lib/hooks/useTransform.mjs.map +0 -7
  71. package/src/lib/components/default-components/DefaultSelectionBackground.tsx +0 -25
  72. package/src/lib/hooks/useTransform.ts +0 -30
@@ -4165,6 +4165,10 @@ export class Editor extends EventEmitter<TLEventMap> {
4165
4165
  }
4166
4166
 
4167
4167
  const doc = this.getContainerDocument()
4168
+ // If the container's document has been torn down (e.g. an iframe being
4169
+ // removed), its body is null and there's nothing meaningful to measure.
4170
+ if (!doc.body) return this
4171
+
4168
4172
  const insets = [
4169
4173
  // top
4170
4174
  screenBounds.minY !== 0,
@@ -10177,6 +10181,12 @@ export class Editor extends EventEmitter<TLEventMap> {
10177
10181
 
10178
10182
  if (ids.length === 0) return undefined
10179
10183
 
10184
+ // Text geometry is measured from the loaded font, so the export's bounds - and the
10185
+ // layout of any text within it - depend on the right fonts having loaded. Wait for them
10186
+ // before we measure; otherwise an export taken before fonts finish loading (e.g. right
10187
+ // after the editor mounts) is sized and laid out with fallback-font metrics.
10188
+ await this.fonts.loadRequiredFontsForCurrentPage(this.options.maxFontsToLoadBeforeRender)
10189
+
10180
10190
  return exportToSvg(this, ids, opts)
10181
10191
  }
10182
10192
 
@@ -11223,8 +11233,10 @@ export class Editor extends EventEmitter<TLEventMap> {
11223
11233
  inputs.setIsPointing(true)
11224
11234
  inputs.setIsDragging(false)
11225
11235
 
11226
- // If pen mode is off but we're not already in pen mode, turn that on
11227
- if (!isPenMode && isPen) {
11236
+ // If pen mode is off, turn it on for direct-display pen input only (e.g. Apple
11237
+ // Pencil on an iPad or a Surface Pen on a touchscreen). Indirect desktop tablet
11238
+ // styluses still draw as pens, but should not auto-enable pen mode.
11239
+ if (!isPenMode && info.isPenDirect) {
11228
11240
  this.updateInstanceState({ isPenMode: true })
11229
11241
  // Once pen mode is on, touch input is ignored, so we discard the
11230
11242
  // in-progress touch interaction .
@@ -9,7 +9,7 @@ import {
9
9
  import { IndexKey } from '@tldraw/utils'
10
10
  import { Mock, Mocked, vi } from 'vitest'
11
11
  import { Editor } from '../../Editor'
12
- import { FontManager } from './FontManager'
12
+ import { clearFontFaceCacheForTests, FontManager } from './FontManager'
13
13
 
14
14
  // Mock the Editor class
15
15
  vi.mock('../../Editor')
@@ -73,6 +73,7 @@ describe('FontManager', () => {
73
73
 
74
74
  beforeEach(() => {
75
75
  vi.clearAllMocks()
76
+ clearFontFaceCacheForTests()
76
77
 
77
78
  mockAssetUrls = {
78
79
  'test-font.woff2': 'https://example.com/fonts/test-font.woff2',
@@ -314,4 +315,26 @@ describe('FontManager', () => {
314
315
  await expect(fontManager.ensureFontIsLoaded(minimalFont)).resolves.toBeUndefined()
315
316
  })
316
317
  })
318
+
319
+ describe('findOrCreateFontFace caching', () => {
320
+ it('reuses a font face across manager instances instead of recreating it on remount', async () => {
321
+ const font = createMockFont()
322
+ await fontManager.ensureFontIsLoaded(font)
323
+ expect(global.FontFace).toHaveBeenCalledTimes(1)
324
+
325
+ // A fresh manager on the same document simulates an editor remount.
326
+ const remounted = new FontManager(editor, mockAssetUrls)
327
+ await remounted.ensureFontIsLoaded(font)
328
+
329
+ // The per-document cache lets the second manager reuse the existing face
330
+ // rather than re-scanning document.fonts and creating a new one.
331
+ expect(global.FontFace).toHaveBeenCalledTimes(1)
332
+ })
333
+
334
+ it('creates separate font faces for fonts with different descriptors', async () => {
335
+ await fontManager.ensureFontIsLoaded(createMockFont({ weight: 'normal' }))
336
+ await fontManager.ensureFontIsLoaded(createMockFont({ weight: 'bold' }))
337
+ expect(global.FontFace).toHaveBeenCalledTimes(2)
338
+ })
339
+ })
317
340
  })
@@ -145,7 +145,26 @@ export class FontManager {
145
145
  }
146
146
 
147
147
  private findOrCreateFontFace(font: TLFontFace) {
148
- const fonts = this.editor.getContainerDocument().fonts
148
+ const containerDocument = this.editor.getContainerDocument()
149
+
150
+ // `findOrCreateFontFace` runs for every font on every editor mount, and a fresh
151
+ // editor (e.g. switching documents) gets a fresh FontManager with no memory of the
152
+ // previous one. The dedup below is an O(n) scan of the document's whole FontFaceSet,
153
+ // so without a cache that scan re-ran on every mount (measurably expensive on mobile
154
+ // Safari). Cache the resolved FontFace per document so repeated lookups - and
155
+ // remounts - are O(1). Keyed per document for cross-window embedding.
156
+ let cache = fontFaceCacheByDocument.get(containerDocument)
157
+ if (!cache) {
158
+ cache = new Map()
159
+ fontFaceCacheByDocument.set(containerDocument, cache)
160
+ }
161
+ const key = getFontFaceCacheKey(font)
162
+ const cached = cache.get(key)
163
+ if (cached) return cached
164
+
165
+ const fonts = containerDocument.fonts
166
+ // On a cache miss we still scan once, so font faces added outside this manager
167
+ // (e.g. preloaded fonts) are reused rather than duplicated.
149
168
  for (const existing of fonts) {
150
169
  if (
151
170
  existing.family === font.family &&
@@ -153,6 +172,7 @@ export class FontManager {
153
172
  ([key, defaultValue]) => existing[key] === (font[key] ?? defaultValue)
154
173
  )
155
174
  ) {
175
+ cache.set(key, existing)
156
176
  return existing
157
177
  }
158
178
  }
@@ -164,6 +184,7 @@ export class FontManager {
164
184
  })
165
185
 
166
186
  fonts.add(instance)
187
+ cache.set(key, instance)
167
188
 
168
189
  return instance
169
190
  }
@@ -205,3 +226,27 @@ const defaultFontFaceDescriptors = {
205
226
  descentOverride: 'normal',
206
227
  lineGapOverride: 'normal',
207
228
  }
229
+
230
+ // A FontFace is fully determined by its family and descriptors, so resolved faces can be
231
+ // cached per document and reused across FontManager instances (e.g. editor remounts),
232
+ // turning the per-lookup FontFaceSet scan into an O(1) map lookup. Faces are never removed
233
+ // from `document.fonts` (FontManager.dispose leaves them), so the cache stays valid for the
234
+ // document's lifetime. Keyed per document for cross-window embedding.
235
+ let fontFaceCacheByDocument = new WeakMap<Document, Map<string, FontFace>>()
236
+
237
+ function getFontFaceCacheKey(font: TLFontFace): string {
238
+ return JSON.stringify([
239
+ font.family,
240
+ ...objectMapEntries(defaultFontFaceDescriptors).map(
241
+ ([key, defaultValue]) => font[key] ?? defaultValue
242
+ ),
243
+ ])
244
+ }
245
+
246
+ /**
247
+ * Resets the per-document font-face cache. Only intended for tests.
248
+ * @internal
249
+ */
250
+ export function clearFontFaceCacheForTests() {
251
+ fontFaceCacheByDocument = new WeakMap()
252
+ }
@@ -63,6 +63,13 @@ export type TLPointerEventInfo = TLBaseEventInfo & {
63
63
  pointerId: number
64
64
  button: number
65
65
  isPen: boolean
66
+ /**
67
+ * Whether this pen event appears to be direct manipulation on the display (e.g. Apple Pencil on
68
+ * an iPad or a Surface Pen on a touchscreen) rather than indirect input from a desktop graphics
69
+ * tablet (e.g. a Wacom Intuos). Only direct-display pens should auto-enable pen mode. Drawing and
70
+ * pressure behavior is driven by `isPen` and applies to all pens regardless of this flag.
71
+ */
72
+ isPenDirect?: boolean
66
73
  } & TLPointerEventTarget
67
74
 
68
75
  /** @public */
@@ -17,6 +17,10 @@ const tlenv = {
17
17
  isWebview: false,
18
18
  isDarwin: false,
19
19
  hasCanvasSupport: false,
20
+ // Whether the device has a touch screen (an integrated coarse pointer, e.g. an iPad or a
21
+ // touchscreen laptop). Unlike `isCoarsePointer`, this reflects the device's hardware rather than
22
+ // the pointer currently in use, so it stays stable when a pen or mouse is used.
23
+ isTouchDevice: false,
20
24
  }
21
25
 
22
26
  let isForcedFinePointer = false
@@ -32,6 +36,9 @@ if (typeof window !== 'undefined') {
32
36
  }
33
37
  tlenv.hasCanvasSupport = 'Promise' in window && 'HTMLCanvasElement' in window
34
38
  isForcedFinePointer = tlenv.isFirefox && !tlenv.isAndroid && !tlenv.isIos
39
+ tlenv.isTouchDevice =
40
+ ('navigator' in window && window.navigator.maxTouchPoints > 0) ||
41
+ (typeof window.matchMedia === 'function' && window.matchMedia('(any-pointer: coarse)').matches)
35
42
  }
36
43
 
37
44
  /**
@@ -2,7 +2,6 @@ import { ComponentType, RefAttributes, createContext, useContext } from 'react'
2
2
  import type { TLCanvasComponentProps } from '../components/default-components/DefaultCanvas'
3
3
  import type { TLErrorFallbackComponent } from '../components/default-components/DefaultErrorFallback'
4
4
  import type { TLGridProps } from '../components/default-components/DefaultGrid'
5
- import type { TLSelectionBackgroundProps } from '../components/default-components/DefaultSelectionBackground'
6
5
  import type { TLShapeErrorFallbackComponent } from '../components/default-components/DefaultShapeErrorFallback'
7
6
  import type { TLShapeWrapperProps } from '../components/default-components/DefaultShapeWrapper'
8
7
 
@@ -14,7 +13,6 @@ export interface TLEditorComponents {
14
13
  InFrontOfTheCanvas?: ComponentType | null
15
14
  LoadingScreen?: ComponentType | null
16
15
  OnTheCanvas?: ComponentType | null
17
- SelectionBackground?: ComponentType<TLSelectionBackgroundProps> | null
18
16
  ShapeWrapper?: ComponentType<TLShapeWrapperProps & RefAttributes<HTMLDivElement>> | null
19
17
  Spinner?: ComponentType<React.SVGProps<SVGSVGElement>> | null
20
18
  SvgDefs?: ComponentType | null
@@ -8,7 +8,7 @@ import {
8
8
  setPointerCapture,
9
9
  } from '../utils/dom'
10
10
  import { getPointerInfo } from '../utils/getPointerInfo'
11
- import { getPointerEventButton, isSecondaryClickEvent } from '../utils/pointer'
11
+ import { getPointerEventButton, isDirectDisplayPen, isSecondaryClickEvent } from '../utils/pointer'
12
12
  import { useEditor } from './useEditor'
13
13
 
14
14
  export function useCanvasEvents() {
@@ -39,6 +39,10 @@ export function useCanvasEvents() {
39
39
 
40
40
  if (button !== 0 && button !== 1 && button !== 2 && button !== 5) return
41
41
 
42
+ // Detect direct-display pen input (Apple Pencil, Surface Pen on a touchscreen) so we
43
+ // only auto-enable pen mode for it, not for an indirect desktop tablet stylus.
44
+ const isPenDirect = isDirectDisplayPen(e)
45
+
42
46
  setPointerCapture(e.currentTarget, e)
43
47
 
44
48
  editor.dispatch({
@@ -46,6 +50,7 @@ export function useCanvasEvents() {
46
50
  target: 'canvas',
47
51
  name: 'pointer_down',
48
52
  ...getPointerInfo(editor, e),
53
+ isPenDirect,
49
54
  })
50
55
  }
51
56
 
@@ -33,7 +33,6 @@ export function EditorComponentsProvider({
33
33
  InFrontOfTheCanvas: null,
34
34
  LoadingScreen: DefaultLoadingScreen,
35
35
  OnTheCanvas: null,
36
- SelectionBackground: null,
37
36
  ShapeWrapper: DefaultShapeWrapper,
38
37
  Spinner: DefaultSpinner,
39
38
  SvgDefs: DefaultSvgDefs,
@@ -11,6 +11,9 @@ export function useScreenBounds(ref: React.RefObject<HTMLElement | null>) {
11
11
  const updateBounds = throttle(
12
12
  () => {
13
13
  if (!ref.current) return
14
+ // If the container's document has been torn down (e.g. an iframe
15
+ // being removed), there's nothing to measure — bail.
16
+ if (!ref.current.ownerDocument.body) return
14
17
  editor.updateViewportScreenBounds(ref.current)
15
18
  },
16
19
  200,
@@ -1,7 +1,7 @@
1
1
  import { tlenv } from '../globals/environment'
2
2
  import { TestEditor } from '../test/TestEditor'
3
3
  import { getPointerInfo } from './getPointerInfo'
4
- import { isSecondaryClickEvent } from './pointer'
4
+ import { isDirectDisplayPen, isSecondaryClickEvent } from './pointer'
5
5
 
6
6
  const originalIsDarwin = tlenv.isDarwin
7
7
 
@@ -21,6 +21,34 @@ describe('isSecondaryClickEvent', () => {
21
21
  })
22
22
  })
23
23
 
24
+ describe('isDirectDisplayPen', () => {
25
+ const originalIsTouchDevice = tlenv.isTouchDevice
26
+
27
+ afterEach(() => {
28
+ tlenv.isTouchDevice = originalIsTouchDevice
29
+ })
30
+
31
+ function event(pointerType: string) {
32
+ return { pointerType, pointerId: 1 } as unknown as PointerEvent
33
+ }
34
+
35
+ it('treats a pen on a touch-capable device as a direct-display pen', () => {
36
+ tlenv.isTouchDevice = true
37
+ expect(isDirectDisplayPen(event('pen'))).toBe(true)
38
+ })
39
+
40
+ it('treats a pen on a non-touch device as indirect', () => {
41
+ tlenv.isTouchDevice = false
42
+ expect(isDirectDisplayPen(event('pen'))).toBe(false)
43
+ })
44
+
45
+ it('is never true for mouse or touch input, even on a touch-capable device', () => {
46
+ tlenv.isTouchDevice = true
47
+ expect(isDirectDisplayPen(event('mouse'))).toBe(false)
48
+ expect(isDirectDisplayPen(event('touch'))).toBe(false)
49
+ })
50
+ })
51
+
24
52
  describe('ctrl + left-click on macOS fires as a right-click (regression)', () => {
25
53
  // Regression test for https://github.com/tldraw/tldraw/issues/8217
26
54
  // On macOS, a ctrl + left-click should be treated as a right-click
@@ -1,5 +1,29 @@
1
+ import type React from 'react'
1
2
  import { tlenv } from '../globals/environment'
2
3
 
4
+ /**
5
+ * Decide whether a pen pointer event looks like direct manipulation on the display (e.g. Apple
6
+ * Pencil on an iPad or a Surface Pen on a touchscreen) rather than indirect input from a desktop
7
+ * graphics tablet (e.g. a Wacom Intuos).
8
+ *
9
+ * We can't tell the two apart from the pointer event itself: both report `pointerType: 'pen'`, and
10
+ * implicit pointer capture — which in theory distinguishes direct-manipulation pointers — isn't
11
+ * reliably observable across browsers (notably WebKit/iPad). Instead we key off the device: a
12
+ * direct-display pen draws on a touch-capable screen, while an indirect graphics tablet is used on
13
+ * a non-touch desktop alongside a mouse. A device with no touch input therefore can't host a
14
+ * direct-display pen.
15
+ *
16
+ * Note this uses {@link tlenv.isTouchDevice} — the device's fixed touch capability — not the
17
+ * editor's dynamic `isCoarsePointer` state, which a pen `pointerdown` flips to coarse regardless
18
+ * of device.
19
+ *
20
+ * @internal
21
+ */
22
+ export function isDirectDisplayPen(e: React.PointerEvent | PointerEvent): boolean {
23
+ if (e.pointerType !== 'pen') return false
24
+ return tlenv.isTouchDevice
25
+ }
26
+
3
27
  /** @internal */
4
28
  interface PointerLike {
5
29
  button: number
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 = '5.2.0-canary.2e7bbb68abd7'
4
+ export const version = '5.2.0-canary.2f9bfdac00de'
5
5
  export const publishDates = {
6
6
  major: '2026-05-06T16:28:18.473Z',
7
- minor: '2026-06-18T14:06:28.264Z',
8
- patch: '2026-06-18T14:06:28.264Z',
7
+ minor: '2026-06-22T14:24:17.659Z',
8
+ patch: '2026-06-22T14:24:17.659Z',
9
9
  }
@@ -1,49 +0,0 @@
1
- "use strict";
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __export = (target, all) => {
9
- for (var name in all)
10
- __defProp(target, name, { get: all[name], enumerable: true });
11
- };
12
- var __copyProps = (to, from, except, desc) => {
13
- if (from && typeof from === "object" || typeof from === "function") {
14
- for (let key of __getOwnPropNames(from))
15
- if (!__hasOwnProp.call(to, key) && key !== except)
16
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
- }
18
- return to;
19
- };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
- var DefaultSelectionBackground_exports = {};
30
- __export(DefaultSelectionBackground_exports, {
31
- DefaultSelectionBackground: () => DefaultSelectionBackground
32
- });
33
- module.exports = __toCommonJS(DefaultSelectionBackground_exports);
34
- var import_jsx_runtime = require("react/jsx-runtime");
35
- var React = __toESM(require("react"), 1);
36
- var import_useTransform = require("../../hooks/useTransform");
37
- var import_utils = require("../../primitives/utils");
38
- function DefaultSelectionBackground({ bounds, rotation }) {
39
- const rDiv = React.useRef(null);
40
- (0, import_useTransform.useTransform)(rDiv, bounds.x, bounds.y, 1, rotation);
41
- React.useLayoutEffect(() => {
42
- const div = rDiv.current;
43
- if (!div) return;
44
- div.style.width = (0, import_utils.toDomPrecision)(Math.max(1, bounds.width)) + "px";
45
- div.style.height = (0, import_utils.toDomPrecision)(Math.max(1, bounds.height)) + "px";
46
- }, [bounds.width, bounds.height]);
47
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: rDiv, className: "tl-selection__bg", draggable: false });
48
- }
49
- //# sourceMappingURL=DefaultSelectionBackground.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../../src/lib/components/default-components/DefaultSelectionBackground.tsx"],
4
- "sourcesContent": ["import * as React from 'react'\nimport { useTransform } from '../../hooks/useTransform'\nimport { Box } from '../../primitives/Box'\nimport { toDomPrecision } from '../../primitives/utils'\n\n/** @public */\nexport interface TLSelectionBackgroundProps {\n\tbounds: Box\n\trotation: number\n}\n\n/** @public @react */\nexport function DefaultSelectionBackground({ bounds, rotation }: TLSelectionBackgroundProps) {\n\tconst rDiv = React.useRef<HTMLDivElement>(null)\n\tuseTransform(rDiv, bounds.x, bounds.y, 1, rotation)\n\n\tReact.useLayoutEffect(() => {\n\t\tconst div = rDiv.current\n\t\tif (!div) return\n\t\tdiv.style.width = toDomPrecision(Math.max(1, bounds.width)) + 'px'\n\t\tdiv.style.height = toDomPrecision(Math.max(1, bounds.height)) + 'px'\n\t}, [bounds.width, bounds.height])\n\n\treturn <div ref={rDiv} className=\"tl-selection__bg\" draggable={false} />\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAuBQ;AAvBR,YAAuB;AACvB,0BAA6B;AAE7B,mBAA+B;AASxB,SAAS,2BAA2B,EAAE,QAAQ,SAAS,GAA+B;AAC5F,QAAM,OAAO,MAAM,OAAuB,IAAI;AAC9C,wCAAa,MAAM,OAAO,GAAG,OAAO,GAAG,GAAG,QAAQ;AAElD,QAAM,gBAAgB,MAAM;AAC3B,UAAM,MAAM,KAAK;AACjB,QAAI,CAAC,IAAK;AACV,QAAI,MAAM,YAAQ,6BAAe,KAAK,IAAI,GAAG,OAAO,KAAK,CAAC,IAAI;AAC9D,QAAI,MAAM,aAAS,6BAAe,KAAK,IAAI,GAAG,OAAO,MAAM,CAAC,IAAI;AAAA,EACjE,GAAG,CAAC,OAAO,OAAO,OAAO,MAAM,CAAC;AAEhC,SAAO,4CAAC,SAAI,KAAK,MAAM,WAAU,oBAAmB,WAAW,OAAO;AACvE;",
6
- "names": []
7
- }
@@ -1,43 +0,0 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
- var useTransform_exports = {};
20
- __export(useTransform_exports, {
21
- useTransform: () => useTransform
22
- });
23
- module.exports = __toCommonJS(useTransform_exports);
24
- var import_react = require("react");
25
- function useTransform(ref, x, y, scale, rotate, additionalOffset) {
26
- (0, import_react.useLayoutEffect)(() => {
27
- const elm = ref.current;
28
- if (!elm) return;
29
- if (x === void 0) return;
30
- let trans = `translate(${x}px, ${y}px)`;
31
- if (scale !== void 0) {
32
- trans += ` scale(${scale})`;
33
- }
34
- if (rotate !== void 0) {
35
- trans += ` rotate(${rotate}rad)`;
36
- }
37
- if (additionalOffset) {
38
- trans += ` translate(${additionalOffset.x}px, ${additionalOffset.y}px)`;
39
- }
40
- elm.style.transform = trans;
41
- });
42
- }
43
- //# sourceMappingURL=useTransform.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/lib/hooks/useTransform.ts"],
4
- "sourcesContent": ["import { useLayoutEffect } from 'react'\nimport { VecLike } from '../primitives/Vec'\n\n/** @public */\nexport function useTransform(\n\tref: React.RefObject<HTMLElement | SVGElement | null>,\n\tx?: number,\n\ty?: number,\n\tscale?: number,\n\trotate?: number,\n\tadditionalOffset?: VecLike\n) {\n\tuseLayoutEffect(() => {\n\t\tconst elm = ref.current\n\t\tif (!elm) return\n\t\tif (x === undefined) return\n\n\t\tlet trans = `translate(${x}px, ${y}px)`\n\t\tif (scale !== undefined) {\n\t\t\ttrans += ` scale(${scale})`\n\t\t}\n\t\tif (rotate !== undefined) {\n\t\t\ttrans += ` rotate(${rotate}rad)`\n\t\t}\n\t\tif (additionalOffset) {\n\t\t\ttrans += ` translate(${additionalOffset.x}px, ${additionalOffset.y}px)`\n\t\t}\n\t\telm.style.transform = trans\n\t})\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAgC;AAIzB,SAAS,aACf,KACA,GACA,GACA,OACA,QACA,kBACC;AACD,oCAAgB,MAAM;AACrB,UAAM,MAAM,IAAI;AAChB,QAAI,CAAC,IAAK;AACV,QAAI,MAAM,OAAW;AAErB,QAAI,QAAQ,aAAa,CAAC,OAAO,CAAC;AAClC,QAAI,UAAU,QAAW;AACxB,eAAS,UAAU,KAAK;AAAA,IACzB;AACA,QAAI,WAAW,QAAW;AACzB,eAAS,WAAW,MAAM;AAAA,IAC3B;AACA,QAAI,kBAAkB;AACrB,eAAS,cAAc,iBAAiB,CAAC,OAAO,iBAAiB,CAAC;AAAA,IACnE;AACA,QAAI,MAAM,YAAY;AAAA,EACvB,CAAC;AACF;",
6
- "names": []
7
- }
@@ -1,19 +0,0 @@
1
- import { jsx } from "react/jsx-runtime";
2
- import * as React from "react";
3
- import { useTransform } from "../../hooks/useTransform.mjs";
4
- import { toDomPrecision } from "../../primitives/utils.mjs";
5
- function DefaultSelectionBackground({ bounds, rotation }) {
6
- const rDiv = React.useRef(null);
7
- useTransform(rDiv, bounds.x, bounds.y, 1, rotation);
8
- React.useLayoutEffect(() => {
9
- const div = rDiv.current;
10
- if (!div) return;
11
- div.style.width = toDomPrecision(Math.max(1, bounds.width)) + "px";
12
- div.style.height = toDomPrecision(Math.max(1, bounds.height)) + "px";
13
- }, [bounds.width, bounds.height]);
14
- return /* @__PURE__ */ jsx("div", { ref: rDiv, className: "tl-selection__bg", draggable: false });
15
- }
16
- export {
17
- DefaultSelectionBackground
18
- };
19
- //# sourceMappingURL=DefaultSelectionBackground.mjs.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../../src/lib/components/default-components/DefaultSelectionBackground.tsx"],
4
- "sourcesContent": ["import * as React from 'react'\nimport { useTransform } from '../../hooks/useTransform'\nimport { Box } from '../../primitives/Box'\nimport { toDomPrecision } from '../../primitives/utils'\n\n/** @public */\nexport interface TLSelectionBackgroundProps {\n\tbounds: Box\n\trotation: number\n}\n\n/** @public @react */\nexport function DefaultSelectionBackground({ bounds, rotation }: TLSelectionBackgroundProps) {\n\tconst rDiv = React.useRef<HTMLDivElement>(null)\n\tuseTransform(rDiv, bounds.x, bounds.y, 1, rotation)\n\n\tReact.useLayoutEffect(() => {\n\t\tconst div = rDiv.current\n\t\tif (!div) return\n\t\tdiv.style.width = toDomPrecision(Math.max(1, bounds.width)) + 'px'\n\t\tdiv.style.height = toDomPrecision(Math.max(1, bounds.height)) + 'px'\n\t}, [bounds.width, bounds.height])\n\n\treturn <div ref={rDiv} className=\"tl-selection__bg\" draggable={false} />\n}\n"],
5
- "mappings": "AAuBQ;AAvBR,YAAY,WAAW;AACvB,SAAS,oBAAoB;AAE7B,SAAS,sBAAsB;AASxB,SAAS,2BAA2B,EAAE,QAAQ,SAAS,GAA+B;AAC5F,QAAM,OAAO,MAAM,OAAuB,IAAI;AAC9C,eAAa,MAAM,OAAO,GAAG,OAAO,GAAG,GAAG,QAAQ;AAElD,QAAM,gBAAgB,MAAM;AAC3B,UAAM,MAAM,KAAK;AACjB,QAAI,CAAC,IAAK;AACV,QAAI,MAAM,QAAQ,eAAe,KAAK,IAAI,GAAG,OAAO,KAAK,CAAC,IAAI;AAC9D,QAAI,MAAM,SAAS,eAAe,KAAK,IAAI,GAAG,OAAO,MAAM,CAAC,IAAI;AAAA,EACjE,GAAG,CAAC,OAAO,OAAO,OAAO,MAAM,CAAC;AAEhC,SAAO,oBAAC,SAAI,KAAK,MAAM,WAAU,oBAAmB,WAAW,OAAO;AACvE;",
6
- "names": []
7
- }
@@ -1,23 +0,0 @@
1
- import { useLayoutEffect } from "react";
2
- function useTransform(ref, x, y, scale, rotate, additionalOffset) {
3
- useLayoutEffect(() => {
4
- const elm = ref.current;
5
- if (!elm) return;
6
- if (x === void 0) return;
7
- let trans = `translate(${x}px, ${y}px)`;
8
- if (scale !== void 0) {
9
- trans += ` scale(${scale})`;
10
- }
11
- if (rotate !== void 0) {
12
- trans += ` rotate(${rotate}rad)`;
13
- }
14
- if (additionalOffset) {
15
- trans += ` translate(${additionalOffset.x}px, ${additionalOffset.y}px)`;
16
- }
17
- elm.style.transform = trans;
18
- });
19
- }
20
- export {
21
- useTransform
22
- };
23
- //# sourceMappingURL=useTransform.mjs.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/lib/hooks/useTransform.ts"],
4
- "sourcesContent": ["import { useLayoutEffect } from 'react'\nimport { VecLike } from '../primitives/Vec'\n\n/** @public */\nexport function useTransform(\n\tref: React.RefObject<HTMLElement | SVGElement | null>,\n\tx?: number,\n\ty?: number,\n\tscale?: number,\n\trotate?: number,\n\tadditionalOffset?: VecLike\n) {\n\tuseLayoutEffect(() => {\n\t\tconst elm = ref.current\n\t\tif (!elm) return\n\t\tif (x === undefined) return\n\n\t\tlet trans = `translate(${x}px, ${y}px)`\n\t\tif (scale !== undefined) {\n\t\t\ttrans += ` scale(${scale})`\n\t\t}\n\t\tif (rotate !== undefined) {\n\t\t\ttrans += ` rotate(${rotate}rad)`\n\t\t}\n\t\tif (additionalOffset) {\n\t\t\ttrans += ` translate(${additionalOffset.x}px, ${additionalOffset.y}px)`\n\t\t}\n\t\telm.style.transform = trans\n\t})\n}\n"],
5
- "mappings": "AAAA,SAAS,uBAAuB;AAIzB,SAAS,aACf,KACA,GACA,GACA,OACA,QACA,kBACC;AACD,kBAAgB,MAAM;AACrB,UAAM,MAAM,IAAI;AAChB,QAAI,CAAC,IAAK;AACV,QAAI,MAAM,OAAW;AAErB,QAAI,QAAQ,aAAa,CAAC,OAAO,CAAC;AAClC,QAAI,UAAU,QAAW;AACxB,eAAS,UAAU,KAAK;AAAA,IACzB;AACA,QAAI,WAAW,QAAW;AACzB,eAAS,WAAW,MAAM;AAAA,IAC3B;AACA,QAAI,kBAAkB;AACrB,eAAS,cAAc,iBAAiB,CAAC,OAAO,iBAAiB,CAAC;AAAA,IACnE;AACA,QAAI,MAAM,YAAY;AAAA,EACvB,CAAC;AACF;",
6
- "names": []
7
- }
@@ -1,25 +0,0 @@
1
- import * as React from 'react'
2
- import { useTransform } from '../../hooks/useTransform'
3
- import { Box } from '../../primitives/Box'
4
- import { toDomPrecision } from '../../primitives/utils'
5
-
6
- /** @public */
7
- export interface TLSelectionBackgroundProps {
8
- bounds: Box
9
- rotation: number
10
- }
11
-
12
- /** @public @react */
13
- export function DefaultSelectionBackground({ bounds, rotation }: TLSelectionBackgroundProps) {
14
- const rDiv = React.useRef<HTMLDivElement>(null)
15
- useTransform(rDiv, bounds.x, bounds.y, 1, rotation)
16
-
17
- React.useLayoutEffect(() => {
18
- const div = rDiv.current
19
- if (!div) return
20
- div.style.width = toDomPrecision(Math.max(1, bounds.width)) + 'px'
21
- div.style.height = toDomPrecision(Math.max(1, bounds.height)) + 'px'
22
- }, [bounds.width, bounds.height])
23
-
24
- return <div ref={rDiv} className="tl-selection__bg" draggable={false} />
25
- }
@@ -1,30 +0,0 @@
1
- import { useLayoutEffect } from 'react'
2
- import { VecLike } from '../primitives/Vec'
3
-
4
- /** @public */
5
- export function useTransform(
6
- ref: React.RefObject<HTMLElement | SVGElement | null>,
7
- x?: number,
8
- y?: number,
9
- scale?: number,
10
- rotate?: number,
11
- additionalOffset?: VecLike
12
- ) {
13
- useLayoutEffect(() => {
14
- const elm = ref.current
15
- if (!elm) return
16
- if (x === undefined) return
17
-
18
- let trans = `translate(${x}px, ${y}px)`
19
- if (scale !== undefined) {
20
- trans += ` scale(${scale})`
21
- }
22
- if (rotate !== undefined) {
23
- trans += ` rotate(${rotate}rad)`
24
- }
25
- if (additionalOffset) {
26
- trans += ` translate(${additionalOffset.x}px, ${additionalOffset.y}px)`
27
- }
28
- elm.style.transform = trans
29
- })
30
- }